Skip to content

Commit 830bed7

Browse files
committed
sort delta blocks
1 parent 1bcdaf4 commit 830bed7

File tree

3 files changed

+200
-12
lines changed

3 files changed

+200
-12
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "RobustAndOptimalControl"
22
uuid = "21fd56a4-db03-40ee-82ee-a87907bee541"
33
authors = ["Fredrik Bagge Carlson", "Marcus Greiff"]
4-
version = "0.2.0"
4+
version = "0.2.1"
55

66
[deps]
77
ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66"

src/uncertainty_interface.jl

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,4 +502,139 @@ function IntervalArithmetic.Interval(d::δ{C}) where C <: Complex
502502
re = Interval(d.val.re-d.radius, d.val.re+d.radius)
503503
im = Interval(d.val.im-d.radius, d.val.im+d.radius)
504504
Complex(re, im)
505-
end
505+
end
506+
507+
508+
509+
"""
510+
block_structure(Δ)
511+
512+
Take a vector of uncertain elements and return a vector of vectors with the block
513+
structure of perturbation blocks as described expected my μ-tools, i.e.
514+
- `[-N, 0]` denots a repeated real block of size `N`
515+
- `[N, 0]` denots a repeated complex block of size `N`
516+
- `[ny, nu]` denots a full complex block of size `ny × nu`
517+
"""
518+
function block_structure(D)
519+
perm = sortperm(D, by=d->d.name)
520+
D = D[perm]
521+
blocks = Vector{Int}[]
522+
blockstart = 1
523+
for i in 2:length(D)
524+
if D[i].name == D[i-1].name
525+
if blockstart == 0
526+
blockstart = i-1
527+
end
528+
else
529+
push!(blocks, makeblock(D[blockstart:i-1]))
530+
blockstart = i
531+
end
532+
end
533+
push!(blocks, makeblock(D[blockstart:length(D)]))
534+
blocks, perm
535+
end
536+
537+
function makeblock(d) # TODO: full uncertain block that is not a dynamical system is not supported
538+
N = length(d)
539+
if N > 1 || (N==1 && d[] isa δ) # Δ = δI -> μ(M) = ρ(M) 8.80
540+
if eltype(d[1]) <: Complex
541+
b = [N, 0] # complex*I
542+
else
543+
b = [-N, 0] # real*I
544+
end
545+
else
546+
b = [size(d[], 1), size(d[], 2)]
547+
end
548+
end
549+
550+
551+
function blocksort(P::UncertainSS)
552+
blocks, perm = block_structure(P.Δ)
553+
Poutinds = []
554+
Pininds = []
555+
# Build indices into M that corresponds to the Δ blocks and permute those indices with the same permutation as was applied to Δ
556+
for (i, d) in enumerate(P.Δ)
557+
if i == 1
558+
push!(Poutinds, 1:size(d, 2))
559+
push!(Pininds, 1:size(d, 1))
560+
else
561+
push!(Poutinds, (1:size(d, 2)) .+ Poutinds[i-1][end])
562+
push!(Pininds, (1:size(d, 1)) .+ Pininds[i-1][end])
563+
end
564+
end
565+
Poutinds = reduce(vcat, Poutinds[perm])
566+
Pininds = reduce(vcat, Pininds[perm])
567+
M = P.M[Poutinds, Pininds]
568+
blocks, M
569+
end
570+
571+
function getinds(blocks::Vector{Vector{Int}}, d::Int)
572+
lengths = [b[2] <= 1 ? 1 : b[d] for b in blocks]
573+
endinds = cumsum(lengths)
574+
startinds = [1; endinds[1:end-1] .+ 1]
575+
inds = UnitRange.(0 .+ startinds, lengths .+ startinds .- 1)
576+
end
577+
578+
# function getinds(D::AbstractVector, d::Int)
579+
# lengths = size.(D, d)
580+
# endinds = cumsum(lengths)
581+
# startinds = [1; endinds[1:end-1] .+ 1]
582+
# inds = UnitRange.(0 .+ startinds, lengths .+ startinds .- 1)
583+
# end
584+
585+
# struct BlockDiag{T}
586+
# D::Vector{T}
587+
# rinds::Vector{UnitRange{Int}}
588+
# cinds::Vector{UnitRange{Int}}
589+
# end
590+
# BlockDiag(D::AbstractVector) = BlockDiag(D, getinds(D, 1), getinds(D, 2))
591+
592+
# Base.size(B::BlockDiag) = (B.rinds[end][end], B.cinds[end][end])
593+
594+
# function Base.:*(A, B::BlockDiag)
595+
# rinds = B.rinds
596+
# submats = map(enumerate(rinds)) do (i,inds)
597+
# A[:, inds] * B.D[i]
598+
# end
599+
# reduce(hcat, submats)
600+
# end
601+
602+
# function quadform_minus(Q::BlockDiag, M, b, op)
603+
# # con = M'Q*M - b^2*Q ⪯ I(n)
604+
# rinds = Q.rinds
605+
# cinds = Q.cinds
606+
# map(eachindex(rinds)) do i
607+
# r = rinds[i]
608+
# c = cinds[i]
609+
# q = Q.D[i]
610+
# op(M[r,:]'q*M[c,:] .- b^2*q, I(size(M,2)))
611+
# end
612+
# end
613+
614+
615+
616+
#=
617+
D_from_Delta: I-full, full-I, diag-diag
618+
Version that constructs from initial guess
619+
=#
620+
621+
622+
# See Skogestad 8.8.3
623+
# for complex δ:
624+
# Δ = δI -> μ(M) = ρ(M) 8.80
625+
# Full block complex -> μ(M) = σ̄(M) = opnorm(M)
626+
# In general ρ(M) <= μ(M) <= σ̄(M)
627+
628+
# function make_D(blocks)
629+
# D = []
630+
# for b in blocks
631+
# if b[2] == 0
632+
# n = abs(b[1])
633+
# T = b[1] > 0 ? ComplexF64 : Float64
634+
# push!(D, zeros(T, n, n))
635+
# else
636+
# push!(D, 0*I)
637+
# end
638+
# end
639+
# BlockDiagonal(D)
640+
# end

test/test_uncertainty.jl

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,75 @@ mu = ss(I(4)) + δss(4,4, bound=0.2)
189189
@test size(Pn) == size(convert(UncertainSS, Pn))
190190
@test convert(UncertainSS, Pn).D22 == Pn.D
191191

192-
193-
194192
P = Pn*mu
195-
196-
197193
Pn2 = system_mapping(P)
198194
@test Pn2 == Pn
199195
@test size(P) == (7,8)
200196
@test size(P.M) == (4,4)
201197

202198

203-
##
199+
## this time mimo real
200+
delta = uss([δr(), δr()])
201+
a = 1
202+
P = ss([0 a; -a -1], I(2), [1 a; 0 1], 0)* (ss(1.0*I(2)) + delta)
203+
K = ss(1.0I(2))
204+
205+
G = lft(P, -K)
206+
hn = norm(G, Inf)
207+
208+
w = exp10.(LinRange(-5, 1, 100))
209+
M = freqresp(G.M, w)
210+
M = permutedims(M, (2,3,1))
211+
# mu = mussv_DG(M)
212+
# maximum(mu)
213+
# # maximum(structured_singular_value(M))
214+
# @test 1/maximum(mu) ≈ √(2) atol=0.01
215+
216+
217+
218+
## Test block structure
219+
d = δr()
220+
@test RobustAndOptimalControl.makeblock([d]) == [-1, 0]
221+
@test RobustAndOptimalControl.makeblock([d,d]) == [-2, 0]
222+
223+
d = δc()
224+
@test RobustAndOptimalControl.makeblock([d]) == [1,0]
225+
@test RobustAndOptimalControl.makeblock([d,d]) == [2, 0]
226+
@test RobustAndOptimalControl.makeblock(δss(2,3).Δ) == [2, 3]
227+
228+
229+
230+
Δ = [δr(0,1,:qkarne), δr(0,1,:chuck_norris)]
231+
@test RobustAndOptimalControl.block_structure(Δ)[1] == [[-1, 0], [-1, 0]]
232+
233+
Δ = [δr(0,1,:qkarne), δc(0,1,:chuck_norris)]
234+
@test RobustAndOptimalControl.block_structure(Δ)[1] == [[1, 0], [-1, 0]] # The two elements should have been sorted and 1,-1 are therefore switched
235+
236+
237+
Δ = [δr(0,1,:a), δss(2,3).Δ[], δr(0,1,:a), δc(0,1,:chuck_norris), δr(0,1,:a)]
238+
239+
@test RobustAndOptimalControl.block_structure(Δ)[1] == [
240+
[2, 3], # The gensym name will come first
241+
[-3, 0],
242+
[1, 0],
243+
]
244+
245+
perm = RobustAndOptimalControl.block_structure(Δ)[2]
246+
@test perm == [2,1,3,5,4]
247+
248+
P = partition(ssrand(8, 7, 2), 6, 7)
249+
P = UncertainSS(P, Δ)
250+
251+
blocks, M = RobustAndOptimalControl.blocksort(P)
252+
@test blocks == [
253+
[2, 3], # The gensym name will come first
254+
[-3, 0],
255+
[1, 0],
256+
]
257+
258+
@test M[7,6] == P.M[6, 5] # test that the permutation was correctly applied, only test for the complex permutation
259+
@test M[1:3,1:2] == P.M[2:4, 2:3]# test that the permutation was correctly applied, only test for the matrix block
260+
204261
w = exp10.(LinRange(-2, 2, 500))
205262
delta = δss(1,1)
206263
P = ss(tf(1,[1, .2, 1])) * (1+0.2*delta)
@@ -249,9 +306,5 @@ dm = diskmargin(system_mapping(P), K, 0, w)
249306
dmm = argmin(dm->dm.margin, dm.simultaneous_input)
250307
@test dmm.margin 0.5335 atol = 0.001
251308

252-
## this time mimo real
253-
delta = uss([δr(), δr()])
254-
a = 1
255-
P = ss([0 a; -a -1], I(2), [1 a; 0 1], 0)* (ss(1.0*I(2)) + delta)
256-
K = ss(1.0I(2))
309+
257310

0 commit comments

Comments
 (0)