Skip to content

Commit 242d2b4

Browse files
committed
Speed up freqresp for transfer functions by an order of magnitude
When determining the frequency response, the list of frequencies is now stored as a numpy array, instead of a list, and evaluated all at once, instead of using `map`. This is much faster. The improved performance can be measured using a script `bench/time_freqresp.py`. On my machine, I get a speedup of more than a factor of 20 (from 3.9 seconds to 0.18 seconds) for a 10th-order transfer function.
1 parent 03c6637 commit 242d2b4

2 files changed

Lines changed: 23 additions & 11 deletions

File tree

control/bench/time_freqresp.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from control import tf
2+
from control.matlab import rss
3+
from numpy import logspace
4+
from timeit import timeit
5+
6+
nstates = 10
7+
sys = rss(nstates)
8+
sys_tf = tf(sys)
9+
w = logspace(-1,1,50)
10+
ntimes = 1000
11+
time_ss = timeit("sys.freqresp(w)", setup="from __main__ import sys, w", number=ntimes)
12+
time_tf = timeit("sys_tf.freqresp(w)", setup="from __main__ import sys_tf, w", number=ntimes)
13+
print("State-space model on %d states: %f" % (nstates, time_ss))
14+
print("Transfer-function model on %d states: %f" % (nstates, time_tf))

control/xferfcn.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -242,18 +242,18 @@ def __init__(self, *args):
242242
self._truncatecoeff()
243243

244244
def __call__(self, s):
245-
"""Evaluate the system's transfer function for a complex vairable
245+
"""Evaluate the system's transfer function for a complex variable
246246
247247
For a SISO transfer function, returns the value of the
248248
transfer function. For a MIMO transfer fuction, returns a
249249
matrix of values evaluated at complex variable s."""
250250

251-
if (self.inputs > 1 or self.outputs > 1):
252-
# MIMO transfer function, return a matrix
253-
return self.horner(s)
254-
else:
255-
# SISO transfer function, return a scalar
251+
if self.issiso():
252+
# return a scalar
256253
return self.horner(s)[0][0]
254+
else:
255+
# return a matrix
256+
return self.horner(s)
257257

258258
def _truncatecoeff(self):
259259
"""Remove extraneous zero coefficients from num and den.
@@ -615,15 +615,13 @@ def freqresp(self, omega):
615615
if (max(omega) * dt > pi):
616616
warn("evalfr: frequency evaluation above Nyquist frequency")
617617
else:
618-
slist = map(lambda w: 1.j * w, omega)
618+
slist = np.array([1j * w for w in omega])
619619

620620
# Compute frequency response for each input/output pair
621621
for i in range(self.outputs):
622622
for j in range(self.inputs):
623-
fresp = map(lambda s: (polyval(self.num[i][j], s) /
624-
polyval(self.den[i][j], s)), slist)
625-
fresp = array(list(fresp))
626-
623+
fresp = (polyval(self.num[i][j], slist) /
624+
polyval(self.den[i][j], slist))
627625
mag[i, j, :] = abs(fresp)
628626
phase[i, j, :] = angle(fresp)
629627

0 commit comments

Comments
 (0)