|
| 1 | +# type_conversion_test.py - test type conversions |
| 2 | +# RMM, 3 Jan 2021 |
| 3 | +# |
| 4 | +# This set of tests looks at how various classes are converted when using |
| 5 | +# algebraic operations. See GitHub issue #459 for some discussion on what the |
| 6 | +# desired combinations should be. |
| 7 | + |
| 8 | +import control as ct |
| 9 | +import numpy as np |
| 10 | +import operator |
| 11 | +import pytest |
| 12 | + |
| 13 | +@pytest.fixture() |
| 14 | +def sys_dict(): |
| 15 | + sdict = {} |
| 16 | + sdict['ss'] = ct.ss([[-1]], [[1]], [[1]], [[0]]) |
| 17 | + sdict['tf'] = ct.tf([1],[0.5, 1]) |
| 18 | + sdict['frd'] = ct.frd([10+0j, 9 + 1j, 8 + 2j], [1,2,3]) |
| 19 | + sdict['lio'] = ct.LinearIOSystem(ct.ss([[-1]], [[5]], [[5]], [[0]])) |
| 20 | + sdict['ios'] = ct.NonlinearIOSystem( |
| 21 | + sdict['lio']._rhs, sdict['lio']._out, 1, 1, 1) |
| 22 | + sdict['arr'] = np.array([[2.0]]) |
| 23 | + sdict['flt'] = 3. |
| 24 | + return sdict |
| 25 | + |
| 26 | +type_dict = { |
| 27 | + 'ss': ct.StateSpace, 'tf': ct.TransferFunction, |
| 28 | + 'frd': ct.FrequencyResponseData, 'lio': ct.LinearICSystem, |
| 29 | + 'ios': ct.InterconnectedSystem, 'arr': np.ndarray, 'flt': float} |
| 30 | + |
| 31 | +# |
| 32 | +# Table of expected conversions |
| 33 | +# |
| 34 | +# This table describes all of the conversions that are supposed to |
| 35 | +# happen for various system combinations. This is written out this way |
| 36 | +# to make it easy to read, but this is converted below into a list of |
| 37 | +# specific tests that can be iterated over. |
| 38 | +# |
| 39 | +# Items marked as 'E' should generate an exception. |
| 40 | +# |
| 41 | +# Items starting with 'x' currently generate an expected exception but |
| 42 | +# should eventually generate a useful result (when everything is |
| 43 | +# implemented properly). |
| 44 | +# |
| 45 | +# Note 1: some of the entries below are currently converted to to lower level |
| 46 | +# types than needed. In particular, LinearIOSystems should combine with |
| 47 | +# StateSpace and TransferFunctions in a way that preserves I/O system |
| 48 | +# structure when possible. |
| 49 | +# |
| 50 | +# Note 2: eventually the operator entry for this table can be pulled out and |
| 51 | +# tested as a separate parameterized variable (since all operators should |
| 52 | +# return consistent values). |
| 53 | + |
| 54 | +rtype_list = ['ss', 'tf', 'frd', 'lio', 'ios', 'arr', 'flt'] |
| 55 | +conversion_table = [ |
| 56 | + # op left ss tf frd lio ios arr flt |
| 57 | + ('add', 'ss', ['ss', 'ss', 'xrd', 'ss', 'xos', 'ss', 'ss' ]), |
| 58 | + ('add', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]), |
| 59 | + ('add', 'frd', ['xrd', 'xrd', 'frd', 'xrd', 'E', 'xrd', 'xrd']), |
| 60 | + ('add', 'lio', ['xio', 'xio', 'xrd', 'lio', 'ios', 'xio', 'xio']), |
| 61 | + ('add', 'ios', ['xos', 'xos', 'E', 'ios', 'ios', 'xos', 'xos']), |
| 62 | + ('add', 'arr', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'arr']), |
| 63 | + ('add', 'flt', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'flt']), |
| 64 | + |
| 65 | + # op left ss tf frd lio ios arr flt |
| 66 | + ('sub', 'ss', ['ss', 'ss', 'xrd', 'ss', 'xos', 'ss', 'ss' ]), |
| 67 | + ('sub', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]), |
| 68 | + ('sub', 'frd', ['xrd', 'xrd', 'frd', 'xrd', 'E', 'xrd', 'xrd']), |
| 69 | + ('sub', 'lio', ['xio', 'xio', 'xrd', 'lio', 'ios', 'xio', 'xio']), |
| 70 | + ('sub', 'ios', ['xos', 'xio', 'E', 'ios', 'xos' 'xos', 'xos']), |
| 71 | + ('sub', 'arr', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'arr']), |
| 72 | + ('sub', 'flt', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'flt']), |
| 73 | + |
| 74 | + # op left ss tf frd lio ios arr flt |
| 75 | + ('mul', 'ss', ['ss', 'ss', 'xrd', 'xio', 'xos', 'ss', 'ss' ]), |
| 76 | + ('mul', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]), |
| 77 | + ('mul', 'frd', ['xrd', 'xrd', 'frd', 'xrd', 'E', 'xrd', 'frd']), |
| 78 | + ('mul', 'lio', ['xio', 'xio', 'xrd', 'lio', 'ios', 'xio', 'xio']), |
| 79 | + ('mul', 'ios', ['xos', 'xos', 'E', 'ios', 'ios', 'xos', 'xos']), |
| 80 | + ('mul', 'arr', ['ss', 'tf', 'xrd', 'xio', 'xos', 'arr', 'arr']), |
| 81 | + ('mul', 'flt', ['ss', 'tf', 'frd', 'xio', 'xos', 'arr', 'flt']), |
| 82 | + |
| 83 | + # op left ss tf frd lio ios arr flt |
| 84 | + ('truediv', 'ss', ['xs', 'tf', 'xrd', 'xio', 'xos', 'xs', 'xs' ]), |
| 85 | + ('truediv', 'tf', ['tf', 'tf', 'xrd', 'tf', 'xos', 'tf', 'tf' ]), |
| 86 | + ('truediv', 'frd', ['xrd', 'xrd', 'frd', 'xrd', 'E', 'xrd', 'frd']), |
| 87 | + ('truediv', 'lio', ['xio', 'tf', 'xrd', 'xio', 'xio', 'xio', 'xio']), |
| 88 | + ('truediv', 'ios', ['xos', 'xos', 'E', 'xos', 'xos' 'xos', 'xos']), |
| 89 | + ('truediv', 'arr', ['xs', 'tf', 'xrd', 'xio', 'xos', 'arr', 'arr']), |
| 90 | + ('truediv', 'flt', ['xs', 'tf', 'frd', 'xio', 'xos', 'arr', 'flt'])] |
| 91 | + |
| 92 | +# Now create list of the tests we actually want to run |
| 93 | +test_matrix = [] |
| 94 | +for i, (opname, ltype, expected_list) in enumerate(conversion_table): |
| 95 | + for rtype, expected in zip(rtype_list, expected_list): |
| 96 | + # Add this to the list of tests to run |
| 97 | + test_matrix.append([opname, ltype, rtype, expected]) |
| 98 | + |
| 99 | +@pytest.mark.parametrize("opname, ltype, rtype, expected", test_matrix) |
| 100 | +def test_xferfcn_ndarray_precedence(opname, ltype, rtype, expected, sys_dict): |
| 101 | + op = getattr(operator, opname) |
| 102 | + leftsys = sys_dict[ltype] |
| 103 | + rightsys = sys_dict[rtype] |
| 104 | + |
| 105 | + # Get rid of warnings for InputOutputSystem objects by making a copy |
| 106 | + if isinstance(leftsys, ct.InputOutputSystem) and leftsys == rightsys: |
| 107 | + rightsys = leftsys.copy() |
| 108 | + |
| 109 | + # Make sure we get the right result |
| 110 | + if expected == 'E' or expected[0] == 'x': |
| 111 | + # Exception expected |
| 112 | + with pytest.raises(TypeError): |
| 113 | + op(leftsys, rightsys) |
| 114 | + else: |
| 115 | + # Operation should work and return the given type |
| 116 | + result = op(leftsys, rightsys) |
| 117 | + |
| 118 | + # Print out what we are testing in case something goes wrong |
| 119 | + assert isinstance(result, type_dict[expected]) |
0 commit comments