Skip to content

Commit c174425

Browse files
authored
add in-place version of freqresp (#644)
* add in-place version of `freqresp` * inplace freqresp for delay systems
1 parent 0fec64d commit c174425

File tree

3 files changed

+51
-20
lines changed

3 files changed

+51
-20
lines changed

src/ControlSystems.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export LTISystem,
7272
solve,
7373
Simulator,
7474
# Frequency Response
75-
freqresp, freqrespv,
75+
freqresp, freqrespv, freqresp!,
7676
evalfr,
7777
bode, bodev,
7878
nyquist, nyquistv,

src/delay_systems.jl

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
function freqresp(sys::DelayLtiSystem, ω::AbstractVector{T}) where {T <: Real}
1+
function freqresp!(R::Array{T,3}, sys::DelayLtiSystem, ω::AbstractVector{W}) where {T, W <: Real}
22
ny = noutputs(sys)
33
nu = ninputs(sys)
4-
4+
@boundscheck size(R) == (ny,nu,length(ω))
55
P_fr = freqresp(sys.P.P, ω).parent
66

7-
G_fr = zeros(eltype(P_fr), ny, nu, length(ω))
8-
97
cache = cis.(ω[1].*sys.Tau)
108

119
@views for ω_idx=1:length(ω)
@@ -17,10 +15,9 @@ function freqresp(sys::DelayLtiSystem, ω::AbstractVector{T}) where {T <: Real}
1715
delay_matrix_inv_fr = Diagonal(cache) # Frequency response of the diagonal matrix with delays
1816
# Inverse of the delay matrix, so there should not be any minus signs in the exponents
1917

20-
G_fr[:,:,ω_idx] .= P11_fr .+ P12_fr/(delay_matrix_inv_fr - P22_fr)*P21_fr # The matrix is invertible (?!)
18+
R[:,:,ω_idx] .= P11_fr .+ P12_fr/(delay_matrix_inv_fr - P22_fr)*P21_fr # The matrix is invertible (?!)
2119
end
22-
23-
return PermutedDimsArray(G_fr, (3,1,2))
20+
return PermutedDimsArray{T,3,(3,1,2),(2,3,1),Array{T,3}}(R)
2421
end
2522

2623
function evalfr(sys::DelayLtiSystem, s)

src/freqresp.jl

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,27 @@ Evaluate the frequency response of a linear system
1919
2020
of system `sys` over the frequency vector `w`.
2121
"""
22-
@autovec () function freqresp(sys::LTISystem, w_vec::AbstractVector{<:Real})
22+
@autovec () function freqresp(sys::LTISystem, w_vec::AbstractVector{W}) where W <: Real
23+
te = timeevol(sys)
24+
ny,nu = noutputs(sys), ninputs(sys)
25+
T = promote_type(Complex{real(numeric_type(sys))}, Complex{W})
26+
R = Array{T, 3}(undef, ny, nu, length(w_vec))
27+
freqresp!(R, sys, w_vec)
28+
end
29+
30+
"""
31+
freqresp!(R::Array{T, 3}, sys::LTISystem, w_vec::AbstractVector{<:Real})
32+
33+
In-place version of [`freqresp`](@ref) that takes a pre-allocated array `R` of size (ny, nu, nw)`
34+
"""
35+
function freqresp!(R::Array{T,3}, sys::LTISystem, w_vec::AbstractVector{<:Real}) where T
2336
te = sys.timeevol
2437
ny,nu = noutputs(sys), ninputs(sys)
25-
[evalfr(sys[i,j], _freq(w, te))[] for w in w_vec, i in 1:ny, j in 1:nu]
38+
@boundscheck size(R) == (ny,nu,length(w_vec))
39+
@inbounds for wi = eachindex(w_vec), ui = 1:nu, yi = 1:ny
40+
R[yi,ui,wi] = evalfr(sys[yi,ui], _freq(w_vec[wi], te))[]
41+
end
42+
PermutedDimsArray{T,3,(3,1,2),(2,3,1),Array{T,3}}(R)
2643
end
2744

2845
@autovec () function freqresp(G::AbstractMatrix, w_vec::AbstractVector{<:Real})
@@ -36,20 +53,29 @@ end
3653
_freq(w, ::Continuous) = complex(0, w)
3754
_freq(w, te::Discrete) = cis(w*te.Ts)
3855

39-
@autovec () function freqresp(sys::AbstractStateSpace, w_vec::AbstractVector{W}) where W <: Real
56+
function freqresp(sys::AbstractStateSpace, w_vec::AbstractVector{W}) where W <: Real
4057
ny, nu = size(sys)
4158
T = promote_type(Complex{real(eltype(sys.A))}, Complex{W})
59+
R = Array{T, 3}(undef, ny, nu, length(w_vec))
60+
freqresp!(R, sys, w_vec)
61+
end
62+
@autovec () function freqresp!(R::Array{T,3}, sys::AbstractStateSpace, w_vec::AbstractVector{W}) where {T, W <: Real}
63+
ny, nu = size(sys)
64+
@boundscheck size(R) == (ny,nu,length(w_vec))
4265
PDT = PermutedDimsArray{T,3,(3,1,2),(2,3,1),Array{T,3}}
4366
if sys.nx == 0 # Only D-matrix
44-
return PDT(repeat(T.(sys.D), 1, 1, length(w_vec)))
67+
@inbounds for i in eachindex(w_vec)
68+
R[:,:,i] .= sys.D
69+
end
70+
return PDT(R)
4571
end
4672
local F, Q
4773
try
4874
F = hessenberg(sys.A)
4975
Q = Matrix(F.Q)
5076
catch e
5177
# For matrix types that do not have a hessenberg implementation, we call the standard version of freqresp.
52-
e isa MethodError && return freqresp_nohess(sys, w_vec)
78+
e isa MethodError && return freqresp_nohess!(R, sys, w_vec)
5379
rethrow()
5480
end
5581
A = F.H
@@ -58,9 +84,8 @@ _freq(w, te::Discrete) = cis(w*te.Ts)
5884
D = sys.D
5985

6086
te = sys.timeevol
61-
R = Array{T, 3}(undef, ny, nu, length(w_vec))
6287
Bc = similar(B, T) # for storage
63-
for i in eachindex(w_vec)
88+
@inbounds for i in eachindex(w_vec)
6489
Ri = @views R[:,:,i]
6590
copyto!(Ri,D) # start with the D-matrix
6691
isinf(w_vec[i]) && continue
@@ -72,27 +97,36 @@ _freq(w, te::Discrete) = cis(w*te.Ts)
7297
PDT(R) # PermutedDimsArray doesn't allocate to perform the permutation
7398
end
7499

100+
function freqresp_nohess(sys::AbstractStateSpace, w_vec::AbstractVector{W}) where W <: Real
101+
ny, nu = size(sys)
102+
T = promote_type(Complex{real(eltype(sys.A))}, Complex{W})
103+
R = Array{T, 3}(undef, ny, nu, length(w_vec))
104+
freqresp_nohess!(R, sys, w_vec)
105+
end
106+
75107
"""
76108
freqresp_nohess(sys::AbstractStateSpace, w_vec::AbstractVector{<:Real})
77109
78110
Compute the frequency response of `sys` without forming a Hessenberg factorization.
79111
This function is called automatically if the Hessenberg factorization fails.
80112
"""
81113
freqresp_nohess
82-
@autovec () function freqresp_nohess(sys::AbstractStateSpace, w_vec::AbstractVector{W}) where W <: Real
114+
@autovec () function freqresp_nohess!(R::Array{T,3}, sys::AbstractStateSpace, w_vec::AbstractVector{W}) where {T, W <: Real}
83115
ny, nu = size(sys)
116+
@boundscheck size(R) == (ny,nu,length(w_vec))
84117
nx = sys.nx
85-
T = promote_type(Complex{real(eltype(sys.A))}, Complex{W})
86118
PDT = PermutedDimsArray{T,3,(3,1,2),(2,3,1),Array{T,3}}
87119
if nx == 0 # Only D-matrix
88-
return PDT(repeat(T.(sys.D), 1, 1, length(w_vec)))
120+
@inbounds for i in eachindex(w_vec)
121+
R[:,:,i] .= sys.D
122+
end
123+
return PDT(R)
89124
end
90125
A,B,C,D = ssdata(sys)
91126
te = sys.timeevol
92-
R = Array{T, 3}(undef, ny, nu, length(w_vec))
93127
Ac = (A+one(T)*I) # for storage
94128
Adiag = diagind(A)
95-
for i in eachindex(w_vec)
129+
@inbounds for i in eachindex(w_vec)
96130
Ri = @views R[:,:,i]
97131
copyto!(Ri,D) # start with the D-matrix
98132
isinf(w_vec[i]) && continue

0 commit comments

Comments
 (0)