@@ -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 ])