feat: [AI] allow passing an operating point to linearize using a solution#4443
Conversation
c6fcf4f to
f9a6be1
Compare
Replace manual operating point extraction in `trajectory_ss` with MTK's `LinearizationOpPoint` API (SciML/ModelingToolkit.jl#4443). This removes the fragile `robust_sol_getindex` helper and simplifies the implementation. - Use `_build_op_from_solution` to extract differential states + parameters - Supplement with linearization system unknowns from the solution - Remove `robust_sol_getindex` (no longer needed) - Bump version to 2.7.0, require MTK >= 11.7 - Update docs narrative for trajectory_ss Closes SciML/ModelingToolkit.jl#4159 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
I'm testing this PR from ControlSystemsMTK.jl and hit a dispatch ambiguity in The two methods are: # Method 1 (line 28): scalar
_build_op_from_solution(op::LinearizationOpPoint)
# Method 2 (line 44): vector
_build_op_from_solution(op::LinearizationOpPoint{S, <:AbstractVector}) where {S}Method 1 implicitly constrains |
78da2e1 to
b63a43c
Compare
df97dab to
fbe0162
Compare
Pins ModelingToolkit and ModelingToolkitBase to the as/linearize-op PR branch (SciML/ModelingToolkit.jl#4443) so CI can validate the LinearizationOpPoint integration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fbe0162 to
e89fd0b
Compare
|
I've been exercising this branch through 1. Operating-point values are boxed into
|
fefd0bb to
a0d4896
Compare
Benchmark Results (Julia vlts)Time benchmarks
Memory benchmarks
|
Benchmark Results (Julia v1)Time benchmarks
Memory benchmarks
|
|
what's the status of this PR? Mike has started to think about ops in dyad so I'd like the low level stuff to be available and working to start trying things out. Last time I tried, there were some performance problems that I hoe were addressed by #4679, anything else needed? |
|
I'll prioritize it. It needs CI to pass, there are some failures in linearization. |
a0d4896 to
1cc75e7
Compare
When linearizing along a trajectory with `LinearizationOpPoint(sol, tvec)`,
the multi-timepoint path was ~340 ms/point with ~92% of the time spent in the
Julia compiler, recompiling on essentially every iteration. Two causes:
1. `_build_op_from_solution` returns a `Dict{SymbolicT, SymbolicT}`, so every
numeric operating-point value is stored as a symbolic constant. The
op-application loop then took the `symbolic_type(v) != NotSymbolic()` branch
for every entry and called `getu(prob, v)(prob)`. For a constant, `getu`
builds an observed function with the value baked into generated code, so a
fresh `RuntimeGeneratedFunction` is compiled per distinct value -> a
recompile at almost every time point. Fixed by `_resolve_op_value`, which
unwraps symbolic constants to plain numbers/arrays and only routes genuinely
symbolic expressions through `getu`.
2. `__linearize_multiple_op_barrier` called the high-level per-point
`linearize` in a loop, reconstructing the `LinearizationProblem` and
rebuilding all setters every iteration. It now builds the problem and setters
once (the op keys are identical across time points) and only updates values
and mutates `.t` per point, as the `LinearizationProblem` docstring intends.
Measured on a gain-scheduled closed loop (7 states, 21 op entries, 200 points):
~340 ms/pt -> ~3.6 ms/pt (~95x), with results bit-identical to the per-point
scalar path. Added a per-point scalar-vs-vector equivalence check to the
`LinearizationOpPoint` testset.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When `loop_openings` is used, each opened analysis point's output is turned into a parameter (via `Break(ap, true)`). Its operating-point value is not implied by the rest of the system, and in particular is not present in a solution passed via `LinearizationOpPoint`. Previously such a parameter silently took a stale/default value, which is especially wrong when linearizing along a trajectory: the value was frozen at the point used to build the linearization function rather than tracking the trajectory. `handle_loop_openings` now returns the variables it turns into parameters; these are threaded through `linearization_ap_transform` / `get_linear_analysis_function` into a new `loop_opening_params` field on `LinearizationFunction`. `linearize` errors (listing the offending parameters) if any of them is missing from the operating point, instead of using an implicit value. To supply them when the operating point is derived from a solution, `LinearizationOpPoint` gains an optional `op` keyword whose entries are merged into the solution-derived operating point at every time point, e.g. `LinearizationOpPoint(sol, t; op = Dict(opened_signal => 0))`. Added a `loop_openings require an operating point` testset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1cc75e7 to
02f43a7
Compare
SciML/ModelingToolkit.jl#4443 (which included the trajectory-linearization perf and loop-opening fixes from #4679) has merged to master, and the `as/linearize-op` branch was deleted, so the previous pin no longer resolves. Pin to `master` until an MTK release >= 11.36 (the first version containing these changes; latest registered is 11.35.2) is available, after which the `[sources]` entries can be dropped and the compat bumped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close #4159