Uniform processing of time response and optimization parameters by murrayrm · Pull Request #1125 · python-control/python-control
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:
- The named parameters
x0andX0are used in different places for the initial state in simulation and optimization functions. - The parameter
return_xis used when a function should return the state as part of a tuple, though sometimereturn_statesis used instead. - Responses use the
statesproperty (notx) and setting the the state names is done viastates. - Similar issues arise for inputs (
u0,U,inputs) and outputs (y0,outputs). - Time points for simulation and optimization are sometimes referred to as
timeptsbut other times asT. - In the optimization and flatness modules, cost functions are referred to as
cost,trajectory_costorintegral cost, depending on the function, and constraints are referred to astrajectory_constraintsin some places asconstraintsin others. - Other parameters have names that don't seem to fit existing python-control naming patterns:
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:
- Parameters specifying the inputs, outputs, and states are referred to as
inputs,outputs, andstatesconsistently throughout the functions. - Variables associated with inputs, outputs, states and time use those words plus an appropriate modifier:
initial_state,final_output,input_indicesetc. - Aliases are used both to maintain backward compatibility and to allow shorthand descriptions: e.g.,
U,Y,X0. Short form aliases are documented in docstrings by listing the parameter aslong_form (or sf) : type. - Existing legacy keywords are allowed and generate a
PendingDeprecationWarning. - Specifying a parameter value in two different ways (eg, via long form and an alias) generates a
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:
[P]arameter names are generally longer strings that describe the purpose fo the paramater. Similar to
matplotlib(e.g., the use oflwas an alias forlinewidth), some commonly used parameter names can be specified using an “alias” that allows the use of a shorter key.Named parameter and keyword variable aliases are processed using the
config._process_kwargs()andconfig._process_param()functions. These functions allow the specification of a list of aliases and a list of legacy keys for a given named parameter or keyword. To make use of these functions, the_process_kwargs()is first called to update the kwargs variable by replacing aliases with the full key:_process_kwargs(kwargs, aliases)The values for named parameters can then be assigned to a local variable using a call to process_param() of the form:
var = _process_kwargs('param', param, kwargs, aliases)where
paramis the named parameter used in the function signature andvaris the local variable in the function (may also be param, but doesn’t have to be)....
The alias mapping is a dictionary that returns a tuple consisting of valid aliases and legacy aliases:
alias_mapping = { 'argument_name_1', (['alias', ...], ['legacy', ...]), ...}If an alias is present in the dictionary of keywords, it will be used to set the value of the argument. If a legacy keyword is used, a warning is issued.
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):
_timeresp_aliases = {
# param: ([alias, ...], [legacy, ...])
'timepts': (['T'], []),
'inputs': (['U'], ['u']),
'outputs': (['Y'], ['y']),
'initial_state': (['X0'], ['x0']),
'final_output': (['yfinal'], []),
'return_states': (['return_x'], []),
'evaluation_times': (['t_eval'], []),
'timepts_num': (['T_num'], []),
'input_indices': (['input'], []),
'output_indices': (['output'], []),
}
The second is a dictionary of aliases for optimal control functions (from optimal.py):
_optimal_aliases = {
# param: ([alias, ...], [legacy, ...])
'integral_cost': (['trajectory_cost', 'cost'], []),
'initial_state': (['x0', 'X0'], []),
'initial_input': (['u0', 'U0'], []),
'final_state': (['xf'], []),
'final_input': (['uf'], []),
'initial_time': (['T0'], []),
'trajectory_constraints': (['constraints'], []),
'return_states': (['return_x'], []),
}