Skip to content

Commit 1fb8a8b

Browse files
baggepinnenclaude
andcommitted
Remove fuzz functionality, simplify trajectory_ss to use LinearizationOpPoint directly
trajectory_ss now delegates entirely to MTK's linearize with LinearizationOpPoint(sol, t) — no manual op construction needed. Removed ControlSystemsMTK.fuzz and all references to it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0040cdf commit 1fb8a8b

2 files changed

Lines changed: 11 additions & 87 deletions

File tree

docs/src/api.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,4 @@ ControlSystemsBase.StateSpace
2828
SymbolicControlSystems.ccode
2929
SymbolicControlSystems.print_c_array
3030
ModelingToolkit.reorder_states
31-
ControlSystemsMTK.fuzz
3231
```

src/ode_system.jl

Lines changed: 11 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ centers, radii = fit_complex_perturbations(P * C, w; relative = false, nominal =
492492
nyquistcircles!(w, centers, radii, ylims = (-4, 1), xlims = (-3, 4))
493493
```
494494
495-
See also [`trajectory_ss`](@ref) and [`fuzz`](@ref).
495+
See also [`trajectory_ss`](@ref).
496496
"""
497497
function batch_ss(args...; kwargs...)
498498
lins, ssys, resolved_ops = batch_linearize(args...; kwargs...)
@@ -512,7 +512,7 @@ end
512512
# end
513513

514514
"""
515-
linsystems, ssys = trajectory_ss(sys, inputs, outputs, sol; t = _max_100(sol.t), fuzzer=nothing, verbose = true, kwargs...)
515+
linsystems, ssys = trajectory_ss(sys, inputs, outputs, sol; t = _max_100(sol.t), kwargs...)
516516
517517
Linearize `sys` around the trajectory `sol` at times `t`. Returns a vector of `StateSpace` objects and the simplified system.
518518
@@ -523,56 +523,20 @@ Operating points are extracted from the solution automatically using `ModelingTo
523523
- `outputs`: A vector of variables or analysis points.
524524
- `sol`: An ODE solution object.
525525
- `t`: Time points along the solution trajectory at which to linearize. The returned array of `StateSpace` objects will be of the same length as `t`.
526-
- `fuzzer`: A function that takes an operating point dictionary and returns an array of "fuzzed" operating points. This is useful for adding noise/uncertainty to the operating points along the trajectory. See [`ControlSystemsMTK.fuzz`](@ref) for such a function.
527526
- `kwargs`: Are sent to the linearization functions (e.g., `loop_openings`).
528527
- `named`: If `true`, the returned systems will be of type `NamedStateSpace`, otherwise they will be of type `StateSpace`.
529528
"""
530-
function trajectory_ss(sys, inputs, outputs, sol; t = _max_100(sol.t), allow_input_derivatives = false, fuzzer = nothing, verbose = true, named = true, kwargs...)
529+
function trajectory_ss(sys, inputs, outputs, sol; t = _max_100(sol.t), allow_input_derivatives = false, verbose = true, named = true, kwargs...)
531530
maximum(t) > maximum(sol.t) && @warn("The maximum time in `t`: $(maximum(t)), is larger than the maximum time in `sol.t`: $(maximum(sol.t)).")
532531
minimum(t) < minimum(sol.t) && @warn("The minimum time in `t`: $(minimum(t)), is smaller than the minimum time in `sol.t`: $(minimum(sol.t)).")
533532

534533
input_names = reduce(vcat, getproperty.(ap.outputs, :u) for ap in vcat(inputs))
535534
output_names = reduce(vcat, ap.input.u for ap in vcat(outputs))
536535

537-
# Use LinearizationOpPoint to extract operating points from the solution.
538-
# _build_op_from_solution gives differential states + parameters; we supplement
539-
# with all unknowns of the linearization system to avoid initialization issues.
540-
_extract_base_op(ti) = ModelingToolkit._build_op_from_solution(ModelingToolkit.LinearizationOpPoint(sol, ti))
536+
# Use LinearizationOpPoint to let MTK extract operating points from the solution
541537
tc = collect(t)
542-
543-
# First pass: get the linearization system's unknowns
544-
lin_fun0, ssys = linearization_function(sys, inputs, outputs; warn_initialize_determined=false, kwargs...)
545-
lin_unknowns = unknowns(ssys)
546-
defs = ModelingToolkit.initial_conditions(sys)
547-
548-
ops = map(tc) do ti
549-
op = _extract_base_op(ti)
550-
for x in lin_unknowns
551-
haskey(op, x) && continue
552-
try
553-
op[x] = sol(ti, idxs=x)
554-
catch
555-
val = get(defs, x, nothing)
556-
val !== nothing && (op[x] = val)
557-
end
558-
end
559-
op
560-
end
561-
562-
if fuzzer !== nothing
563-
opsv = map(ops) do op
564-
fuzzer(op)
565-
end
566-
ops = reduce(vcat, opsv)
567-
tc = repeat(tc, inner = length(ops) ÷ length(tc))
568-
end
569-
570-
lin_fun, ssys = linearization_function(sys, inputs, outputs; op=ops[1], t=tc[1], initialize=false, kwargs...)
571-
lins_ops = map(zip(ops, tc)) do (op, ti)
572-
linearize(ssys, lin_fun; op, t=ti, allow_input_derivatives)
573-
end
574-
lins = first.(lins_ops)
575-
resolved_ops = last.(lins_ops)
538+
op = ModelingToolkit.LinearizationOpPoint(sol, tc)
539+
lins, ssys, resolved_ops = linearize(sys, inputs, outputs; op, allow_input_derivatives, kwargs...)
576540

577541
named_linsystems = map(lins) do l
578542
if named
@@ -583,51 +547,12 @@ function trajectory_ss(sys, inputs, outputs, sol; t = _max_100(sol.t), allow_inp
583547
ss(l.A, l.B, l.C, l.D)
584548
end
585549
end
586-
(; linsystems = named_linsystems, ssys, ops, resolved_ops)
550+
(; linsystems = named_linsystems, ssys, ops = resolved_ops, resolved_ops)
587551
end
588552

589553
"_max_100(t) = length(t) > 100 ? range(extrema(t)..., 100) : t"
590554
_max_100(t) = length(t) > 100 ? range(extrema(t)..., 100) : t
591555

592-
"""
593-
fuzz(op, p; N = 10, parameters = true, variables = true)
594-
595-
"Fuzz" an operating point `op::Dict` by changing each non-zero value to an uncertain number with multiplicative uncertainty `p`, represented by `N` samples, i.e., `p = 0.1` means that the value is multiplied by a `N` numbers between 0.9 and 1.1.
596-
597-
`parameters` and `variables` indicate whether to fuzz parameters and state variables, respectively.
598-
599-
This function modifies all variables the same way. For more fine-grained control, load the `MonteCarloMeasurements` package and use the `Particles` type directly, followed by `MonteCarloMeasurements.particle_dict2dict_vec(op)`, i.e., the following makes `uncertain_var` uncertain with a 10% uncertainty:
600-
```julia
601-
using MonteCarloMeasurements
602-
op = ModelingToolkit.defaults(sys)
603-
op[uncertain_var] = op[uncertain_var] * Particles(10, Uniform(0.9, 1.1))
604-
ops = MonteCarloMeasurements.particle_dict2dict_vec(op)
605-
batch_ss(model, inputs, outputs, ops)
606-
```
607-
If you have more than one uncertain parameter, it's important to use the same number of particles for all of them (10 in the example above).
608-
609-
To make use of this function in [`trajectory_ss`](@ref), pass something like
610-
```
611-
fuzzer = op -> ControlSystemsMTK.fuzz(op, 0.02; N=10)
612-
```
613-
to fuzz each operating point 10 times with a 2% uncertainty. The resulting number of operating points will increase by 10x.
614-
"""
615-
function fuzz(op, p; N=10, parameters = true, variables = true)
616-
op = map(collect(keys(op))) do key
617-
par = ModelingToolkit.isparameter(key)
618-
val = op[key]
619-
par && !parameters && return (key => val)
620-
!par && !variables && return (key => val)
621-
aval = abs(val)
622-
uval = issymbolic(val) ? val : iszero(val) ? 0.0 : Particles(N, MonteCarloMeasurements.Uniform(val-p*aval, val+p*aval))
623-
key => uval
624-
end |> Dict
625-
MonteCarloMeasurements.particle_dict2dict_vec(op)
626-
end
627-
628-
MonteCarloMeasurements.vecindex(p::Symbolics.BasicSymbolic,i) = p
629-
issymbolic(x) = x isa Union{Symbolics.Num, Symbolics.BasicSymbolic}
630-
631556
maybe_interp(interpolator, x, t) = allequal(x) ? x[1] : interpolator(x, t)
632557

633558
"""
@@ -676,10 +601,10 @@ function GainScheduledStateSpace(systems, vt; interpolator, x = zeros(systems[1]
676601
description = "Scheduling variable of gain-scheduled statespace system $name",
677602
]
678603

679-
@variables A(t)[1:nx, 1:nx] = systems[1].A
680-
@variables B(t)[1:nx, 1:nu] = systems[1].B
681-
@variables C(t)[1:ny, 1:nx] = systems[1].C
682-
@variables D(t)[1:ny, 1:nu] = systems[1].D
604+
@variables A(t)[1:nx, 1:nx] [guess=systems[1].A]
605+
@variables B(t)[1:nx, 1:nu] [guess=systems[1].B]
606+
@variables C(t)[1:ny, 1:nx] [guess=systems[1].C]
607+
@variables D(t)[1:ny, 1:nu] [guess=systems[1].D]
683608
A,B,C,D = collect.((A,B,C,D))
684609

685610
eqs = [

0 commit comments

Comments
 (0)