Skip to content

refactor: Introduce GeneratedFunctionOptions for the code-generation entry points - #4768

Merged
AayushSabharwal merged 4 commits into
masterfrom
as/generated-fn-opts
Jul 16, 2026
Merged

refactor: Introduce GeneratedFunctionOptions for the code-generation entry points#4768
AayushSabharwal merged 4 commits into
masterfrom
as/generated-fn-opts

Conversation

@AayushSabharwal

Copy link
Copy Markdown
Member

Follow-up of #4762

@AayushSabharwal
AayushSabharwal marked this pull request as ready for review July 16, 2026 11:15
Add `GeneratedFunctionOptions{expression, wrap_gfw}` (in ModelingToolkitBase's
codegen_utils.jl): the "output/compile" options layer one level above
`BuildFunctionWrapperOptions`. `expression`/`wrap_gfw` are `Bool` type parameters
(they gate the return type, so generators branch on them statically); the fields
are `eval_expression`, `eval_module`, `compiler_options::CompilerOptions`, and a
nested `codegen::Symbolics.CodegenFunctionOptions` for the code-generation options
threaded down to `build_function_wrapper`.

Convert the code-generation entry points that funnel into `maybe_compile_function`
to take a positional `GeneratedFunctionOptions` plus a strictly-typed set of
function-specific keyword arguments (no `kwargs...` sink on the primaries):
generate_rhs, generate_jacobian, generate_W, generate_tgrad, generate_dae_jacobian,
generate_diffusion_function, generate_cost(_gradient/_hessian), generate_cons,
generate_constraint_jacobian/_hessian, generate_bvp_cost, generate_boundary_conditions,
generate_history, generate_control_jacobian, generate_update_A/_b,
generate_custom_function, and generate_semiquadratic_functions/_jacobian.

Function-specific options (`simplify`, `sparse`, `return_sparsity`, `implicit_dae`,
`scalar`, `cachesyms`, `stiff_*`, ...) become strict typed keyword arguments;
`checkbounds` flows through `opts.codegen`; wrapper-derived codegen options
(`wrap_code`, `similarto`, `iip_config`) are written back with `setproperties`.

The backwards-compatible keyword forms move to new `codegen_compat.jl` files (one
in ModelingToolkitBase, one at the top level for the semiquadratic generators) to
keep `codegen.jl` focused on the primary methods. `generate_custom_function`'s
primary moves from abstractsystem.jl to codegen.jl so its
`opts::GeneratedFunctionOptions` annotation is defined after the struct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AayushSabharwal and others added 3 commits July 16, 2026 18:39
Thread the `GeneratedFunctionOptions` options struct through the remaining
codegen entry points that still splatted `kwargs...` into
`Symbolics.CodegenFunctionOptions`, eliminating per-kwarg-combination
specialization and the silent-drop kwarg sinks. Each function gains a
positional `opts::GeneratedFunctionOptions` primary method; the historical
keyword signatures are preserved by backward-compatibility wrappers.

- build_explicit_observed_function (codegen.jl): expression/eval_expression/
  eval_module/compiler_options + codegen via `opts`. The inert `try_namespaced`
  and codegen-level `expression` (never fields of CodegenFunctionOptions; the
  wrapper always emits Exprs) are dropped without behavior change.
- compile_condition, compile_explicit_affect (callbacks.jl): the latter's
  previously-dropped `kwargs...` now flow through `opts.codegen`, with per-call
  `wrap_code`/`outputidxs`/`iip_config` applied via `setproperties`.
- generate_control_function (inputoutput.jl).
- SymbolicTstops (problem_utils.jl) and CacheWriter (sccnonlinearproblem.jl):
  their keyword wrappers live adjacent to the primaries, after the `struct`
  definitions of the same name, so they register as methods on that binding.

Move `codegen_utils.jl` before `callbacks.jl` in the include order so the
`opts::GeneratedFunctionOptions` signature annotations resolve at definition
time; its only definition-time dependencies are Symbolics symbols and
AbstractSystem.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the source callers of the code-generation entry points converted in the
previous commits so they pass a positional `GeneratedFunctionOptions` struct
instead of splatting keyword arguments. Each `*Function`/`*Problem` constructor
now builds one options struct and threads it into every generator call
(generate_rhs/tgrad/jacobian/diffusion/history, the cost/constraint/bvp/update/
semiquadratic generators, build_explicit_observed_function, SymbolicTstops,
CacheWriter); per-call structural options (simplify, sparse, implicit_dae,
cachesyms, ...) remain loose keyword arguments.

Behavior-preserving details worth calling out:
- `GeneratedFunctionOptions` defaults `expression = Val{true}`, whereas
  `build_explicit_observed_function` and `SymbolicTstops` historically default
  to compiled output; callsites that relied on that now pass
  `expression = Val{false}` explicitly.
- `cachesyms` reaches `LinearFunction` through `kwargs` in the SCC path and is a
  structural (not codegen) kwarg, so it is extracted and forwarded as such
  rather than dropped into the codegen options.
- `compiler_options` is pulled out of `kwargs` where the generator consumes it.
- optimal_control_interface's historical `expression_module = eval_module` was
  never a recognized keyword (silently dropped, so `eval_module` defaulted); the
  migration preserves that and documents it inline.

Callers that merely forward an opaque `kwargs...` sink (compile_condition and
generate_control_function forwarders, the generic observed helper in
problem_utils) are left on their keyword back-compat wrappers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia vlts)

Time benchmarks
master cf1ad2b... master / cf1ad2b...
ODEProblem 4.49 ± 0.16 ms 4.68 ± 0.26 ms 0.961 ± 0.065
init 0.0665 ± 0.019 ms 0.0535 ± 0.022 ms 1.24 ± 0.62
large_parameter_init/ODEProblem 19.9 ± 3.6 ms 22.3 ± 4.3 ms 0.893 ± 0.23
large_parameter_init/init 0.0758 ± 0.033 ms 0.0755 ± 0.029 ms 1 ± 0.58
mtkcompile 7.95 ± 0.85 ms 8.04 ± 0.85 ms 0.989 ± 0.15
sparse_analytical_jacobian/ODEProblem 22.1 ± 1.4 ms 23.1 ± 1.5 ms 0.957 ± 0.089
sparse_analytical_jacobian/f_iip 0.06 ± 0 μs 0.06 ± 0 μs 1 ± 0
sparse_analytical_jacobian/f_oop 0.305 ± 0.0063 ms 0.306 ± 0.0074 ms 0.998 ± 0.032
time_to_load 5.4 ± 0.083 s 5.16 ± 0.061 s 1.05 ± 0.02
Memory benchmarks
master cf1ad2b... master / cf1ad2b...
ODEProblem 0.0394 M allocs: 2 MB 0.0394 M allocs: 1.99 MB 1.01
init 0.417 k allocs: 0.0698 MB 0.417 k allocs: 0.0698 MB 1
large_parameter_init/ODEProblem 0.325 M allocs: 11.4 MB 0.325 M allocs: 11.4 MB 1
large_parameter_init/init 0.605 k allocs: 0.172 MB 0.605 k allocs: 0.172 MB 1
mtkcompile 0.0622 M allocs: 3.4 MB 0.0622 M allocs: 3.4 MB 1
sparse_analytical_jacobian/ODEProblem 0.193 M allocs: 8.01 MB 0.193 M allocs: 8.02 MB 0.999
sparse_analytical_jacobian/f_iip 0 allocs: 0 B 0 allocs: 0 B
sparse_analytical_jacobian/f_oop 0.634 k allocs: 19.6 kB 0.634 k allocs: 19.6 kB 1
time_to_load 0.153 k allocs: 14.5 kB 0.153 k allocs: 14.5 kB 1

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
master cf1ad2b... master / cf1ad2b...
ODEProblem 5.3 ± 0.41 ms 5.04 ± 0.19 ms 1.05 ± 0.091
init 0.0486 ± 0.021 ms 0.0475 ± 0.02 ms 1.02 ± 0.62
large_parameter_init/ODEProblem 27.8 ± 6.3 ms 26 ± 7.2 ms 1.07 ± 0.38
large_parameter_init/init 0.0696 ± 0.042 ms 0.0692 ± 0.042 ms 1.01 ± 0.86
mtkcompile 8.59 ± 0.52 ms 8.57 ± 0.72 ms 1 ± 0.1
sparse_analytical_jacobian/ODEProblem 25.4 ± 3.6 ms 24.9 ± 3.4 ms 1.02 ± 0.2
sparse_analytical_jacobian/f_iip 0.09 ± 0 μs 0.11 ± 0.001 μs 0.818 ± 0.0074
sparse_analytical_jacobian/f_oop 0.124 ± 0.014 ms 0.13 ± 0.014 ms 0.957 ± 0.15
time_to_load 5.95 ± 0.034 s 5.89 ± 0.078 s 1.01 ± 0.014
Memory benchmarks
master cf1ad2b... master / cf1ad2b...
ODEProblem 0.0397 M allocs: 1.77 MB 0.0397 M allocs: 1.77 MB 1
init 0.416 k allocs: 0.0518 MB 0.416 k allocs: 0.0518 MB 1
large_parameter_init/ODEProblem 0.353 M allocs: 12.9 MB 0.353 M allocs: 12.9 MB 1
large_parameter_init/init 0.799 k allocs: 0.152 MB 0.799 k allocs: 0.152 MB 1
mtkcompile 0.0603 M allocs: 2.71 MB 0.0603 M allocs: 2.71 MB 1
sparse_analytical_jacobian/ODEProblem 0.188 M allocs: 7 MB 0.188 M allocs: 7.01 MB 0.999
sparse_analytical_jacobian/f_iip 0 allocs: 0 B 0 allocs: 0 B
sparse_analytical_jacobian/f_oop 0.848 k allocs: 27 kB 0.848 k allocs: 27 kB 1
time_to_load 0.145 k allocs: 11 kB 0.145 k allocs: 11 kB 1

@AayushSabharwal
AayushSabharwal merged commit 77941f8 into master Jul 16, 2026
63 of 68 checks passed
@AayushSabharwal
AayushSabharwal deleted the as/generated-fn-opts branch July 16, 2026 14:46
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.

1 participant