Skip to content

Commit 2122225

Browse files
author
Kevin Chen
committed
Modified _common_den to handle complex roots more accurately.
Minor changes in TestConvert.py and TestBDAlg.py. Kevin K. Chen <kkchen@princeton.edu>
1 parent 2e6d16c commit 2122225

3 files changed

Lines changed: 37 additions & 16 deletions

File tree

src/TestBDAlg.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def testScalarSS(self):
3838
ans1 = feedback(self.x1, self.sys2)
3939
ans2 = feedback(self.x1, self.sys2, 1.)
4040

41-
# This one doesn't work yet. The feedback system has too many states.
4241
np.testing.assert_array_almost_equal(ans1.A, [[-1.5, 4.], [13., 2.]])
4342
np.testing.assert_array_almost_equal(ans1.B, [[2.5], [-10.]])
4443
np.testing.assert_array_almost_equal(ans1.C, [[-2.5, 0.]])
@@ -65,7 +64,6 @@ def testSSScalar(self):
6564
ans1 = feedback(self.sys2, self.x1)
6665
ans2 = feedback(self.sys2, self.x1, 1.)
6766

68-
# This one doesn't work yet. The feedback system has too many states.
6967
np.testing.assert_array_almost_equal(ans1.A, [[-1.5, 4.], [13., 2.]])
7068
np.testing.assert_array_almost_equal(ans1.B, [[1.], [-4.]])
7169
np.testing.assert_array_almost_equal(ans1.C, [[1., 0.]])

src/TestConvert.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
55
Test state space and transfer function conversion.
66
7-
Currently, this unit test script is not complete. It converts
8-
several random state spaces back and forth between state space and transfer
9-
function representations, and asserts that the conversion outputs are correct.
10-
As it stands, tests pass but there is some rounding error in one of the conversions leading to imaginary numbers. See the warning message. This script may be used to diagnose errors.
7+
Currently, this unit test script is not complete. It converts several random
8+
state spaces back and forth between state space and transfer function
9+
representations. Ideally, it should be able to assert that the conversion
10+
outputs are correct. This is not yet implemented.
11+
12+
Also, the conversion seems to enter an infinite loop once in a while. The cause
13+
of this is unknown.
1114
1215
"""
1316

@@ -22,11 +25,11 @@ def setUp(self):
2225
"""Set up testing parameters."""
2326

2427
# Number of times to run each of the randomized tests.
25-
self.numTests = 1
28+
self.numTests = 10
2629
# Maximum number of states to test + 1
27-
self.maxStates = 3
30+
self.maxStates = 2
2831
# Maximum number of inputs and outputs to test + 1
29-
self.maxIO = 3
32+
self.maxIO = 2
3033
# Set to True to print systems to the output.
3134
self.debug = True
3235

src/xferfcn.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
"""
7676

7777
# External function declarations
78-
from numpy import angle, array, empty, finfo, insert, ndarray, ones, polyadd, \
79-
polymul, polyval, roots, sort, zeros
78+
from numpy import angle, any, array, empty, finfo, insert, ndarray, ones, \
79+
polyadd, polymul, polyval, roots, sort, sqrt, zeros
8080
from scipy.signal import lti
8181
from copy import deepcopy
8282
from slycot import tb04ad
@@ -491,7 +491,10 @@ def _common_den(self):
491491
[something] array.
492492
493493
"""
494-
494+
495+
# Machine precision for floats.
496+
eps = finfo(float).eps
497+
495498
# A sorted list to keep track of cumulative poles found as we scan
496499
# self.den.
497500
poles = []
@@ -512,8 +515,7 @@ def _common_den(self):
512515
# cumulative poles, until one of them reaches the end. Keep in
513516
# mind that both lists are always sorted.
514517
while cp_ind < len(currentpoles) and p_ind < len(poles):
515-
if abs(currentpoles[cp_ind] - poles[p_ind]) < (10 *
516-
finfo(float).eps):
518+
if abs(currentpoles[cp_ind] - poles[p_ind]) < (10 * eps):
517519
# If the current element of both lists match, then we're
518520
# good. Move to the next pair of elements.
519521
cp_ind += 1
@@ -558,8 +560,21 @@ def _common_den(self):
558560

559561
# Construct the common denominator.
560562
den = 1.
561-
for p in poles:
562-
den = polymul(den, [1., -p])
563+
n = 0
564+
while n < len(poles):
565+
if abs(poles[n].imag) > 10 * eps:
566+
# To prevent buildup of imaginary part error, handle complex
567+
# pole pairs together.
568+
quad = polymul([1., -poles[n]], [1., -poles[n+1]])
569+
assert all(quad.imag < eps), "The quadratic has a nontrivial \
570+
imaginary part: %g" % quad.imag.max()
571+
quad = quad.real
572+
573+
den = polymul(den, quad)
574+
n += 2
575+
else:
576+
den = polymul(den, [1., -poles[n].real])
577+
n += 1
563578

564579
# Modify the numerators so that they each take the common denominator.
565580
num = deepcopy(self.num)
@@ -585,6 +600,11 @@ def _common_den(self):
585600
zeros(largest - len(num[i][j])))
586601
# Finally, convert the numerator to a 3-D array.
587602
num = array(num)
603+
# Remove trivial imaginary parts. Check for nontrivial imaginary parts.
604+
if any(abs(num.imag) > sqrt(eps)):
605+
print ("Warning: The numerator has a nontrivial nontrivial part: %g"
606+
% abs(num.imag).max())
607+
num = num.real
588608

589609
return num, den
590610

0 commit comments

Comments
 (0)