57
57
polyadd , polymul , polyval , roots , sqrt , zeros , squeeze , exp , pi , \
58
58
where , delete , real , poly , nonzero
59
59
import scipy as sp
60
- from numpy .polynomial .polynomial import polyfromroots
61
60
from scipy .signal import lti , tf2zpk , zpk2tf , cont2discrete
62
61
from copy import deepcopy
63
62
from warnings import warn
@@ -803,8 +802,6 @@ def _common_den(self, imag_tol=None):
803
802
output numerator array num is modified to use the common
804
803
denominator for this input/column; the coefficient arrays are also
805
804
padded with zeros to be the same size for all num/den.
806
- num is an sys.outputs by sys.inputs
807
- by len(d) array.
808
805
809
806
Parameters
810
807
----------
@@ -815,17 +812,20 @@ def _common_den(self, imag_tol=None):
815
812
Returns
816
813
-------
817
814
num: array
818
- Multi-dimensional array of numerator coefficients. num[i][j]
819
- gives the numerator coefficient array for the ith input and jth
820
- output, also prepared for use in td04ad; matches the denorder
821
- order; highest coefficient starts on the left.
815
+ n by n by kd where n = max(sys.outputs,sys.inputs)
816
+ kd = max(denorder)+1
817
+ Multi-dimensional array of numerator coefficients. num[i,j]
818
+ gives the numerator coefficient array for the ith output and jth
819
+ input; padded for use in td04ad ('C' option); matches the
820
+ denorder order; highest coefficient starts on the left.
822
821
823
822
den: array
823
+ sys.inputs by kd
824
824
Multi-dimensional array of coefficients for common denominator
825
825
polynomial, one row per input. The array is prepared for use in
826
826
slycot td04ad, the first element is the highest-order polynomial
827
- coefficiend of s, matching the order in denorder, if denorder <
828
- number of columns in den, the den is padded with zeros
827
+ coefficient of s, matching the order in denorder. If denorder <
828
+ number of columns in den, the den is padded with zeros.
829
829
830
830
denorder: array of int, orders of den, one per input
831
831
@@ -839,16 +839,18 @@ def _common_den(self, imag_tol=None):
839
839
840
840
# Machine precision for floats.
841
841
eps = finfo (float ).eps
842
+ real_tol = sqrt (eps * self .inputs * self .outputs )
842
843
843
- # Decide on the tolerance to use in deciding of a pole is complex
844
+ # The tolerance to use in deciding if a pole is complex
844
845
if (imag_tol is None ):
845
- imag_tol = 1e-8 # TODO: figure out the right number to use
846
+ imag_tol = 2 * real_tol
846
847
847
848
# A list to keep track of cumulative poles found as we scan
848
849
# self.den[..][..]
849
850
poles = [[] for j in range (self .inputs )]
850
851
851
852
# RvP, new implementation 180526, issue #194
853
+ # BG, modification, issue #343, PR #354
852
854
853
855
# pre-calculate the poles for all num, den
854
856
# has zeros, poles, gain, list for pole indices not in den,
@@ -867,30 +869,36 @@ def _common_den(self, imag_tol=None):
867
869
poleset [- 1 ].append ([z , p , k , [], 0 ])
868
870
869
871
# collect all individual poles
870
- epsnm = eps * self .inputs * self .outputs
871
872
for j in range (self .inputs ):
872
873
for i in range (self .outputs ):
873
874
currentpoles = poleset [i ][j ][1 ]
874
875
nothave = ones (currentpoles .shape , dtype = bool )
875
876
for ip , p in enumerate (poles [j ]):
876
- idx , = nonzero (
877
- (abs (currentpoles - p ) < epsnm ) * nothave )
878
- if len (idx ):
879
- nothave [idx [0 ]] = False
877
+ collect = (np .isclose (currentpoles .real , p .real ,
878
+ atol = real_tol ) &
879
+ np .isclose (currentpoles .imag , p .imag ,
880
+ atol = imag_tol ) &
881
+ nothave )
882
+ if np .any (collect ):
883
+ # mark first found pole as already collected
884
+ nothave [nonzero (collect )[0 ][0 ]] = False
880
885
else :
881
886
# remember id of pole not in tf
882
887
poleset [i ][j ][3 ].append (ip )
883
888
for h , c in zip (nothave , currentpoles ):
884
889
if h :
890
+ if abs (c .imag ) < imag_tol :
891
+ c = c .real
885
892
poles [j ].append (c )
886
893
# remember how many poles now known
887
894
poleset [i ][j ][4 ] = len (poles [j ])
888
895
889
896
# figure out maximum number of poles, for sizing the den
890
- npmax = max ([len (p ) for p in poles ])
891
- den = zeros ((self .inputs , npmax + 1 ), dtype = float )
897
+ maxindex = max ([len (p ) for p in poles ])
898
+ den = zeros ((self .inputs , maxindex + 1 ), dtype = float )
892
899
num = zeros ((max (1 , self .outputs , self .inputs ),
893
- max (1 , self .outputs , self .inputs ), npmax + 1 ),
900
+ max (1 , self .outputs , self .inputs ),
901
+ maxindex + 1 ),
894
902
dtype = float )
895
903
denorder = zeros ((self .inputs ,), dtype = int )
896
904
@@ -901,12 +909,11 @@ def _common_den(self, imag_tol=None):
901
909
for i in range (self .outputs ):
902
910
num [i , j , 0 ] = poleset [i ][j ][2 ]
903
911
else :
904
- # create the denominator matching this input polyfromroots
905
- # gives coeffs in opposite order from what we use coefficients
906
- # should be padded on right, ending at np
907
- np = len (poles [j ])
908
- den [j , np ::- 1 ] = polyfromroots (poles [j ]).real
909
- denorder [j ] = np
912
+ # create the denominator matching this input
913
+ # coefficients should be padded on right, ending at maxindex
914
+ maxindex = len (poles [j ])
915
+ den [j , :maxindex + 1 ] = poly (poles [j ])
916
+ denorder [j ] = maxindex
910
917
911
918
# now create the numerator, also padded on the right
912
919
for i in range (self .outputs ):
@@ -915,22 +922,15 @@ def _common_den(self, imag_tol=None):
915
922
# add all poles not found in the original denominator,
916
923
# and the ones later added from other denominators
917
924
for ip in chain (poleset [i ][j ][3 ],
918
- range (poleset [i ][j ][4 ], np )):
925
+ range (poleset [i ][j ][4 ], maxindex )):
919
926
nwzeros .append (poles [j ][ip ])
920
927
921
- numpoly = poleset [i ][j ][2 ] * polyfromroots (nwzeros ).real
922
- # print(numpoly, den[j])
923
- # polyfromroots gives coeffs in opposite order => invert
928
+ numpoly = poleset [i ][j ][2 ] * np .atleast_1d (poly (nwzeros ))
924
929
# numerator polynomial should be padded on left and right
925
- # ending at np to line up with what td04ad expects.. .
926
- num [i , j , np + 1 - len (numpoly ):np + 1 ] = numpoly [:: - 1 ]
930
+ # ending at maxindex to line up with what td04ad expects.
931
+ num [i , j , maxindex + 1 - len (numpoly ):maxindex + 1 ] = numpoly
927
932
# print(num[i, j])
928
933
929
- if (abs (den .imag ) > epsnm ).any ():
930
- print ("Warning: The denominator has a nontrivial imaginary part: "
931
- " %f" % abs (den .imag ).max ())
932
- den = den .real
933
-
934
934
return num , den , denorder
935
935
936
936
def sample (self , Ts , method = 'zoh' , alpha = None ):
0 commit comments