Skip to content

Use Enzyme in ipopt rather than FiniteDiff#20

Merged
rafaqz merged 5 commits into
IPOPT-implementationfrom
autodiff-ipopt
May 11, 2026
Merged

Use Enzyme in ipopt rather than FiniteDiff#20
rafaqz merged 5 commits into
IPOPT-implementationfrom
autodiff-ipopt

Conversation

@rafaqz

@rafaqz rafaqz commented May 10, 2026

Copy link
Copy Markdown
Member

Still has a few hacky approaches skipping around DifferentiationInterface because of units.

We need to extract the unit stripping code from debtool into a shared package to clean this up.

rafaqz added 4 commits May 10, 2026 19:01
# Conflicts:
#	Project.toml
#	examples/budgerigar.jl
#	src/endotherm/thermoregulation/ipopt.jl
#	src/endotherm/thermoregulation/rulebased.jl
Caller-side updates for upstream renames: generated_heat_flow → metabolic_heat_flow,
per-side insulation_depth/conductivity moved under .dorsal/.ventral, shape_b → axis_ratio_b,
respiration_mass → respiration_mass_flow. Adds `smoothing` field on IPOPTControl
(default SmoothBound(1e-5)) threaded into nlp_pack so AD sees differentiable kinks.
@rafaqz rafaqz requested a review from mrke May 10, 2026 10:06
generated_heat_flow_ipopt, skin_temperature_ipopt, insulation_temperature_ipopt)

generated_heat_flow_ipopt = out.energy_flows.generated_heat_flow
generated_heat_flow_ipopt = out.energy_flows.metabolic_heat_flow

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

metabolic_heat_flow_ipopt

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.

how come? why would it change?

called at runtime when L-BFGS is active.
**Differentiation strategy.** Gradients and constraint Jacobians are computed via
[`Enzyme.jl`](https://github.com/EnzymeAD/Enzyme.jl): reverse-mode for the scalar objective
gradient and forward-mode for the constraint Jacobian. The `hess` and `cons_h` callbacks

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can delete this last caveat - it's repeated below in the limitations

# of `p` doesn't compose with Unitful values.
# hess! and cons_h! are registered (required by IpoptOptimizer) but not
# called at runtime when hessian_approximation="limited-memory" is set.
function grad_fn!(g, x, _)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we should go for expressive names in this IPOPT code too

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.

Yeah, all of these functions likely need to be deleted later on

@mrke

mrke commented May 11, 2026

Copy link
Copy Markdown
Contributor

Would be good to keep finite diff as an option, at least initially to compare performance

@gdalle

gdalle commented May 11, 2026

Copy link
Copy Markdown

@rafaqz anything I can do for better unit support in DI?

Comment thread docs/ipopt_endotherm_thermoregulation.md Outdated
@rafaqz rafaqz merged commit 154c641 into IPOPT-implementation May 11, 2026
2 checks passed
@rafaqz rafaqz deleted the autodiff-ipopt branch May 11, 2026 23:26
rafaqz added a commit that referenced this pull request Jun 21, 2026
* initial implementation of IPOPT - slow but works

* speed up IPOPT, add weighting factors for user specification of thermoregulatory strategy, update budgerigar plots

* faster setup and better names for IPOPT approach, plus updated plots for budgie comparison

Co-authored-by: Copilot <copilot@github.com>

* remove generated heat flow as an effector, add optional core-skin temperature gradient based constraint, implement q10 effect

* add document on IPOPT, remove warm-start strategy from budgie

* update Project.toml

* update Project.toml

* update Project.toml

* update Project.toml

* Better names (#15)

* update names for endotherm related code

* better names for ectotherm thermoregulation code

* update Project.toml

---------

Co-authored-by: Rafael Schouten <rafaelschouten@gmail.com>

* refactor thermoregulate

* bugfix shape_b

* Update IPOPT implementation to new HeatExchange names and NLP hook (#19)

* update to new names and NLP hooks

* update to IPOPT code to allow MultiSidedNLP (dorso/ventral-specific calculations) to work, plus naming consistency updates and docs update

* update Project.toml

* Use Enzyme in ipopt rather than FiniteDiff (#20)

* working Enzyme

# Conflicts:
#	Project.toml
#	examples/budgerigar.jl
#	src/endotherm/thermoregulation/ipopt.jl
#	src/endotherm/thermoregulation/rulebased.jl

* Track HeatExchange API renames and add smoothing kwarg to IPOPTControl

Caller-side updates for upstream renames: generated_heat_flow → metabolic_heat_flow,
per-side insulation_depth/conductivity moved under .dorsal/.ventral, shape_b → axis_ratio_b,
respiration_mass → respiration_mass_flow. Adds `smoothing` field on IPOPTControl
(default SmoothBound(1e-5)) threaded into nlp_pack so AD sees differentiable kinks.

* cleanup and deps

* more cleanup

* Apply suggestion from @rafaqz

* aspect_ratio to axis_ratio (#23)

* aspect_ratio to axis_ratio

* update HeatExchange.jl branch in Project.toml

* change HeatExchange.jl version in Project.toml

* use fluid=Air(), convert remaining cases of aspect_ratio* in example_variables_and_parameters, revert HeatExchange.jl version to main

* fix fluid specification in endotherm test script

* Move example heat exchange pars to HeatExchange.jl (#21)

* remove HeatExchange.jl parameter and variable examples (now live in HeatExchange.jl as per issue #4)

* update HeatExchange.jl branch

---------

Co-authored-by: Rafael Schouten <rafaelschouten@gmail.com>

* Reverse mode and exact hessians (#22)

* bugfix number type instability with Dual

* Switch IPOPT backend to direct Ipopt.jl and export solver cache

- Drop Optimization.jl / OptimizationIpopt.jl / SciMLBase in favour of
  the direct Ipopt.jl C interface. Gives access to the primal+dual
  warm-start API (IpoptProblem.x / mult_g) that the SciMLBase wrappers
  do not expose, so consecutive solves at similar parameter settings
  reuse the previous solution as the new initial guess.
- Export IPOPTSolverCache so callers can construct the cache once and
  pass it across sweeps.
- ipopt.jl rewritten end-to-end: cached buffers for primals, duals,
  bounds, constraint values, and Enzyme.forward-over-reverse Hessian
  (replaces FiniteDiff).
- Adopt HeatExchange Air/Water Fluid singletons in examples and test
  data: example_environment_pars defaults to Air(); endotherm test now
  maps the FLTYPE input flag to Water()/Air().
- Add bench_ipopt_sweep.jl and hessian_validation.jl examples for
  benchmarking warm-start speedup and Hessian correctness.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* cleanup

* better multiple dispatch

* more dispatch simplifications

* Expand abbreviated names and make IPOPTSolverCache immutable

Spell out short identifiers in the IPOPT thermoregulation code: lb/ub/u0,
lcons/ucons, metab_pars/int_cond/evap_pars/opt_pars/nlp_pars, M/sT/iT
init shorthand, jac_*/hess_*/lag_* AD buffers, eval_f/eval_g/eval_grad_f/
eval_jac_g/eval_h closures, and the corresponding type parameters.

Drop the mutable-struct on IPOPTSolverCache: the only mutating field was
the warm-start flag, now stored as Base.RefValue{Bool}.

* Pipe init NamedTuple through thermoregulate; turn IPOPT callbacks into functors

- `thermoregulate` and `IPOPTSolverCache` take a single `init` NamedTuple
  (`metabolic_heat_flow`, `skin_temperature`, `insulation_temperature`) instead
  of three loose positional args, threaded through the rule-based loop, IPOPT
  builder, `_inputs!`, and `_refresh!`.
- Rename `_IPOPTState` to `IPOPTParameters` and its instance field to
  `ipopt_parameters` — what it holds is per-solve problem parameters, not
  optimizer state. The mutable cell is just so the callbacks can be reused
  across solves.
- Replace the anonymous Ipopt callback closures with six named functor structs
  (`EvaluateObjective`, `EvaluateConstraints`, `EvaluateObjectiveGradient`,
  `EvaluateConstraintJacobian`, `EvaluateHessian`, `LagrangianGradient`), all
  subtypes of `Function` so `Ipopt.IpoptProblem`'s typed callback fields accept
  them.
- Collapse `_build_cache` into two layered `IPOPTSolverCache` constructors and
  factor out `_problem_size` / `_constraint_bounds` so the strategy-specific
  dimensions live in one place.
- Deduplicate `_inputs!`: one shared body computes all scalars and parameter
  NamedTuples; the strategy-specific index layout is delegated to a small
  `_write_layout!` dispatched on packed type.
- Drop the `effectors[i]` magic numbers in `_objective_value` — name only the
  variables each method actually uses.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* update ipopt doc, including background on the method, and add a rulebased doc

* cleanup with templates

* no branch for HeatExchange

* dont specify LinearAlgebra version

* use fibrous not fur

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: mrke <mrke@unimelb.edu.au>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Rafael Schouten <rafaelschouten@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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