-
Notifications
You must be signed in to change notification settings - Fork 440
Uniform processing of time response and optimization parameters #1125
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
Uniform processing of time response and optimization parameters #1125
Conversation
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.
Good work! There are still several places where _process_legacy_keyword()
is called (statefbk.py, pzmap.py, ...), but we can change those later.
I marked several small items to correct.
doc/develop.rst
Outdated
----------------- | ||
|
||
As described above, parameter names are generally longer strings that | ||
describe the purpose fo the paramater. Similar to `matplotlib` (e.g., |
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.
describe the purpose fo the paramater. Similar to `matplotlib` (e.g., | |
describe the purpose for the parameter. Similar to `matplotlib` (e.g., |
doc/develop.rst
Outdated
|
||
var = _process_kwargs('param', param, kwargs, aliases) | ||
|
||
where 'param` is the named parameter used in the function signature |
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.
where 'param` is the named parameter used in the function signature | |
where `param` is the named parameter used in the function signature |
control/config.py
Outdated
param = _process_param('param', defval, kwargs, function_aliases) | ||
|
||
If `param` is a variable keyword argument (in `kwargs`), `defval` can | ||
be pssed as either None or the default value to use if `param` is not |
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.
be pssed as either None or the default value to use if `param` is not | |
be passed as either None or the default value to use if `param` is not |
control/config.py
Outdated
defval : object or dict | ||
Default value for the parameter. | ||
kwargs : dict | ||
Dictionary of varaible keyword arguments. |
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.
Dictionary of varaible keyword arguments. | |
Dictionary of variable keyword arguments. |
Raises | ||
------ | ||
TypeError | ||
If multiple keyword aliased are used for the same parameter. |
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.
If multiple keyword aliased are used for the same parameter. | |
If multiple keyword aliases are used for the same parameter. |
control/optimal.py
Outdated
@@ -1016,10 +1018,25 @@ def __init__( | |||
self.states = response.states | |||
|
|||
|
|||
# Parameter and keyword aliases |
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 dictionary is better placed near the top of the file, next to other module-level variables like _optimal_defaults
. The motivation for this practice is clarity: the variable has module-level scope, so it is in-scope for functions defined before it, too. In my experience, it is easier to have all of these variables in the same part of file, just after imports.
control/timeresp.py
Outdated
@@ -738,6 +739,20 @@ def plot(self, *args, **kwargs): | |||
lines[row, col] += cplt.lines[row, col] | |||
return ControlPlot(lines, cplt.axes, cplt.figure) | |||
|
|||
# Dictionary of aliases for time response commands | |||
_timeresp_aliases = { |
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.
Same comment here as for _optimal_aliases
: I recommend to move the definition of _timeresp_aliases
to the top of the file, just after __all__
.
|
||
# Aliases | ||
resp_short = ct.input_output_response(sys, timepts, 1, X0=[1, 1]) | ||
np.testing.assert_allclose(resp_long.states, resp_posn.states) |
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.
np.testing.assert_allclose(resp_long.states, resp_posn.states) | |
np.testing.assert_allclose(resp_short.states, resp_posn.states) |
docstring): | ||
# Found the string, but not in numpydoc form | ||
_warn(f"{funcname}: {argname} docstring missing space") | ||
|
||
elif not (match := re.search( | ||
"\n" + r"((\w+|\.{3}), )*" + argname + r"(, (\w+|\.{3}))* :", | ||
"\n" + r"((\w+|\.{3}), )*" + argname_ + r"(, (\w+|\.{3}))* :", |
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.
There is a search for duplicates below on line 658. Should argname
there also be changed to argname_
?
# Legacy | ||
with pytest.warns(PendingDeprecationWarning, match="legacy"): | ||
resp_legacy = ct.input_output_response(sys, timepts, 1, x0=[1, 1]) | ||
np.testing.assert_allclose(resp_long.states, resp_posn.states) |
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.
np.testing.assert_allclose(resp_long.states, resp_posn.states) | |
np.testing.assert_allclose(resp_legacy.states, resp_posn.states) |
This PR regularizes the use of various keywords for time responses and optimization routines, while maintaining backward compatibility with existing code. The motivation for the PR is that over time we have introduced different terms to refer to parameters that have the same purpose. This can be confusing for people trying to use the package.
For example:
x0
andX0
are used in different places for the initial state in simulation and optimization functions.return_x
is used when a function should return the state as part of a tuple, though sometimereturn_states
is used instead.states
property (notx
) and setting the the state names is done viastates
.u0
,U
,inputs
) and outputs (y0
,outputs
).timepts
but other times asT
.cost
,trajectory_cost
orintegral cost
, depending on the function, and constraints are referred to astrajectory_constraints
in some places asconstraints
in others.t_eval
,yfinal
, etc.To address these issues, this PR introduces a common set of terms across various time response and optimal control functions, with the following more consistent usage patterns and functionality:
inputs
,outputs
, andstates
consistently throughout the functions.initial_state
,final_output
,input_indices
etc.U
,Y
,X0
. Short form aliases are documented in docstrings by listing the parameter aslong_form (or sf) : type
.PendingDeprecationWarning
.TypeError
.The implementation is done in a manner that can later be utilized for other functions where we want to have aliases and legacy keys, as documented in the Developer Notes section of the Reference Manual:
Two primary tables have been created in this PR. The first is a
dictionary of aliases and legacy names for time response commands (from
timeresp.py
):The second is a dictionary of aliases for optimal control functions (from
optimal.py
):