Skip to content

Commit 32bc1be

Browse files
albheimmfalt
andauthored
Update autovec and add more functions (#388)
* Fix docstring attachment issue * Add simple tests for all autovecd methods * Add autovec to freqresp * Fix how autovec handles single output function * Fix freqrespv error introduced in previous commit * Update sigma docstring Co-authored-by: Mattias Fält <mfalt@users.noreply.github.com> Co-authored-by: Mattias Fält <mfalt@users.noreply.github.com>
1 parent 1099a20 commit 32bc1be

File tree

4 files changed

+74
-22
lines changed

4 files changed

+74
-22
lines changed

src/ControlSystems.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export LTISystem,
7070
solve,
7171
Simulator,
7272
# Frequency Response
73-
freqresp,
73+
freqresp, freqrespv,
7474
evalfr,
7575
bode, bodev,
7676
nyquist, nyquistv,

src/freqresp.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Evaluate the frequency response of a linear system
55
`w -> C*((iw*im -A)^-1)*B + D`
66
77
of system `sys` over the frequency vector `w`."""
8-
function freqresp(sys::LTISystem, w_vec::AbstractVector{<:Real})
8+
@autovec () function freqresp(sys::LTISystem, w_vec::AbstractVector{<:Real})
99
# Create imaginary freq vector s
1010
if iscontinuous(sys)
1111
s_vec = im*w_vec
@@ -100,7 +100,7 @@ end
100100
Compute the magnitude and phase parts of the frequency response of system `sys`
101101
at frequencies `w`
102102
103-
`mag` and `phase` has size `(length(w), ny, nu)`""" bode
103+
`mag` and `phase` has size `(length(w), ny, nu)`"""
104104
@autovec (1, 2) function bode(sys::LTISystem, w::AbstractVector)
105105
resp = freqresp(sys, w)
106106
return abs.(resp), rad2deg.(unwrap!(angle.(resp),1)), w
@@ -112,7 +112,7 @@ end
112112
Compute the real and imaginary parts of the frequency response of system `sys`
113113
at frequencies `w`
114114
115-
`re` and `im` has size `(length(w), ny, nu)`""" nyquist
115+
`re` and `im` has size `(length(w), ny, nu)`"""
116116
@autovec (1, 2) function nyquist(sys::LTISystem, w::AbstractVector)
117117
resp = freqresp(sys, w)
118118
return real(resp), imag(resp), w
@@ -121,10 +121,10 @@ end
121121

122122
"""`sv, w = sigma(sys[, w])`
123123
124-
Compute the singular values of the frequency response of system `sys` at
124+
Compute the singular values `sv` of the frequency response of system `sys` at
125125
frequencies `w`
126126
127-
`sv` has size `(length(w), max(ny, nu))`""" sigma
127+
`sv` has size `(length(w), max(ny, nu))`"""
128128
@autovec (1) function sigma(sys::LTISystem, w::AbstractVector)
129129
resp = freqresp(sys, w)
130130
sv = dropdims(mapslices(svdvals, resp, dims=(2,3)),dims=3)

src/utilities.jl

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -153,35 +153,39 @@ index2range(ind::Colon) = ind
153153
"""@autovec (indices...) f() = (a, b, c)
154154
155155
A macro that helps in creating versions of functions where excessive dimensions are
156-
removed automatically for specific outputs. `indices` are the indexes of the outputs
157-
of the functions which should be flattened.
156+
removed automatically for specific outputs. `indices` contains each index for which
157+
the output tuple should be flattened. If the function only has a single output it
158+
(not a tuple with a single item) it should be called as `@autovec () f() = ...`.
158159
`f()` is the original function and `fv()` will be the version with flattened outputs.
159160
"""
160161
macro autovec(indices, f)
161162
dict = MacroTools.splitdef(f)
162163
rtype = get(dict, :rtype, :Any)
163164
indices = eval(indices)
164-
maxidx = max(indices...)
165-
166-
# Build the returned expression on the form (ret[1], vec(ret[2]), ret[3]...) where 2 ∈ indices
167-
idxmax = maximum(indices)
168-
return_exp = :()
169-
for i in 1:idxmax
170-
if i in indices
171-
return_exp = :($return_exp..., vec(result[$i]))
172-
else
173-
return_exp = :($return_exp..., result[$i])
165+
166+
# If indices is empty it means we vec the entire return value
167+
if length(indices) == 0
168+
return_exp = :(vec(result))
169+
else # Build the returned expression on the form (ret[1], vec(ret[2]), ret[3]...) where 2 ∈ indices
170+
idxmax = maximum(indices)
171+
return_exp = :()
172+
for i in 1:idxmax
173+
if i in indices
174+
return_exp = :($return_exp..., vec(result[$i]))
175+
else
176+
return_exp = :($return_exp..., result[$i])
177+
end
174178
end
179+
return_exp = :($return_exp..., result[$idxmax+1:end]...)
175180
end
176-
return_exp = :($return_exp..., result[$idxmax+1:end]...)
177181

178182
fname = dict[:name]
179183
args = get(dict, :args, [])
180184
kwargs = get(dict, :kwargs, [])
181185
argnames = extractvarname.(args)
182186
kwargnames = extractvarname.(kwargs)
183187
quote
184-
$(esc(f)) # Original function
188+
Core.@__doc__ $(esc(f)) # Original function
185189

186190
"""`$($(esc(fname)))v($(join($(args), ", ")); $(join($(kwargs), ", ")))`
187191

test/test_autovec.jl

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
21
@testset "test_autovec" begin
32

43

4+
# Test all different autovecd methods
55
sys = tf([1], [1, 2])
6+
w = exp10.(LinRange(-3,3,10))
67

78
# Check output of bode and make sure dimensions are correct
89
mag, phase, w = bode(sys)
@@ -13,12 +14,59 @@ magv, phasev, w = bodev(sys)
1314
@test size(magv) == size(phasev) == size(w)
1415
@test vec(mag) == magv && vec(phase) == phasev
1516

16-
w = exp10.(LinRange(-3,3,10))
1717
mag, phase, _ = bodev(sys, w)
1818
@test size(mag) == size(phase) == size(w)
1919

20+
# Check output of nyquist and make sure dimensions are correct
21+
real, imag, w = nyquist(sys)
22+
@test size(real) == size(imag) == (size(w,1), 1, 1)
23+
24+
# Check output of nyquistv and make sure dimensions are correct
25+
realv, imagv, w = nyquistv(sys)
26+
@test size(realv) == size(imagv) == size(w)
27+
@test vec(real) == realv && vec(imag) == imagv
28+
29+
real, imag, _ = nyquistv(sys, w)
30+
@test size(real) == size(imag) == size(w)
31+
32+
# Check output of sigma and make sure dimensions are correct
33+
sv, w = sigma(sys)
34+
@test size(sv) == (size(w,1), 1)
35+
36+
# Check output of sigmav and make sure dimensions are correct
37+
svv, w = sigmav(sys)
38+
@test size(svv) == size(w)
39+
@test vec(sv) == svv
40+
41+
sv, _ = sigmav(sys, w)
42+
@test size(sv) == size(w)
43+
44+
# Check output of freqresp and make sure dimensions are correct
45+
fs = freqresp(sys, w)
46+
@test size(fs) == (size(w,1), 1, 1)
47+
48+
# Check output of freqrespv and make sure dimensions are correct
49+
fsv = freqrespv(sys, w)
50+
@test size(fsv) == size(w)
51+
@test vec(fs) == fsv
52+
53+
54+
55+
56+
57+
2058
# Test that we can define varous kinds of methods with autovec
2159

60+
# Test arguments for single output
61+
ControlSystems.@autovec () function t0(b::Int, c::Float64=1.0)
62+
return [b c]
63+
end
64+
65+
@test @isdefined t0
66+
@test @isdefined t0v
67+
@test t0(5) == [5.0 1.0]
68+
@test t0v(5) == [5.0, 1.0]
69+
2270
# Test arguments
2371
ControlSystems.@autovec (2,) function t0(a, b::Int, c::Float64=1.0)
2472
return a, [b c]

0 commit comments

Comments
 (0)