feat: [AI] allow passing an operating point to linearize using a solution#4443
feat: [AI] allow passing an operating point to linearize using a solution#4443AayushSabharwal wants to merge 9 commits into
linearize using a solution#4443Conversation
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
|
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>
2881651 to
fefd0bb
Compare
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>
fefd0bb to
a0d4896
Compare
Benchmark Results (Julia vlts)Time benchmarks
Memory benchmarks
|
Benchmark Results (Julia v1)Time benchmarks
Memory benchmarks
|
Close #4159