Skip to content
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

TB05AD wrapper #10

Merged
merged 1 commit into from
May 30, 2017
Merged

TB05AD wrapper #10

merged 1 commit into from
May 30, 2017

Conversation

rabraker
Copy link
Contributor

This pull request is to address issue 116 in python-control. It provides a wrapper for the TB05AD slicot routine which computes the frequency response of a statespace system. The routine is mostly useful when computing the frequency response at many frequencies.

The wrapper implements three different modalities for TB05AD:
1). You pass in general (A, B, C) matrices. The A matrix is balanced, transformed to upper Hessenberg form, and the B, and C matrices are appropriately transformed. This returns the frequency response as well as the transformed (A, B, C) matrices.
2). The same as (1), except no balancing is performed.
3). You pass matrices which have already been transformed to upper Hessenberg form.

These three different jobs are wrapped as three different f2py functions in transform.pyf. In python, they are all called via the same function in transform.py 'tb05ad' by setting a different job flag. These are 'AG' (All, general), 'NG' (none, general), 'NH' (none, hessernberg) respectively.

Using job 'NG' (2) then job 'NH' (3) works perfectly as far as I can tell.

For certain A matrices, job 'AG' (1) gives the wrong the results. I believe this is a bug in TB05AD.f circa line 354. Above that line, I have commented out my proposed change which I believe fixes the issue. But I wanted somebody else to see it fail as well to make sure I'm not crazy :). When doing the balancing, TB05AD calls the lapack routine DGEBAL. From what I can tell, TB05AD misinterprets the permutation information provided by DGEBAL, but this misinterpretation is only a problem when the permutation corresponds to non-symmetric permutation matrix. This is similar to the scipy matrix_balance bug which I reported here. Before this was fixed, TB05AD and linalg.matrix_balance would yield the same transformed set (A, B, C), but you would get the wrong frequency response.

The failure is documented in the unit tests as a known failure.

I deviated some in writing the unit tests from what is currently implemented. The motivation was to test the three different routines on the same set of matrices while avoiding copy-and-paste coding. I'm sure there is a better way...

Let me know what you think.

Thanks!

Arnold

@slivingston slivingston self-assigned this Apr 18, 2017
@slivingston
Copy link
Member

The email address used in the commits begins with =. Is that intentional? This manifests as GitHub not recognizing the commits as being associated with your GitHub handle. OK to keep the email address this way if you prefer.

@rabraker
Copy link
Contributor Author

No, it was not intentional. There was an error in my .gitconfig file. According to this, I can change the commit email by rebasing. Is that the preferred method?

@roryyorke
Copy link
Collaborator

See @slivingston's take on rebasing in pull requests here. In brief, rebasing in PRs is fine.

You can't depend on python-control in Slycot or its tests; that's why Travis CI is failing.

I'd like to do a more thorough review, but it will take some time.

@rabraker rabraker force-pushed the master branch 2 times, most recently from 8f505a2 to 60d6c2c Compare April 19, 2017 05:52
@rabraker
Copy link
Contributor Author

Thanks for the link.

I didn't know that a tool like travis CL even existed until today. After removing the dependence on python-control and making a couple tweaks to the transform.pyf file, the builds all succeed now.

Copy link
Collaborator

@roryyorke roryyorke left a comment

Choose a reason for hiding this comment

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

Looks good, thank you. I've made a few suggestions, but nothing major.

I haven't looked at your proposed fix to TB05AD.f -- I'll need to study DGEBAL, and try to resurrect what little Fortran knowledge I have to do that.

I think @slivingston would prefer you open a new issue for this SLICOT bug, and remove the "Proposed fix" c.omment in this PR.

@@ -240,3 +240,26 @@ def tb01pd_example():
print('reduced order', out[-2])
print(out)


def tb05ad_example():
import numpy as np
Copy link
Collaborator

Choose a reason for hiding this comment

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

A docstring wouldn't hurt here ("Demonstrate TB05AD with a second-order system with natural frequency 10r/s and damping 1.05").

@@ -135,7 +135,97 @@ subroutine tb04ad_c(rowcol,n,m,p,a,lda,b,ldb,c,ldc,d,ldd,nr,index_bn,dcoeff,lddc
double precision intent(hide,cache),dimension(ldwork) :: dwork
integer optional :: ldwork = max(1,n*(n+1)+max(n*p+2*n+max(n,p),max(3*p,m)))
integer intent(out) :: info
end subroutine tb04ad_c
end subroutine tb04ad_c
Copy link
Collaborator

Choose a reason for hiding this comment

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

assume you didn't mean to indent end subroutine here?

end subroutine tb04ad_c
end subroutine tb04ad_c
subroutine tb05ad_ag(baleig,inita,n,m,p,freq,a,lda,b,ldb,c,ldc,rcond,g,ldg,evre,evim,hinvb,ldhinv,iwork,dwork,ldwork,zwork,lzwork,info)
fortranname tb05ad
Copy link
Collaborator

Choose a reason for hiding this comment

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

A one-line comment (like you have for tb05ad_nh) would be good here.

From the TB05AD docs, 'C' and 'B' (and also 'E', which is the same as 'B'?) are other first letter options. Do you know if these other options are useful?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After reading the documentation again I do think 'C' could be useful. The difference between 'N' and 'C' is that 'C' computes RCOND (an estimate of the reciprocal of the condition number of H), 'N' does not. Here,
H = (freq*I - A)
so if A has modes with zero or very little damping H will become poorly conditioned for frequencies near those modes. So 'C' could be useful in order to throw a warning that the calculation at a particular frequency may be inaccurate if the condition number is really bad. You could then either just throw that point away or use the information to adjust the points you're evaluating at.

I'd be willing to add option 'C' at some point in the future if there is interest in that.

The difference between 'A' and 'B' is that 'A' calculates eigenvalues, and RCOND while 'B' only computes eigenvalues. Both balance matrix A. It is my understanding that these two options are only to be used on the "initial" call to TB05AD. In that sense, I don't think its worth adding a different wrapper just to avoid estimating the condition number in a single call.

integer optional,depend(n) :: lzwork = n*n+2*n
integer intent(out) :: info
end subroutine tb05ad_ag
subroutine tb05ad_ng(baleig,inita,n,m,p,freq,a,lda,b,ldb,c,ldc,rcond,g,ldg,evre,evim,hinvb,ldhinv,iwork,dwork,ldwork,zwork,lzwork,info)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need NG? Aren't AG and NH sufficient?

[Later] oh, I see you have a comment about this in transform.py. Fair enough.


class test_tb05ad(unittest.TestCase):

def setUp(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Omit setUp if it does nothing.

used to transform A1 to Hessenberg form. See docs for lappack
DGEHRD():
http://www.netlib.org/lapack/explore-3.1.1-html/dgehrd.f.html
However, it does not apparently to contain information on P, the
Copy link
Collaborator

Choose a reason for hiding this comment

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

"apparently to contain" =>"apparently contain" ?

e.info = out[-1]
raise e
if out[-1] == 1:
error_text = ("More than 30*"+str(n)+"iterations are required "
Copy link
Collaborator

Choose a reason for hiding this comment

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

why 30*str(n) ? The docs just say 30 iterations.

# Checks done, do computation.
if job == 'AG':
out = _wrapper.tb05ad_ag(n, m, p, jomega, A, B, C)
error_handler(out, arg_list)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is good; it's tidier than the error handling elsewhere in Slycot.

It might be good to turn your error_handler into the constructor for (currently non-existant) SlycotValueError, inherited from ValueError, that handle alls the re-indexing, etc., in one place. However, that's for another PR.

@@ -333,6 +333,254 @@ def tb04ad(n,m,p,A,B,C,D,tol1=0.0,tol2=0.0,ldwork=None):
kdcoef = max(index)+1
return A[:Nr,:Nr],B[:Nr,:m],C[:p,:Nr],Nr,index,dcoeff[:porm,:kdcoef],ucoeff[:porm,:porp,:kdcoef]


def tb05ad(n, m, p, jomega, A, B, C, job='NG'):
"""At, Bt, Ct, g_jw, rcond, ev, hinvb = tb05ad_a(n, m, p, jomega, A, B, C):
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doc string isn't right: what is returned depends on job (which isn't listed as an argument).

I'm not sure if we (Slycot and python-control) have a good convention for different return forms. Numpy appears to just omit the return list in the header (e.g., numpy.linalg.svd) and give details below.

tb05ad_a => tb05ad.

e = ValueError("The shape of A is (" + str(A.shape[0]) + "," +
str(A.shape[1]) + "), but expected (" + str(n) +
"," + str(n) + ")")
e.info = -6
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure it's worth faking the info attribute. You haven't done it for the "Unrecognized job" error at the end (where it's not clear what it would be...).

@rabraker
Copy link
Contributor Author

Thanks for the review. I have quite a few deadlines coming in the next couple of weeks, so it may be a bit before I can get to it though.

@rabraker
Copy link
Contributor Author

rabraker commented May 2, 2017

I believe this have addressed all your comments. I made a few more minor changes to the docstring for tb05ad in transform.py that fixed a few other typos and made the wording between 'job' descriptions more consistent.

I can rebase all of it into a single commit, but I thought having them separated might be useful for the moment.

Copy link
Collaborator

@roryyorke roryyorke left a comment

Choose a reason for hiding this comment

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

Great, thanks for the update. I've got one last nitpick (see below, or wherever github puts the other comments).

Will you file a bug for the issue you found in TB05AD.f ?

@@ -64,6 +64,7 @@ def test_td04ad_static(self):
nr,a,b,c,d = transform.td04ad(rc,nin,nout,index,den,num)



Copy link
Collaborator

Choose a reason for hiding this comment

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

a nitpick: please remove this new newline, then this file won't be in the changeset at all.

@rabraker
Copy link
Contributor Author

I've removed that newline as requested. I should be able to file a bug report for TB05AD.f in the next few days.

@rabraker
Copy link
Contributor Author

A Bug report has been submitted, as requested.

@roryyorke
Copy link
Collaborator

OK, thanks for all the fixes. This looks good to me. @slivingston, what do you think?

@roryyorke
Copy link
Collaborator

Last thing: could you please squash this to one commit? I have push rights now, will merge to master when that's done.

@rabraker
Copy link
Contributor Author

It's all squashed now.

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.

3 participants