Skip to content

Complex matrices #376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed

Complex matrices #376

wants to merge 12 commits into from

Conversation

lytex
Copy link

@lytex lytex commented Feb 29, 2020

Now I've written new tests that use complex matrices without touching the old ones

def test_evalfr_complex(self):
"""Evaluate the frequency response at one frequency."""

resp = [[4.6799374736968655 -34.9854626345217383j,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be easier on the precision of your reference values. This will almost certainly fail on some build systems due to rounding errors or depending on the used BLAS/LAPACK implementation.

@bnavigator
Copy link
Contributor

https://travis-ci.org/python-control/python-control/jobs/656662515#L1598-L1631

======================================================================
1599ERROR: test_multiply_ss_complex (control.tests.statesp_test.TestStateSpace)
1600Multiply two complex MIMO systems.
1601----------------------------------------------------------------------
1602Traceback (most recent call last):
1603  File "/home/travis/build/python-control/python-control/control/tests/statesp_test.py", line 690, in test_multiply_ss_complex
1604    np.testing.assert_array_almost_equal(sys.A, A)
1605  File "/home/travis/miniconda/envs/test-environment/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 1047, in assert_array_almost_equal
1606    precision=decimal)
1607  File "/home/travis/miniconda/envs/test-environment/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 834, in assert_array_compare
1608    max_rel_error = max(error[nonzero] / abs(y[nonzero]))
1609  File "/home/travis/miniconda/envs/test-environment/lib/python3.7/site-packages/numpy/matrixlib/defmatrix.py", line 195, in __getitem__
1610    out = N.ndarray.__getitem__(self, index)
1611IndexError: boolean index did not match indexed array along dimension 0; dimension is 1 but corresponding boolean dimension is 25
1612
1613======================================================================
1614FAIL: test_pole_complex (control.tests.statesp_test.TestStateSpace)
1615Evaluate the poles of a complex MIMO system.
1616----------------------------------------------------------------------
1617Traceback (most recent call last):
1618  File "/home/travis/build/python-control/python-control/control/tests/statesp_test.py", line 574, in test_pole_complex
1619    np.testing.assert_array_almost_equal(p, true_p)
1620  File "/home/travis/miniconda/envs/test-environment/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 1047, in assert_array_almost_equal
1621    precision=decimal)
1622  File "/home/travis/miniconda/envs/test-environment/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 765, in assert_array_compare
1623    raise AssertionError(msg)
1624AssertionError: 
1625Arrays are not almost equal to 6 decimals
1626
1627(shapes (3,), (2,) mismatch)
1628 x: array([-6.462073+2.870158j,  2.709877+6.291667j, 21.752196+7.838175j])
1629 y: array([ 2.709877 +6.291667j, 15.290123+10.708333j])
1630
1631----------------------------------------------------------------------

@bnavigator
Copy link
Contributor

What happens if you call a function that uses slycot and give it a np.array(data, dtype=complex) (with imaginary part or without)? Will it fail gracefully, or will it give the wrong result without indication?

@lytex
Copy link
Author

lytex commented Mar 3, 2020

I've run statesp_test.py on my local machine, with slycot installed, I'm getting some complex casting warnings, but I don't know if there are more hidden cases failing without indication

slycot/transform.py:560: ComplexWarning: Casting complex values to real discards the imaginary part
  out = _wrapper.tb05ad_ng(n, m, p, jomega, A, B, C)
slycot/analysis.py:470: ComplexWarning: Casting complex values to real discards the imaginary part
  out = _wrapper.ab08nd(n,m,p,A,B,C,D,equil=equil,tol=tol,ldwork=ldwork)
slycot/transform.py:323: ComplexWarning: Casting complex values to real discards the imaginary part
  out = _wrapper.tb04ad_r(n,m,p,A,B,C,D,tol1,tol2,ldwork)

Regarding the second warning, there is a complex routine ab08nz in SLICOT we could use instead of ab08nd, so that should be pretty simple. No clue on the other ones, though.

@lytex
Copy link
Author

lytex commented Mar 3, 2020

I've made a more detailed traceback of the warnings:

======================================================================
ERROR: test_freq_resp_complex (__main__.TestStateSpace)
Evaluate the frequency response at multiple frequencies.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/julian/code/python/python-control/control/tests/statesp_test.py", line 732, in test_freq_resp_complex
    mag, phase, omega = self.sysC623.freqresp(true_omega)
  File "/home/julian/code/python/python-control/control/statesp.py", line 537, in freqresp
    self.B, self.C, job='NG')
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/slycot-0.3.5.21-py3.7-linux-x86_64.egg/slycot/transform.py", line 560, in tb05ad
    out = _wrapper.tb05ad_ng(n, m, p, jomega, A, B, C)
numpy.ComplexWarning: Casting complex values to real discards the imaginary part

======================================================================
ERROR: test_multiply_ss_complex (__main__.TestStateSpace)
Multiply two complex MIMO systems.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/julian/code/python/python-control/control/tests/statesp_test.py", line 692, in test_multiply_ss_complex
    np.testing.assert_array_almost_equal(sys.A, A)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 1047, in assert_array_almost_equal
    precision=decimal)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 834, in assert_array_compare
    max_rel_error = max(error[nonzero] / abs(y[nonzero]))
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/numpy/matrixlib/defmatrix.py", line 195, in __getitem__
    out = N.ndarray.__getitem__(self, index)
IndexError: boolean index did not match indexed array along dimension 0; dimension is 1 but corresponding boolean dimension is 25

======================================================================
ERROR: test_zero_mimo_sysC222_square_complex (__main__.TestStateSpace)
Evaluate the zeros of a square complex MIMO system.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/julian/code/python/python-control/control/tests/statesp_test.py", line 609, in test_zero_mimo_sysC222_square_complex
    z = np.sort(self.sysC222.zero())
  File "/home/julian/code/python/python-control/control/statesp.py", line 584, in zero
    self.A, self.B, self.C, self.D)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/slycot-0.3.5.21-py3.7-linux-x86_64.egg/slycot/analysis.py", line 470, in ab08nd
    out = _wrapper.ab08nd(n,m,p,A,B,C,D,equil=equil,tol=tol,ldwork=ldwork)
numpy.ComplexWarning: Casting complex values to real discards the imaginary part

======================================================================
ERROR: test_zero_mimo_sysC322_square_complex (__main__.TestStateSpace)
Evaluate the zeros of a square complex MIMO system.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/julian/code/python/python-control/control/tests/statesp_test.py", line 599, in test_zero_mimo_sysC322_square_complex
    z = np.sort(self.sysC322.zero())
  File "/home/julian/code/python/python-control/control/statesp.py", line 584, in zero
    self.A, self.B, self.C, self.D)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/slycot-0.3.5.21-py3.7-linux-x86_64.egg/slycot/analysis.py", line 470, in ab08nd
    out = _wrapper.ab08nd(n,m,p,A,B,C,D,equil=equil,tol=tol,ldwork=ldwork)
numpy.ComplexWarning: Casting complex values to real discards the imaginary part

======================================================================
ERROR: test_zero_mimo_sysC623_non_square_complex (__main__.TestStateSpace)
Evaluate the zeros of a non square complex MIMO system.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/julian/code/python/python-control/control/tests/statesp_test.py", line 618, in test_zero_mimo_sysC623_non_square_complex
    z = np.sort(self.sysC623.zero())
  File "/home/julian/code/python/python-control/control/statesp.py", line 584, in zero
    self.A, self.B, self.C, self.D)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/slycot-0.3.5.21-py3.7-linux-x86_64.egg/slycot/analysis.py", line 470, in ab08nd
    out = _wrapper.ab08nd(n,m,p,A,B,C,D,equil=equil,tol=tol,ldwork=ldwork)
numpy.ComplexWarning: Casting complex values to real discards the imaginary part

======================================================================
ERROR: test_zero_siso_complex (__main__.TestStateSpace)
Evaluate the zeros of a complex SISO system.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/julian/code/python/python-control/control/tests/statesp_test.py", line 584, in test_zero_siso_complex
    tfC111 = ss2tf(self.sysC222)
  File "/home/julian/code/python/python-control/control/xferfcn.py", line 1422, in ss2tf
    return _convert_to_transfer_function(sys)
  File "/home/julian/code/python/python-control/control/xferfcn.py", line 1184, in _convert_to_transfer_function
    array(sys.B), array(sys.C), array(sys.D), tol1=0.0)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/slycot-0.3.5.21-py3.7-linux-x86_64.egg/slycot/transform.py", line 323, in tb04ad
    out = _wrapper.tb04ad_r(n,m,p,A,B,C,D,tol1,tol2,ldwork)
numpy.ComplexWarning: Casting complex values to real discards the imaginary part

======================================================================
FAIL: test_pole_complex (__main__.TestStateSpace)
Evaluate the poles of a complex MIMO system.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/julian/code/python/python-control/control/tests/statesp_test.py", line 576, in test_pole_complex
    np.testing.assert_array_almost_equal(p, true_p)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 1047, in assert_array_almost_equal
    precision=decimal)
  File "/home/julian/.virtualenvs/python-control/lib/python3.7/site-packages/numpy/testing/_private/utils.py", line 765, in assert_array_compare
    raise AssertionError(msg)
AssertionError: 
Arrays are not almost equal to 6 decimals

(shapes (3,), (2,) mismatch)
 x: array([-6.462073+2.870158j,  2.709877+6.291667j, 21.752196+7.838175j])
 y: array([ 2.709877 +6.291667j, 15.290123+10.708333j])

Tests failing with ComplexWarning

  • test_freq_resp_complex
  • test_zero_mimo_sysC222_square_complex
  • test_zero_mimo_sysC623_non_square_complex
  • test_zero_siso_complex

Test failing without ComplexWarning

  • test_multiply_ss_complex
  • test_pole_complex

lytex added 4 commits March 3, 2020 05:24
If we compare sys.A against A we get a IndexError (not sure why), but
building a new auxiliary StateSpace seems to do the trick
If we compare sys.A against A we get a IndexError (not sure why), but
building a new auxiliary StateSpace seems to do the trick
@lytex
Copy link
Author

lytex commented Mar 31, 2020

Well, it seems that the current f2py interface doesn't support complex arrays as an argument. I've tried to use the complex routine ab08nz instead of ab08nd but I always get a SEGFAULT

@bnavigator
Copy link
Contributor

So you wrote a wrapper for ab08nz? can you share that as PR in slycot and we see if the CI also segfaults?

@bnavigator
Copy link
Contributor

According to the f2py typespec documentation, f2py signature files understand complex

@lytex
Copy link
Author

lytex commented Apr 1, 2020

Yes f2py does support complex, however I'm not sure if it supports a complex array

I'll do the pull request in a minute

@lytex
Copy link
Author

lytex commented Apr 1, 2020

This is the PR

@lytex
Copy link
Author

lytex commented Apr 1, 2020

So you wrote a wrapper for ab08nz? can you share that as PR in slycot and we see if the CI also segfaults?

Will the CI check a new function that it is just added or there is some config file to declare with functions will be teseted?

@murrayrm
Copy link
Member

murrayrm commented Apr 1, 2020

You need to write unit tests associated with any new functions that you add, otherwise they won't be checked. If you are adding something to slycot, the unit tests should go there, although I think the Travis CI builds for slycot also try running python-control unit tests, since python-control is the primary user of slycot (as far as I know).

@lytex lytex mentioned this pull request Apr 1, 2020
@lytex
Copy link
Author

lytex commented Apr 1, 2020

I've changed control/statesp.py to use the complex-case function ab08nz in the complex branch.

The tests affected by this change should be test_zero_mimi_sysCXXX_square_complex, also new tests written in the complex branch

@bnavigator
Copy link
Contributor

The PR here in python-control will only be able to access the new function in Slycot when your PR over there is merged, a new version is released and the CI in python-control/master is updated to use this new release. So please first amend python-control/Slycot#96 with a unit test, convince the maintainers to merge and wait for a release. The python-control unit tests which are run within Slycot's CI are also from master.

Of course you could also modify the CI scripts here and over at Slycot#96 to use your respective modified branches for development, but that won't be suitable for merging.

Thus I recommend to develop first in Slycot and as soon as that is done continue in python-control.

@lytex
Copy link
Author

lytex commented Apr 3, 2020

I've run statesp_test.py on my local machine, with slycot installed, I'm getting some complex casting warnings, but I don't know if there are more hidden cases failing without indication

slycot/transform.py:560: ComplexWarning: Casting complex values to real discards the imaginary part
  out = _wrapper.tb05ad_ng(n, m, p, jomega, A, B, C)
slycot/analysis.py:470: ComplexWarning: Casting complex values to real discards the imaginary part
  out = _wrapper.ab08nd(n,m,p,A,B,C,D,equil=equil,tol=tol,ldwork=ldwork)
slycot/transform.py:323: ComplexWarning: Casting complex values to real discards the imaginary part
  out = _wrapper.tb04ad_r(n,m,p,A,B,C,D,tol1,tol2,ldwork)

Regarding the second warning, there is a complex routine ab08nz in SLICOT we could use instead of ab08nd, so that should be pretty simple. No clue on the other ones, though.

ab08nd has been successfully changed to ab08nz thanks to @bnavigator
lytex/Slycot#1 and now the test_zero_mimo_sysCXXX_(non_)square_complex test pass.

tb05ad_ng and tb04ad_r are casting complex values to real, but as far as I know it seems there are no complex routine equivalents to these functions

Copy link
Contributor

@bnavigator bnavigator left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So with the new Slycot out, I do not see a reason why python-control should not allow complex arrays. Through the "ComplexWarning", the user is informed that a function discarded the imaginary part and is thus not suitable for complex input (yet).

Comment on lines -96 to +104
arr = np.matrix(data, dtype=float)
if np.isrealobj(data):
arr = np.matrix(data, dtype=float)
elif np.iscomplexobj(data):
arr = np.matrix(data, dtype=complex)
else:
arr = np.array(data, dtype=float)
if np.isrealobj(data):
arr = np.array(data, dtype=float)
elif np.iscomplexobj(data):
arr = np.array(data, dtype=complex)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really necessary? Why not just use matrix() and asarray() without dtype and figure out the dtype later?

Copy link
Contributor

@bnavigator bnavigator Jul 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out if dtype is omitted completely, it could still be int and trip operations later on.

The test suite already has complex casting warnings from Scipy and Slycot functions returning complex arrays (with 0 imaginary part), which are passed to _ssmatrix. Thus, my approach in #438 is to use dtype=np.result_type(np.float, arr), which ensures to cast from int to at least float and retains complex if arr is already complex.

bnavigator added a commit to bnavigator/python-control that referenced this pull request Jul 29, 2020
It is not necessary to enforce float and can raise
ComplexWarning during casting; cf. python-control#376
bnavigator added a commit to bnavigator/python-control that referenced this pull request Jul 29, 2020
@bnavigator bnavigator added this to the 0.9.0 milestone Aug 17, 2020
@bnavigator bnavigator linked an issue Aug 17, 2020 that may be closed by this pull request
bnavigator added a commit to bnavigator/python-control that referenced this pull request Aug 20, 2020
bnavigator added a commit to bnavigator/python-control that referenced this pull request Dec 29, 2020
bnavigator added a commit to bnavigator/python-control that referenced this pull request Dec 29, 2020
bnavigator added a commit to bnavigator/python-control that referenced this pull request Dec 29, 2020
@bnavigator bnavigator added the needs rebase The PR needs a rebase to current master branch label Jan 4, 2021
@murrayrm murrayrm removed this from the 0.9.0 milestone Jan 13, 2021
@lytex lytex closed this Jul 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs rebase The PR needs a rebase to current master branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Complex state matrices
3 participants