Skip to content

Implement ERA, change api to TimeResponseData #1024

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 8 commits into from
Aug 6, 2024

Conversation

KybernetikJo
Copy link
Contributor

This PR implements the ERA method. Related to #1002 and #847

  • it uses the TimeResponseData Type.

@coveralls
Copy link

Coverage Status

coverage: 94.21% (-0.3%) from 94.533%
when pulling 28744f8 on KybernetikJo:implement_era
into 4acc78b on python-control:main.

@coveralls
Copy link

Coverage Status

coverage: 94.21% (-0.3%) from 94.533%
when pulling 596e5ed on KybernetikJo:implement_era
into 4acc78b on python-control:main.

@coveralls
Copy link

Coverage Status

coverage: 94.374% (-0.2%) from 94.533%
when pulling 55ac0ed on KybernetikJo:implement_era
into 4acc78b on python-control:main.

@coveralls
Copy link

Coverage Status

coverage: 94.533% (-0.02%) from 94.554%
when pulling 614a080 on KybernetikJo:implement_era
into 7a135d1 on python-control:main.

@KybernetikJo KybernetikJo marked this pull request as ready for review July 7, 2024 17:46
Copy link
Member

@murrayrm murrayrm left a comment

Choose a reason for hiding this comment

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

Overall this looks good. I found a few small things that you might want to update.

Comment on lines 397 to 398
impulse-response data from which the StateSpace model is estimated,
1D or 3D array.
Copy link
Member

Choose a reason for hiding this comment

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

impulse → Impulse (start sentence with a capital letter) and try to fit on one line (if less than 75 characters).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

impulse-response data from which the StateSpace model is estimated,
1D or 3D array.
data : TimeResponseData
impulse-response data from which the StateSpace model is estimated.
Copy link
Member

Choose a reason for hiding this comment

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

impose → Impulse

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 404 to 405
Number of rows in Hankel matrix.
Default is 2*r.
Copy link
Member

Choose a reason for hiding this comment

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

Put on a single line (here and below).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Comment on lines 410 to 414
True indicates discrete time with unspecified sampling time,
positive number is discrete time with specified sampling time.
It can be used to scale the StateSpace model in order to match
the impulse response of this library.
Default values is True.
Copy link
Member

Choose a reason for hiding this comment

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

Can probably have fewer lines by going out to column 75 before wrapping. Default value sentence doesn't need to start on a new line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tried and Done!

Comment on lines 372 to 373
def era(arg, r, m=None, n=None, dt=True, transpose=False):
r"""era(YY, r)
Copy link
Member

Choose a reason for hiding this comment

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

Optional: to be consistent with the longer naming style we are using in python-control, I suggest we call the function eigensys_realization and then define era = eigensys_realization at the bottom of the file (see freqplot.py for some example). We should also (but not here) redefine the other functions in this file (minimal_realization, balanced_realization, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! See #1028

@KybernetikJo KybernetikJo requested a review from murrayrm July 11, 2024 08:57
@coveralls
Copy link

coveralls commented Jul 11, 2024

Coverage Status

coverage: 94.608% (-0.02%) from 94.628%
when pulling 8b93264 on KybernetikJo:implement_era
into 6406868 on python-control:main.

@KybernetikJo
Copy link
Contributor Author

KybernetikJo commented Jul 11, 2024

I get a warning

../python-control/doc/generated/control.era.rst: WARNING: document isn't included in any toctree

I could not exclude the alias name era from autosummary.

__all__=[eigensys_realization, era]

How to exclude era from autosummary?


SOLUTION: The Solution is, to delete the generated folder in doc. Then the sphinx/autosummary does not use old files.

Copy link
Contributor

@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.

some minor suggestions, and a question.

Comment on lines 392 to 393
where `response` is an `TimeResponseData` object, and `YY` is 1D or 3D
array and r is an integer.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
where `response` is an `TimeResponseData` object, and `YY` is 1D or 3D
array and r is an integer.
where `data` is a `TimeResponseData` object, `YY` is a 1D or 3D
array, and r is an integer.

response is more specific than data, perhaps better?

Copy link
Contributor Author

@KybernetikJo KybernetikJo Jul 14, 2024

Choose a reason for hiding this comment

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

response or data? Not sure.

For me, the function time_response_plot which uses data for time_response, is the blueprint.

The plotting functions use data for input.
bode_plot

The response data types use response for input.
NyquistResponseData

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor Author

@KybernetikJo KybernetikJo Jul 15, 2024

Choose a reason for hiding this comment

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

OK, in my PR #1022 I initially used response instead of data. I will go with data for now.

@roryyorke @murrayrm what should we use?

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer response as it's more specific, but the main thing is to be consistent within the ERA docstring(s).

Number of columns in Hankel matrix. Default is 2*r.
dt : True or float, optional
True indicates discrete time with unspecified sampling time,
positive number is discrete time with specified sampling time. It
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
positive number is discrete time with specified sampling time. It
and a positive float means discrete time with the specified sampling time. It

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

True indicates discrete time with unspecified sampling time,
positive number is discrete time with specified sampling time. It
can be used to scale the StateSpace model in order to match the
impulse response of this library. Default is True.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand the idea of "impulse response of this library" - what does library mean here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This library "python-control" uses a unit area impulse "delta = 1/dt" for dt > 0 and a unit impulse "delta = 1" for dt = True.

The dt parameter is used to create a discrete time state space system (A,B,C,D,dt) to match an impulse response generated by control.impulse_response.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

I change the text to " It can be used to scale the StateSpace model in order to match the unit-area impulse response of python-control. "

Is there a better way to explain that behavior. Should we add something to Library conventions in order to explain the unit-area impulse response of python-control?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, thanks, now I understand.

One possibly-not-obvious side-effect of using True is that it has a value of 1, so one gets debatably natural discrete-time behaviour in that case.

The unit-area convention is documented in timeresp.impulse_resp under Notes, I suggest a reference to that.

"""
raise NotImplementedError('This function is not implemented yet.')
def block_hankel_matrix(Y, m, n):
Copy link
Contributor

Choose a reason for hiding this comment

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

suggest making this top-level _block_hankel.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

A reduced order model sys=StateSpace(Ar,Br,Cr,Dr,dt).
S : array
Singular values of Hankel matrix. Can be used to choose a good r
value.
Copy link
Contributor

Choose a reason for hiding this comment

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

given this, is there an alternative interface with a minsigma argument which could be used to choose r based on the singular values instead? (or maybe tol, with minsigma = tol * maxsigma ?)

This probably a bit out-of-scope, and if the user needs this capability it is simple enough to implement in their own code.

Copy link
Contributor Author

@KybernetikJo KybernetikJo Jul 14, 2024

Choose a reason for hiding this comment

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

I have thought about it, but I will not be adding this feature at this time.

I don't know the literature on this topic well enough, but I know that there are papers that study the choice of r.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Postponed. Possible improvement in the future.

# balanced realizations
Sigma_inv = np.diag(1./np.sqrt(S[0:r]))
Ar = Sigma_inv @ Ur.T @ Hl @ Vhr.T @ Sigma_inv
Br = Sigma_inv @ Ur.T @ Hf[:,0:p]*dt
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like the reference here is "Algorithm 1" of the arxiv paper.

Why does dt makes an appearance here? I don't see any mention of sample rate or sample period in the referenced paper. It must be needed since you have dt=0.1 in the spring-damper model unit test -- I guess I'm missing something obvious?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ERA in the paper assumes a unit impulse response "delta = 1". There is no dt in the paper.

However, python control generates a unit-area impulse response "delta = 1/dt". See also: #812, #977.

In other words, the impulse responses of StateSpace(A,B,C,D,dt=True) and StateSpace(A,B,C,D,dt=0.1) differ by a scaling.
The dt parameter in era can be used to create the right StateSpace model.

Alternative:
We could also rescale the output data by Y=Y*dt in order to get the unit impulse response instead of the unit-area impulse response.

Overall, my goal is that impulse_response, era, markov and StateSpace should work well together.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I added a little comment to indicate the intentional use of Br*dt.

Is there a more elegant way to achieve this?

@KybernetikJo KybernetikJo requested a review from roryyorke July 15, 2024 08:07
Copy link
Contributor

@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.

LGTM. If you think it's useful, add a reference to timeresp.impulse_response.

@murrayrm murrayrm merged commit 4c7977b into python-control:main Aug 6, 2024
23 checks passed
@murrayrm murrayrm added this to the 0.10.1 milestone Aug 8, 2024
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.

How to convert finite step response model to state space representation?
4 participants