Skip to content

Refactored matrix inversions (see #85 and #91) #101

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

Merged
merged 14 commits into from
Dec 31, 2016

Conversation

mp4096
Copy link
Contributor

@mp4096 mp4096 commented Jul 3, 2016

Continuing work on the issue raised in #85 and partially done in #91:

  • Replaced most of the .Is and invs with numpy.linalg.solve or numpy.linalg.lstsq. The remaining inversions which I didn't touch are in yottalab.py. Is it still used?
  • Where applicable, replaced A\B, A\C by solving A\[B, C] and taking views for the corresponding results. This avoids redundant cubic LU decompositions.
  • Added singularity checks before matrix inversions.
  • Added two unit tests for the reachable canonical form.
  • Refactored an old TODO.
  • Fixed a minor typo: compatability -> compatability

I commented out the matrix singularity check in modelsimp.py since it correctly raises an error during testModred() in matlab_test.py. Matrix A22 is indeed numerically singular in this case (second column is something like [[0.0, 3.0e-16]]). Should we change the test case? See comment for bf6c446

@coveralls
Copy link

coveralls commented Jul 3, 2016

Coverage Status

Coverage increased (+0.6%) to 76.569% when pulling c5157aa on mp4096:master into 7b07af3 on python-control:master.

@robertobucher
Copy link
Member

The original "yottalab" project doesn't exixt anymore. It has been
splitted into two different modules:

  • ctrl_repl.py: replacement or implementation of particular functions,
    containing
    • d2c
    • dlqr
    • place (using a wrapper to fortran functions derived from the
      scicoslab project

and

  • ctrl_utils with design utilities like:
    • full_obs - full order observer
    • red_obs - reduced order observer
    • comp_form - state feedback controller+observer in compact form
    • comp_form_i - state feedback controller+observer+integ in compact form
    • set_aw - introduce anti-windup into controller
    • grstep - graphical step response

All the other functions of the original yottalab project have migrated
in the control toolbox.

Best regards

Roberto

BTW: For the Swiss Control Association I've prepared a little
description about Python for control purposes.
The link is
http://robertobucher.dti.supsi.ch/bucherdl/BookPythonForControl.pdf

On 07/03/2016 04:36 PM, Mikhail Pak wrote:

Continuing work on the issue raised in #85
#85 and
partially done in #91
#91:

I commented out the matrix singularity check in |modelsimp.py|
https://github.com/mp4096/python-control/blob/master/control/modelsimp.py#L183
since it correctly raises an error during |testModred()|
https://github.com/python-control/python-control/blob/master/control/tests/matlab_test.py#L364
in |matlab_test.py|. Matrix |A22| is indeed numerically singular in
this case (second column is something like |[[0.0, 3.0e-16]]|). Should
we change the test case?


    You can view, comment on, or merge this pull request online at:

#101

    Commit Summary


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#101, or mute
the thread
https://github.com/notifications/unsubscribe/AH9h_sFMOWaLxPBJUZKNeeahe3w7pTNyks5qR8j_gaJpZM4JD4XJ.

@coveralls
Copy link

coveralls commented Jul 12, 2016

Coverage Status

Coverage increased (+0.6%) to 76.569% when pulling 1fe639c on mp4096:master into 7b07af3 on python-control:master.

@mp4096
Copy link
Contributor Author

mp4096 commented Jul 12, 2016

1fe639c: After some consideration I've also replaced the rank check with both numpy.linalg.matrix_rank and numpy.linalg.det with a single numpy.linalg.matrix_rank check. Determinant is really unsuitable for real-life matrix rank computation.

Case in point:

import numpy as np

A = np.matrix("1.0e-3, 0.0; 0.0, 1.0e-3")
print(np.linalg.det(A))
print(np.linalg.matrix_rank(A))

This change does affect unit tests: Fewer systems are skipped in testAcker.

@coveralls
Copy link

coveralls commented Jul 13, 2016

Coverage Status

Coverage increased (+0.5%) to 76.551% when pulling bf6c446 on mp4096:master into 7b07af3 on python-control:master.

@coveralls
Copy link

coveralls commented Jul 13, 2016

Coverage Status

Coverage increased (+0.5%) to 76.513% when pulling 32323d4 on mp4096:master into 7b07af3 on python-control:master.

@mp4096
Copy link
Contributor Author

mp4096 commented Jul 13, 2016

bf6c446: Added singularity check before matrix inversion for DC matching / residualization. I took the liberty to change the unit test slightly, since it does not really matter anyway (see the docstring, it just calls MATLAB-like methods and does not check the results). Instead of eliminating the 2nd and the 3rd state variable, it now eliminates the 1st and the 2nd.

2c25b73: Adds a unit test to check residualization of unstable systems (should throw a ValueError exception).

32323d4: Made code more pythonic, in this case list comprehensions instead of for-loops. Also call np.linalg.eigvals instead of np.linalg.eig, for we do not need the eigenvector matrix, only the eigenvalues.

Edit: Spelling

@mp4096
Copy link
Contributor Author

mp4096 commented Jul 13, 2016

See https://travis-ci.org/mp4096/python-control/builds/144536541 for CI results with the correct configuration from #102.

@slivingston
Copy link
Member

I will review this tonight.

@coveralls
Copy link

coveralls commented Aug 25, 2016

Coverage Status

Coverage increased (+0.3%) to 76.272% when pulling 020f64c on mp4096:master into 531df81 on python-control:master.

* The performance remains the same.
* The source code is more readable.
Also improved the error message for the singularity check.
* `numpy.linalg.solve` instead of matrix inversion.
* Better controllability matrix rank check.
Also made unity elements of the A, B matrices float -- just in case.
* Changed the unit test in matlab_test.py so that no error is thrown
Mainly list comprehensions instead of for-loops
Faster than converting to a pure Python list.
@coveralls
Copy link

Coverage Status

Coverage increased (+0.5%) to 76.1% when pulling 71fef4b on mp4096:master into 1953399 on python-control:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.5%) to 76.1% when pulling 71fef4b on mp4096:master into 1953399 on python-control:master.

@coveralls
Copy link

coveralls commented Nov 21, 2016

Coverage Status

Coverage increased (+0.5%) to 76.1% when pulling 71fef4b on mp4096:master into 1953399 on python-control:master.

@slivingston
Copy link
Member

For posterity, the "old TODO" that is referenced in the opening post of this pull request is in control/statesp.py at line 127 as of commit c498d3d (current tip of master branch).

@slivingston
Copy link
Member

slivingston commented Dec 30, 2016

The change in your first commit (4d70d00) refactors some code to use map, whereas changes from PR #110 refactor that code using a list comprehension.

I compared the two alternatives using %timeit of Jupyter for matrices that would correspond to LTI systems with 10 state dimensions, 2 inputs, 2 outputs (so, the A matrix has shape (10, 10), B matrix shape (10,2), etc.), and with 100 state dimensions, 20 inputs, 20 outputs, all using matrices from numpy.random.random((r,c)).

The difference in timing performance does not appear significant. However, the case of map was slightly faster (approximately 500 to 900 ns less in duration), so I recommend that we use it.

@slivingston slivingston merged commit 71fef4b into python-control:master Dec 31, 2016
slivingston added a commit that referenced this pull request Dec 31, 2016
#101

Changes are from branch `master` of
https://github.com/mp4096/python-control.git

There was merge conflict in how a for-loop was refactored into
`map` (here) vs. list comprehension (from PR #110).

I compared the two alternatives using %timeit of Jupyter for matrices
that would correspond to LTI systems with 10 state dimensions, 2
inputs, 2 outputs (so, the A matrix has shape (10, 10), B matrix has
shape (10,2), etc.), and with 100 state dimensions, 20 inputs,
20 outputs, all using matrices from numpy.random.random((r,c)).

The difference in timing performance does not appear
significant. However, the case of `map` was slightly faster
(approximately 500 to 900 ns less in duration), so I decided to use
that one to resolve the merge conflict.
@slivingston
Copy link
Member

@mp4096 Thanks for these improvements of numerical reliability.

As future work, I think that it would be interesting to add more test cases that would fail to be detected if numpy.linalg.inv was used instead of numpy.linalg.solve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants