updated optimal constraint handling + unit tests · python-control/python-control@f807b33
@@ -146,6 +146,11 @@ def __init__(
146146else:
147147self.trajectory_constraints = trajectory_constraints
148148149+# Make sure that we recognize all of the constraint types
150+for ctype, fun, lb, ub in self.trajectory_constraints:
151+if not ctype in [opt.LinearConstraint, opt.NonlinearConstraint]:
152+raise TypeError(f"unknown constraint type {ctype}")
153+149154# Process terminal constraints
150155if isinstance(terminal_constraints, tuple):
151156self.terminal_constraints = [terminal_constraints]
@@ -154,6 +159,11 @@ def __init__(
154159else:
155160self.terminal_constraints = terminal_constraints
156161162+# Make sure that we recognize all of the constraint types
163+for ctype, fun, lb, ub in self.terminal_constraints:
164+if not ctype in [opt.LinearConstraint, opt.NonlinearConstraint]:
165+raise TypeError(f"unknown constraint type {ctype}")
166+157167#
158168# Compute and store constraints
159169#
@@ -401,7 +411,8 @@ def _constraint_function(self, coeffs):
401411value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
402412elif ctype == opt.NonlinearConstraint:
403413value.append(fun(states[:, i], inputs[:, i]))
404-else:
414+else: # pragma: no cover
415+# Checked above => we should never get here
405416raise TypeError(f"unknown constraint type {ctype}")
406417407418# Evaluate the terminal constraint functions
@@ -413,7 +424,8 @@ def _constraint_function(self, coeffs):
413424value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
414425elif ctype == opt.NonlinearConstraint:
415426value.append(fun(states[:, i], inputs[:, i]))
416-else:
427+else: # pragma: no cover
428+# Checked above => we should never get here
417429raise TypeError(f"unknown constraint type {ctype}")
418430419431# Update statistics
@@ -485,7 +497,8 @@ def _eqconst_function(self, coeffs):
485497value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
486498elif ctype == opt.NonlinearConstraint:
487499value.append(fun(states[:, i], inputs[:, i]))
488-else:
500+else: # pragma: no cover
501+# Checked above => we should never get here
489502raise TypeError(f"unknown constraint type {ctype}")
490503491504# Evaluate the terminal constraint functions
@@ -497,7 +510,8 @@ def _eqconst_function(self, coeffs):
497510value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
498511elif ctype == opt.NonlinearConstraint:
499512value.append(fun(states[:, i], inputs[:, i]))
500-else:
513+else: # pragma: no cover
514+# Checked above => we should never get here
501515raise TypeError("unknown constraint type {ctype}")
502516503517# Update statistics
@@ -844,7 +858,7 @@ def __init__(
844858845859# Compute the input for a nonlinear, (constrained) optimal control problem
846860def solve_ocp(
847-sys, horizon, X0, cost, constraints=[], terminal_cost=None,
861+sys, horizon, X0, cost, trajectory_constraints=[], terminal_cost=None,
848862terminal_constraints=[], initial_guess=None, basis=None, squeeze=None,
849863transpose=None, return_states=False, log=False, **kwargs):
850864@@ -865,7 +879,7 @@ def solve_ocp(
865879 Function that returns the integral cost given the current state
866880 and input. Called as `cost(x, u)`.
867881868- constraints : list of tuples, optional
882+ trajectory_constraints : list of tuples, optional
869883 List of constraints that should hold at each point in the time vector.
870884 Each element of the list should consist of a tuple with first element
871885 given by :meth:`scipy.optimize.LinearConstraint` or
@@ -943,13 +957,18 @@ def solve_ocp(
943957 :func:`OptimalControlProblem` for more information.
944958945959 """
960+# Process keyword arguments
961+if trajectory_constraints is None:
962+# Backwards compatibility
963+trajectory_constraints = kwargs.pop('constraints', None)
964+946965# Allow 'return_x` as a synonym for 'return_states'
947966return_states = ct.config._get_param(
948967'optimal', 'return_x', kwargs, return_states, pop=True)
949968950969# Set up the optimal control problem
951970ocp = OptimalControlProblem(
952-sys, horizon, cost, trajectory_constraints=constraints,
971+sys, horizon, cost, trajectory_constraints=trajectory_constraints,
953972terminal_cost=terminal_cost, terminal_constraints=terminal_constraints,
954973initial_guess=initial_guess, basis=basis, log=log, **kwargs)
955974