@@ -15,6 +15,7 @@ def sys_dict():
15
15
sdict = {}
16
16
sdict ['ss' ] = ct .ss ([[- 1 ]], [[1 ]], [[1 ]], [[0 ]])
17
17
sdict ['tf' ] = ct .tf ([1 ],[0.5 , 1 ])
18
+ sdict ['tfx' ] = ct .tf ([1 , 1 ],[1 ]) # non-proper transfer function
18
19
sdict ['frd' ] = ct .frd ([10 + 0j , 9 + 1j , 8 + 2j ], [1 ,2 ,3 ])
19
20
sdict ['lio' ] = ct .LinearIOSystem (ct .ss ([[- 1 ]], [[5 ]], [[5 ]], [[0 ]]))
20
21
sdict ['ios' ] = ct .NonlinearIOSystem (
@@ -29,7 +30,7 @@ def sys_dict():
29
30
'ios' : ct .InterconnectedSystem , 'arr' : np .ndarray , 'flt' : float }
30
31
31
32
#
32
- # Table of expected conversions
33
+ # Current table of expected conversions
33
34
#
34
35
# This table describes all of the conversions that are supposed to
35
36
# happen for various system combinations. This is written out this way
@@ -50,6 +51,10 @@ def sys_dict():
50
51
# Note 2: eventually the operator entry for this table can be pulled out and
51
52
# tested as a separate parameterized variable (since all operators should
52
53
# return consistent values).
54
+ #
55
+ # Note 3: this table documents the current state, but not actually the desired
56
+ # state. See bottom of the file for the (eventual) desired behavior.
57
+ #
53
58
54
59
rtype_list = ['ss' , 'tf' , 'frd' , 'lio' , 'ios' , 'arr' , 'flt' ]
55
60
conversion_table = [
@@ -97,7 +102,7 @@ def sys_dict():
97
102
test_matrix .append ([opname , ltype , rtype , expected ])
98
103
99
104
@pytest .mark .parametrize ("opname, ltype, rtype, expected" , test_matrix )
100
- def test_xferfcn_ndarray_precedence (opname , ltype , rtype , expected , sys_dict ):
105
+ def test_operator_type_conversion (opname , ltype , rtype , expected , sys_dict ):
101
106
op = getattr (operator , opname )
102
107
leftsys = sys_dict [ltype ]
103
108
rightsys = sys_dict [rtype ]
@@ -117,3 +122,66 @@ def test_xferfcn_ndarray_precedence(opname, ltype, rtype, expected, sys_dict):
117
122
118
123
# Print out what we are testing in case something goes wrong
119
124
assert isinstance (result , type_dict [expected ])
125
+
126
+ #
127
+ # Updated table that describes desired outputs for all operators
128
+ #
129
+ # General rules (subject to change)
130
+ #
131
+ # * For LTI/LTI, keep the type of the left operand whenever possible. This
132
+ # prioritizes the first operand, but we need to watch out for non-proper
133
+ # transfer functions (in which case TransferFunction should be returned)
134
+ #
135
+ # * For FRD/LTI, convert LTI to FRD by evaluating the LTI transfer function
136
+ # at the FRD frequencies (can't got the other way since we can't convert
137
+ # an FRD object to state space/transfer function).
138
+ #
139
+ # * For IOS/LTI, convert to IOS. In the case of a linear I/O system (LIO),
140
+ # this will preserve the linear structure since the LTI system will
141
+ # be converted to state space.
142
+ #
143
+ # * When combining state space or transfer with linear I/O systems, the
144
+ # * output should be of type Linear IO system, since that maintains the
145
+ # * underlying state space attributes.
146
+ #
147
+ # Note: tfx = non-proper transfer function, order(num) > order(den)
148
+ #
149
+
150
+ type_list = ['ss' , 'tf' , 'tfx' , 'frd' , 'lio' , 'ios' , 'arr' , 'flt' ]
151
+ conversion_table = [
152
+ # L / R ['ss', 'tf', 'tfx', 'frd', 'lio', 'ios', 'arr', 'flt']
153
+ ('ss' , ['ss' , 'ss' , 'tf' 'frd' , 'lio' , 'ios' , 'ss' , 'ss' ]),
154
+ ('tf' , ['tf' , 'tf' , 'tf' 'frd' , 'lio' , 'ios' , 'tf' , 'tf' ]),
155
+ ('tfx' , ['tf' , 'tf' , 'tf' , 'frd' , 'E' , 'E' , 'tf' , 'tf' ]),
156
+ ('frd' , ['frd' , 'frd' , 'frd' , 'frd' , 'E' , 'E' , 'frd' , 'frd' ]),
157
+ ('lio' , ['lio' , 'lio' , 'E' , 'E' , 'lio' , 'ios' , 'lio' , 'lio' ]),
158
+ ('ios' , ['ios' , 'ios' , 'E' , 'E' , 'ios' , 'ios' , 'ios' , 'ios' ]),
159
+ ('arr' , ['ss' , 'tf' , 'tf' 'frd' , 'lio' , 'ios' , 'arr' , 'arr' ]),
160
+ ('flt' , ['ss' , 'tf' , 'tf' 'frd' , 'lio' , 'ios' , 'arr' , 'flt' ])]
161
+
162
+ @pytest .mark .skip (reason = "future test; conversions not yet fully implemented" )
163
+ # @pytest.mark.parametrize("opname", ['add', 'sub', 'mul', 'truediv'])
164
+ # @pytest.mark.parametrize("ltype", type_list)
165
+ # @pytest.mark.parametrize("rtype", type_list)
166
+ def test_binary_op_type_conversions (opname , ltype , rtype , sys_dict ):
167
+ op = getattr (operator , opname )
168
+ leftsys = sys_dict [ltype ]
169
+ rightsys = sys_dict [rtype ]
170
+ expected = \
171
+ conversion_table [type_list .index (ltype )][1 ][type_list .index (rtype )]
172
+
173
+ # Get rid of warnings for InputOutputSystem objects by making a copy
174
+ if isinstance (leftsys , ct .InputOutputSystem ) and leftsys == rightsys :
175
+ rightsys = leftsys .copy ()
176
+
177
+ # Make sure we get the right result
178
+ if expected == 'E' or expected [0 ] == 'x' :
179
+ # Exception expected
180
+ with pytest .raises (TypeError ):
181
+ op (leftsys , rightsys )
182
+ else :
183
+ # Operation should work and return the given type
184
+ result = op (leftsys , rightsys )
185
+
186
+ # Print out what we are testing in case something goes wrong
187
+ assert isinstance (result , type_dict [expected ])
0 commit comments