-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
ENH: Addition/multiplication operators for StateSpace systems #7483
Conversation
scipy/signal/ltisys.py
Outdated
return StateSpace(a, b, c, d) | ||
|
||
# A scalar/matrix is really just a static system (A=0, B=0, C=0) | ||
return StateSpace(self.A, self.B, self.C, self.D + other) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this may not always do the right thing or give a sensible exception. You probably have to check what other
is and raise an exception if it's not of the right type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't numpy complain on its own already if other
is something that's not compatible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking about things that work when added to an array, e.g. if other
is a masked array, or a pandas or xarray class. Or even just a higher-dimensional ndarray with a shape that D
broadcasts to.
Anyway, not 100% sure you can write a sensible check, but worth thinking about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, yes broadcasting could cause some really strange bugs here - nice catch! I think
other = np.atleast_2d(other)
if self.D.shape != other.shape:
raise Error
should cover almost all use cases anyways
Looks like a good idea. What did you decide on for the minimal realisations you suggested in #2973 (comment)? |
I wouldn't automatically minreal it. There are many cases where people won't minreal things until the very end such as model reduction and also FEM based modeling. It should at least have an option to not to minreal it . |
So I'm not doing anything in terms of minimal realisations. This seemed like the best approach - especially for a function that will typically not be called directly. |
I've removed some functionality, such as I've kept
So you get back an object array with a system for each entry in the array. Maybe the best would be to strictly only allow multiplication of two Down the road one could introduce a convenient Thoughts? |
Oh you are opening a can of worms 😃 And even then, it is still not watertight thanks to NumPy's broadcasting behavior. |
@befelix you need to set |
Some comments: the class should set Also, the binary operations should be written in a way that they (i) check if they can deal with Made those changes in 520b5cd (and pushed to contributor branch). Was there something else that's still needed for this PR before merging? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scipy/signal/ltisys.py
Outdated
@@ -26,6 +26,7 @@ | |||
# np.linalg.qr fails on some tests with LinAlgError: zgeqrf returns -7 | |||
# use scipy's qr until this is solved | |||
|
|||
import six |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be from scipy._lib.six
, no? (And same for the tests
code presumably.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, fixed.
scipy/signal/ltisys.py
Outdated
H(s) = E1(s) + E2(s), means that applying H(s) to U(s) | ||
is equivalent to applying E1(s) and E2(s) separately and adding up the | ||
result. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this explanation is a tautology which already follows from LTIness of the systems. What is more informative might be mentioning that this is parallel connection behavior.
scipy/signal/ltisys.py
Outdated
# A scalar/matrix is really just a static system (A=0, B=0, C=0) | ||
return StateSpace(self.A, self.B, self.C, self.D + other) | ||
else: | ||
raise ValueError('Other needs to have compatible dimensions.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess users won't know what "other" means if they are not into class definitions in Python.
return NotImplemented | ||
|
||
return (-self).__add__(other) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we are at it and if you agree; Quickly scaling the model with G/6.5
is also possible with __truediv__()
method where you can simply test for float or int as you do for mult and then return self*(1/other)
. You can also explicitly reject __rtruediv__()
with a more meaningful error message.
Disable numpy binops and ufuncs via setting __array_priority__ and __array_ufunc__. Check type(other) in binops, and return NotImplemented if it's unsupported. For any other causes of operation not being supported, raise errors.
Updated with added |
other comments? merge? |
Nothing from me, let's merge Tuesday if nobody else wants to take a look. |
This PR adds addition and multiplication operators for
StateSpace
systems, see also #2973. This allows for things likeThese operations have a frequency-domain interpretation, where adding means the systems operate in parallel and their output is added, while multiplying
s1 * s2
means that the output ofs2
is applied as the input to systems1
.