@@ -247,9 +247,9 @@ def point_to_point(
247247 The initial time for the trajectory (corresponding to x0). If not
248248 specified, its value is taken to be zero.
249249
250- basis : :class:`~control.flat .BasisFamily` object, optional
250+ basis : :class:`~control.flatsys .BasisFamily` object, optional
251251 The basis functions to use for generating the trajectory. If not
252- specified, the :class:`~control.flat .PolyFamily` basis family will be
252+ specified, the :class:`~control.flatsys .PolyFamily` basis family will be
253253 used, with the minimal number of elements required to find a feasible
254254 trajectory (twice the number of system states)
255255
@@ -260,8 +260,8 @@ def point_to_point(
260260 constraints : list of tuples, optional
261261 List of constraints that should hold at each point in the time vector.
262262 Each element of the list should consist of a tuple with first element
263- given by :meth :`scipy.optimize.LinearConstraint` or
264- :meth :`scipy.optimize.NonlinearConstraint` and the remaining
263+ given by :class :`scipy.optimize.LinearConstraint` or
264+ :class :`scipy.optimize.NonlinearConstraint` and the remaining
265265 elements of the tuple are the arguments that would be passed to those
266266 functions. The following tuples are supported:
267267
@@ -280,7 +280,7 @@ def point_to_point(
280280
281281 Returns
282282 -------
283- traj : :class:`~control.flat .SystemTrajectory` object
283+ traj : :class:`~control.flatsys .SystemTrajectory` object
284284 The system trajectory is returned as an object that implements the
285285 `eval()` function, we can be used to compute the value of the state
286286 and input and a given time t.
@@ -372,10 +372,13 @@ def point_to_point(
372372 #
373373 # At this point, we need to solve the equation M alpha = zflag, where M
374374 # is the matrix constrains for initial and final conditions and zflag =
375- # [zflag_T0; zflag_tf]. Since everything is linear, just compute the
376- # least squares solution for now.
375+ # [zflag_T0; zflag_tf].
376+ #
377+ # If there are no constraints, then we just need to solve a linear
378+ # system of equations => use least squares. Otherwise, we have a
379+ # nonlinear optimal control problem with equality constraints => use
380+ # scipy.optimize.minimize().
377381 #
378-
379382
380383 # Look to see if we have costs, constraints, or both
381384 if cost is None and constraints is None :
@@ -410,25 +413,26 @@ def traj_cost(coeffs):
410413 costval += cost (x , u )
411414 return costval
412415
413- # If not cost given, override with magnitude of the coefficients
416+ # If no cost given, override with magnitude of the coefficients
414417 if cost is None :
415418 traj_cost = lambda coeffs : coeffs @ coeffs
416419
417420 # Process the constraints we were given
418421 if constraints is None :
419- constraints = []
422+ traj_constraints = []
420423 elif isinstance (constraints , tuple ):
421- constraints = [constraints ]
424+ traj_constraints = [constraints ]
422425 elif not isinstance (constraints , list ):
423426 raise TypeError ("trajectory constraints must be a list" )
424427
425428 # Process constraints
426- if len (constraints ) > 0 :
429+ minimize_constraints = []
430+ if len (traj_constraints ) > 0 :
427431 # Set up a nonlinear function to evaluate the constraints
428432 def traj_const (coeffs ):
429433 # Evaluate the constraints at the listed time points
430434 values = []
431- for t in timepts :
435+ for i , t in enumerate ( timepts ) :
432436 # Calculate the states and inputs for the flat output
433437 M_t = np .zeros ((flag_tot , basis .N * sys .ninputs ))
434438 flag_off = 0
@@ -439,44 +443,53 @@ def traj_const(coeffs):
439443 range (basis .N ), range (flag_len )):
440444 M_t [flag_off + k , coeff_off + j ] = \
441445 basis .eval_deriv (j , k , t )
442- flag_off += flag_len
443- coeff_off += basis .N
446+ flag_off += flag_len
447+ coeff_off += basis .N
444448
445449 # Compute flag at this time point
446450 zflag = (M_t @ coeffs ).reshape (sys .ninputs , - 1 )
447451
448452 # Find states and inputs at the time points
449- x , u = sys .reverse (zflag )
450-
451- # Evaluate the constraints at this time point
452- for constraint in constraints :
453- values .append (constraint [0 ](x , u ))
454- lb .append (constraint [1 ])
455- ub .append (constraint [2 ])
456- return values
453+ states , inputs = sys .reverse (zflag )
454+
455+ # Evaluate the constraint function along the trajectory
456+ for type , fun , lb , ub in traj_constraints :
457+ if type == sp .optimize .LinearConstraint :
458+ # `fun` is A matrix associated with polytope...
459+ values .append (
460+ np .dot (fun , np .hstack ([states , inputs ])))
461+ elif type == sp .optimize .NonlinearConstraint :
462+ values .append (fun (states , inputs ))
463+ else :
464+ raise TypeError ("unknown constraint type %s" %
465+ constraint [0 ])
466+ return np .array (values ).flatten ()
457467
458468 # Store upper and lower bounds
459- lb , ub = [], [], []
460- for constraint in constraints :
461- lb .append (constraint [1 ])
462- ub .append (constraint [2 ])
469+ const_lb , const_ub = [], []
470+ for t in timepts :
471+ for type , fun , lb , ub in traj_constraints :
472+ const_lb .append (lb )
473+ const_ub .append (ub )
474+ const_lb = np .array (const_lb ).flatten ()
475+ const_ub = np .array (const_ub ).flatten ()
463476
464477 # Store the constraint as a nonlinear constraint
465- constraints = [
466- sp . optimize . NonlinearConstraint ( traj_cost , lb , ub )]
478+ minimize_constraints = [sp . optimize . NonlinearConstraint (
479+ traj_const , const_lb , const_ub )]
467480
468481 # Add initial and terminal constraints
469- constraints += [sp .optimize .LinearConstraint (M , Z , Z )]
482+ minimize_constraints += [sp .optimize .LinearConstraint (M , Z , Z )]
470483
471484 # Process the initial condition
472485 if initial_guess is None :
473486 initial_guess = np .zeros (basis .N * sys .ninputs )
474487 else :
475- raise NotImplementedError ("initial_guess not yet available " )
488+ raise NotImplementedError ("Initial guess not yet implemented. " )
476489
477490 # Find the optimal solution
478491 res = sp .optimize .minimize (
479- traj_cost , initial_guess , constraints = constraints ,
492+ traj_cost , initial_guess , constraints = minimize_constraints ,
480493 ** minimize_kwargs )
481494 if res .success :
482495 alpha = res .x
0 commit comments