Skip to content

Commit fbe66a5

Browse files
committed
add utility to convert uncertain model to multimodel with many outputs
1 parent 63c703b commit fbe66a5

File tree

5 files changed

+80
-8
lines changed

5 files changed

+80
-8
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Optim = "429524aa-4258-5aef-a3af-852621145aeb"
1818
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1919
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
2020
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
21+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
2122
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
2223
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
2324
UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
@@ -35,6 +36,7 @@ MonteCarloMeasurements = "1.0"
3536
Optim = "1.5"
3637
Plots = "1"
3738
RecipesBase = "1"
39+
SparseArrays = "1"
3840
Statistics = "1"
3941
UnPack = "1.0"
4042
julia = "1.7"

src/RobustAndOptimalControl.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ include("ExtendedStateSpace.jl")
3737
export modal_form, schur_form, hess_form, frequency_separation
3838
include("canonical.jl")
3939

40-
export δ, δr, δc, δss, nominal, UncertainSS, uss, blocksort, sys_from_particles, ss2particles
40+
export δ, δr, δc, δss, nominal, UncertainSS, uss, blocksort, sys_from_particles, mo_sys_from_particles, ss2particles
4141
include("uncertainty_interface.jl")
4242

4343
export makeweight, neglected_delay, gain_and_delay_uncertainty, neglected_lag, fit_complex_perturbations

src/named_systems2.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,9 @@ function Base.show(io::IO, G::NamedStateSpace)
277277
end
278278
print(io, "Named")
279279
show(io, G.sys)
280-
print(io, "\nWith state names: "); println(io, join(G.x, ' '))
281-
print(io, " input names: "); println(io, join(G.u, ' '))
282-
print(io, " output names: "); println(io, join(G.y, ' '))
280+
length(G.x) < 50 && (print(io, "\nWith state names: "); println(io, join(G.x, ' ')))
281+
length(G.u) < 50 && (print(io, " input names: "); println(io, join(G.u, ' ')))
282+
length(G.y) < 50 && (print(io, " output names: "); println(io, join(G.y, ' ')))
283283
end
284284

285285
function Base.:-(s1::NamedStateSpace{T,S}) where {T <: CS.TimeEvolution, S}

src/uncertainty_interface.jl

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using SparseArrays
12
abstract type UncertainElement{C, F} end
23

34
struct δ{C, F<:Real} <: UncertainElement{C, F}
@@ -477,11 +478,11 @@ using MonteCarloMeasurements: vecindex
477478
478479
Return the `i`th system from a system `P` with `Particles` coefficients.
479480
480-
If called without an index, return a vector of systems, one for each possibly `i`.
481+
If called without an index, return a vector of systems, one for each possible `i`.
481482
482483
This function is used to convert from an uncertain representation using `Particles` to a "multi-model" representation using multiple `StateSpace` models.
483484
484-
See also [`ss2particles`](@ref) and `MonteCarloMeasurements.nominal`.
485+
See also [`ss2particles`](@ref), [`mo_sys_from_particles`](@ref) and `MonteCarloMeasurements.nominal`.
485486
"""
486487
function sys_from_particles(P, i)
487488
A,B,C,D = ssdata(P)
@@ -502,6 +503,36 @@ function sys_from_particles(P)
502503
end
503504
end
504505

506+
"""
507+
mo_sys_from_particles(P::AbstractStateSpace; sparse = nparticles(P.A) > 500)
508+
509+
Converts a state-space model with `Particles` coefficients to a single state-space model with the same number of inputs as `P`, but with `nparticles(P.A)` times the output and state dimensions.
510+
511+
If `sparse` is true, the resulting model will be a `HeteroStateSpace` with sparse `A`, `C`, and `D` matrices.
512+
"""
513+
function mo_sys_from_particles(P::AbstractStateSpace; sparse = nparticles(P.A) > 500)
514+
systems = sys_from_particles(P)
515+
A = blockdiag(s.A for s in systems)
516+
B = reduce(vcat, s.B for s in systems)
517+
C = blockdiag(s.C for s in systems)
518+
D = reduce(vcat, s.D for s in systems)
519+
if sparse
520+
if count(!iszero, D) / length(D) < 0.1
521+
D = SparseArrays.sparse(D)
522+
end
523+
Pmo = HeteroStateSpace(SparseArrays.sparse(A), B, SparseArrays.sparse(C), D, ControlSystemsBase.timeevol(P))
524+
else
525+
Pmo = ss(A, B, C, D, ControlSystemsBase.timeevol(P))
526+
end
527+
suffix_i(x, i) = Symbol.(string.(x) .* string("_", i))
528+
P isa NamedStateSpace || (return Pmo)
529+
named_ss(Pmo, P.name;
530+
x = reduce(vcat, suffix_i(P.x, i) for i in eachindex(systems)),
531+
u = P.u,
532+
y = reduce(vcat, suffix_i(P.y, i) for i in eachindex(systems)),
533+
)
534+
end
535+
505536

506537
function ControlSystemsBase.lsim(sys::DelayLtiSystem{T,S}, u, t::AbstractArray{<:Real}, args...; x0=fill(zero(T), nstates(sys)), kwargs...) where {T, S<:AbstractParticles}
507538
syss = sys_from_particles(sys)

test/test_uncertainty.jl

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using RobustAndOptimalControl, ControlSystemsBase, MonteCarloMeasurements
1+
using RobustAndOptimalControl, ControlSystemsBase, MonteCarloMeasurements, Test
22

33
d = δr()
44
@test d.val == 0
@@ -348,4 +348,43 @@ nyquistplot(P*C, w[1:10:end], points=true, xlims=(-3.5, 2.5), ylims=(-5, 1.5), M
348348

349349
P = ss(-1, 1, 1, 0)
350350
Pd = P * delay(0.1 .. 0.3)
351-
@test Pd.P.P (tf(P) * delay(0.1 .. 0.3)).P.P
351+
@test Pd.P.P (tf(P) * delay(0.1 .. 0.3)).P.P
352+
353+
354+
## Convert a named particle system to a single system with multiple outputs
355+
# Tests that output and state names are set in a reasonable way
356+
N = 20
357+
A = randn(2,2) .+ 0.1Particles(N)
358+
B = randn(2,3) .+ 0.1Particles(N)
359+
C = randn(2,2) .+ 0.1Particles(N)
360+
D = 0
361+
P = named_ss(ss(A,B,C,D), "P")
362+
363+
MOP = RobustAndOptimalControl.mo_sys_from_particles(P);
364+
365+
@test MOP.ny == P.ny*N
366+
@test MOP.nx == P.nx*N
367+
@test MOP.nu == P.nu
368+
369+
@test MOP.x[1] == :Px1_1
370+
@test MOP.x[2] == :Px2_1
371+
@test MOP.x[3] == :Px1_2
372+
@test MOP.u == P.u
373+
374+
# res = step(MOP, 0:0.1:1, method=:tustin)
375+
376+
N = 2000
377+
A = randn(2,2) .+ 0.1Particles(N)
378+
B = randn(2,3) .+ 0.1Particles(N)
379+
C = randn(2,2) .+ 0.1Particles(N)
380+
D = 0
381+
P = named_ss(ss(A,B,C,D), "P")
382+
383+
MOP = RobustAndOptimalControl.mo_sys_from_particles(P);
384+
385+
using SparseArrays
386+
@test MOP.ny == P.ny*N
387+
@test MOP.nx == P.nx*N
388+
@test MOP.nu == P.nu
389+
390+
@test MOP.A isa SparseMatrixCSC

0 commit comments

Comments
 (0)