5757 polyadd , polymul , polyval , roots , sqrt , zeros , squeeze , exp , pi , \
5858 where , delete , real , poly , nonzero
5959import scipy as sp
60- from numpy .polynomial .polynomial import polyfromroots
6160from scipy .signal import lti , tf2zpk , zpk2tf , cont2discrete
6261from copy import deepcopy
6362from warnings import warn
@@ -803,8 +802,6 @@ def _common_den(self, imag_tol=None):
803802 output numerator array num is modified to use the common
804803 denominator for this input/column; the coefficient arrays are also
805804 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.
808805
809806 Parameters
810807 ----------
@@ -815,17 +812,20 @@ def _common_den(self, imag_tol=None):
815812 Returns
816813 -------
817814 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.
822821
823822 den: array
823+ sys.inputs by kd
824824 Multi-dimensional array of coefficients for common denominator
825825 polynomial, one row per input. The array is prepared for use in
826826 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.
829829
830830 denorder: array of int, orders of den, one per input
831831
@@ -839,16 +839,18 @@ def _common_den(self, imag_tol=None):
839839
840840 # Machine precision for floats.
841841 eps = finfo (float ).eps
842+ real_tol = sqrt (eps * self .inputs * self .outputs )
842843
843- # Decide on the tolerance to use in deciding if a pole is complex
844+ # The tolerance to use in deciding if a pole is complex
844845 if (imag_tol is None ):
845- imag_tol = 1e-8 # TODO: figure out the right number to use
846+ imag_tol = 2 * real_tol
846847
847848 # A list to keep track of cumulative poles found as we scan
848849 # self.den[..][..]
849850 poles = [[] for j in range (self .inputs )]
850851
851852 # RvP, new implementation 180526, issue #194
853+ # BG, modification, issue #343, PR #354
852854
853855 # pre-calculate the poles for all num, den
854856 # has zeros, poles, gain, list for pole indices not in den,
@@ -867,31 +869,34 @@ def _common_den(self, imag_tol=None):
867869 poleset [- 1 ].append ([z , p , k , [], 0 ])
868870
869871 # collect all individual poles
870- epsnm = eps * self .inputs * self .outputs
871872 for j in range (self .inputs ):
872873 for i in range (self .outputs ):
873874 currentpoles = poleset [i ][j ][1 ]
874875 nothave = ones (currentpoles .shape , dtype = bool )
875876 for ip , p in enumerate (poles [j ]):
876- idx , = nonzero (
877- (abs (currentpoles - p ) < sqrt (epsnm )) * nothave )
878- if len (idx ):
879- nothave [idx [0 ]] = False
877+ collect = ((abs (currentpoles .real - p .real ) < real_tol ) &
878+ (abs (currentpoles .imag - p .imag ) < imag_tol ) &
879+ nothave )
880+ if np .any (collect ):
881+ # mark first found pole as already collected
882+ nothave [nonzero (collect )[0 ][0 ]] = False
880883 else :
881884 # remember id of pole not in tf
882885 poleset [i ][j ][3 ].append (ip )
883886 for h , c in zip (nothave , currentpoles ):
884887 if h :
888+ if abs (c .imag ) < imag_tol :
889+ c = c .real
885890 poles [j ].append (c )
886891 # remember how many poles now known
887892 poleset [i ][j ][4 ] = len (poles [j ])
888893
889894 # 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 )
895+ maxindex = max ([len (p ) for p in poles ])
896+ den = zeros ((self .inputs , maxindex + 1 ), dtype = float )
892897 num = zeros ((max (1 , self .outputs , self .inputs ),
893898 max (1 , self .outputs , self .inputs ),
894- npmax + 1 ),
899+ maxindex + 1 ),
895900 dtype = float )
896901 denorder = zeros ((self .inputs ,), dtype = int )
897902
@@ -903,18 +908,10 @@ def _common_den(self, imag_tol=None):
903908 num [i , j , 0 ] = poleset [i ][j ][2 ]
904909 else :
905910 # create the denominator matching this input
906- # polyfromroots gives coeffs in opposite order from what we use
907- # coefficients should be padded on right, ending at np
908- np = len (poles [j ])
909- denpoly = polyfromroots (poles [j ])
910- if (abs (denpoly .imag ) > imag_tol ).any ():
911- warn ("The denominator of the transfer function "
912- "for input {j} to output {i} has a nontrivial "
913- "imaginary part of {imag}, which was removed" .format (
914- i = i , j = j , imag = max (denpoly .imag )))
915- denpoly = denpoly .real
916- den [j , np ::- 1 ] = denpoly
917- denorder [j ] = np
911+ # coefficients should be padded on right, ending at maxindex
912+ maxindex = len (poles [j ])
913+ den [j , :maxindex + 1 ] = poly (poles [j ])
914+ denorder [j ] = maxindex
918915
919916 # now create the numerator, also padded on the right
920917 for i in range (self .outputs ):
@@ -923,20 +920,17 @@ def _common_den(self, imag_tol=None):
923920 # add all poles not found in the original denominator,
924921 # and the ones later added from other denominators
925922 for ip in chain (poleset [i ][j ][3 ],
926- range (poleset [i ][j ][4 ], np )):
923+ range (poleset [i ][j ][4 ], maxindex )):
927924 nwzeros .append (poles [j ][ip ])
928925
929- numpoly = poleset [i ][j ][2 ] * polyfromroots (nwzeros ).real
930- # print(numpoly, den[j])
931- # polyfromroots gives coeffs in opposite order => invert
926+ numpoly = poleset [i ][j ][2 ] * np .atleast_1d (poly (nwzeros ))
932927 # numerator polynomial should be padded on left and right
933- # ending at np to line up with what td04ad expects.. .
934- num [i , j , np + 1 - len (numpoly ):np + 1 ] = numpoly [:: - 1 ]
928+ # ending at maxindex to line up with what td04ad expects.
929+ num [i , j , maxindex + 1 - len (numpoly ):maxindex + 1 ] = numpoly
935930 # print(num[i, j])
936931
937932 return num , den , denorder
938933
939-
940934 def sample (self , Ts , method = 'zoh' , alpha = None ):
941935 """Convert a continuous-time system to discrete time
942936
0 commit comments