Improve markov function, add mimo support, change api to TimeResponseData by KybernetikJo · Pull Request #1022 · python-control/python-control

Conversation

@KybernetikJo

This PR adds

  • MIMO support to the markov function and
  • it uses the TimeResponseData Type.
T = np.linspace(0, 10, 100)
U = np.ones((1, 100))
response = ct.forced_response(ct.tf([1], [1, 0.5], True), T, U)
H = ct.markov(response, 3)

This breaks old user code!
Does not break old user code!

T = np.linspace(0, 10, 100)
U = np.ones((1, 100))
T, Y = ct.forced_response(ct.tf([1], [1, 0.5], True), T, U)
H = ct.markov(Y, U, 3, transpose=False)

@coveralls

Coverage Status

coverage: 94.519% (-0.01%) from 94.533%
when pulling ec38f2e on KybernetikJo:improve_markove_mimo
into 4acc78b on python-control:main.

@coveralls

Coverage Status

coverage: 94.519% (-0.01%) from 94.533%
when pulling 4c1f4ca on KybernetikJo:improve_markove_mimo
into 4acc78b on python-control:main.

@coveralls

Coverage Status

coverage: 94.519% (-0.01%) from 94.533%
when pulling 446a161 on KybernetikJo:improve_markove_mimo
into 4acc78b on python-control:main.

@murrayrm

Rather than breaking old code, it would be nice to do this in a way that preserved prior functionality. So use *args processing to allow both of the following to work:

H = ct.markov(response, 3)
H = ct.markov(Y, U, 3)

It also seems a bit odd (to me) to have the return type be a TimeResponseData object. It true that the Markov parameters are the impulse response, but are people going to be plotting that impulse response as the primary way they interact with the output of the markov command?

@KybernetikJo

Rather than breaking old code, it would be nice to do this in a way that preserved prior functionality. So use *args processing to allow both of the following to work:

H = ct.markov(response, 3)
H = ct.markov(Y, U, 3)

I will update this one.

It also seems a bit odd (to me) to have the return type be a TimeResponseData object. It true that the Markov parameters are the impulse response, but are people going to be plotting that impulse response as the primary way they interact with the output of the markov command?

I do not have strong feelings about that. The idea was to simplify the joint use of markov and era:

impulse_response = ct.markov(response, m=10)
sysd = ct.era(impulse_response, r=4)

Direct access of Markov parameters:

_, H = ct.markov(response, m=10)

I can change the return value to an array. I think it is also better for the old API.
I guess I should also keep the old API for era.

H = ct.markov(Y, U, m=10)
sysd = ct.era(H, r=4)

@coveralls

Coverage Status

coverage: 94.538% (+0.005%) from 94.533%
when pulling 317d261 on KybernetikJo:improve_markove_mimo
into 4acc78b on python-control:main.

@coveralls

Coverage Status

coverage: 94.516% (-0.02%) from 94.533%
when pulling 2b1cd21 on KybernetikJo:improve_markove_mimo
into 4acc78b on python-control:main.

@coveralls

Coverage Status

coverage: 94.516% (-0.02%) from 94.533%
when pulling cee59d1 on KybernetikJo:improve_markove_mimo
into 4acc78b on python-control:main.

@coveralls

Coverage Status

coverage: 94.669% (+0.04%) from 94.629%
when pulling e61ac53 on KybernetikJo:improve_markove_mimo
into da64e0e on python-control:main.

murrayrm

Choose a reason for hiding this comment

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

"""markov(Y, U, [, m])

Calculate the first `m` Markov parameters [D CB CAB ...]
from data

Choose a reason for hiding this comment

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

Should end in a period.

Choose a reason for hiding this comment

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

Done.

if Umat.shape[0] != 1 or Ymat.shape[0] != 1:
raise ControlMIMONotImplemented
# Get the system description
if (len(args) < 1):

Choose a reason for hiding this comment

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

Parenthesis are not needed here. The more standard style convention would be

Choose a reason for hiding this comment

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

Done.

elif (len(args) > 2):
raise ControlArgument("too many positional arguments")
else:
if (len(args) < 2):

Choose a reason for hiding this comment

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

OK to remove parens (here and below).

Choose a reason for hiding this comment

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

Done.

dt : True of float, optional
True indicates discrete time with unspecified sampling time,
positive number is discrete time with specified sampling time.It
can be used to scale the markov parameters in order to match the

Choose a reason for hiding this comment

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

Markov should be capitalized.

Choose a reason for hiding this comment

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

Done.

Comment on lines +447 to +450

dt : True of float, optional
True indicates discrete time with unspecified sampling time,
positive number is discrete time with specified sampling time.It
can be used to scale the markov parameters in order to match the

Choose a reason for hiding this comment

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

In the case that a TimeResponseData object is given, should dt default to the value coming from the time array? You could set the default value of dt to None and then if the input is Y, U then you default to dt=1 and the input is data, you default to data.time[1] - data.time[0] (perhaps with a check to make sure that time points are equally spaced?).

Choose a reason for hiding this comment

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

Done.