updated optimal constraint handling + unit tests · python-control/python-control@f807b33

@@ -146,6 +146,11 @@ def __init__(

146146

else:

147147

self.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

150155

if isinstance(terminal_constraints, tuple):

151156

self.terminal_constraints = [terminal_constraints]

@@ -154,6 +159,11 @@ def __init__(

154159

else:

155160

self.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):

401411

value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))

402412

elif ctype == opt.NonlinearConstraint:

403413

value.append(fun(states[:, i], inputs[:, i]))

404-

else:

414+

else: # pragma: no cover

415+

# Checked above => we should never get here

405416

raise TypeError(f"unknown constraint type {ctype}")

406417407418

# Evaluate the terminal constraint functions

@@ -413,7 +424,8 @@ def _constraint_function(self, coeffs):

413424

value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))

414425

elif ctype == opt.NonlinearConstraint:

415426

value.append(fun(states[:, i], inputs[:, i]))

416-

else:

427+

else: # pragma: no cover

428+

# Checked above => we should never get here

417429

raise TypeError(f"unknown constraint type {ctype}")

418430419431

# Update statistics

@@ -485,7 +497,8 @@ def _eqconst_function(self, coeffs):

485497

value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))

486498

elif ctype == opt.NonlinearConstraint:

487499

value.append(fun(states[:, i], inputs[:, i]))

488-

else:

500+

else: # pragma: no cover

501+

# Checked above => we should never get here

489502

raise TypeError(f"unknown constraint type {ctype}")

490503491504

# Evaluate the terminal constraint functions

@@ -497,7 +510,8 @@ def _eqconst_function(self, coeffs):

497510

value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))

498511

elif ctype == opt.NonlinearConstraint:

499512

value.append(fun(states[:, i], inputs[:, i]))

500-

else:

513+

else: # pragma: no cover

514+

# Checked above => we should never get here

501515

raise 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

846860

def solve_ocp(

847-

sys, horizon, X0, cost, constraints=[], terminal_cost=None,

861+

sys, horizon, X0, cost, trajectory_constraints=[], terminal_cost=None,

848862

terminal_constraints=[], initial_guess=None, basis=None, squeeze=None,

849863

transpose=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'

947966

return_states = ct.config._get_param(

948967

'optimal', 'return_x', kwargs, return_states, pop=True)

949968950969

# Set up the optimal control problem

951970

ocp = OptimalControlProblem(

952-

sys, horizon, cost, trajectory_constraints=constraints,

971+

sys, horizon, cost, trajectory_constraints=trajectory_constraints,

953972

terminal_cost=terminal_cost, terminal_constraints=terminal_constraints,

954973

initial_guess=initial_guess, basis=basis, log=log, **kwargs)

955974