# -*- coding: future_fstrings -*- # # Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, # Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, # Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, # Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl # # This file is part of acados. # # The 2-Clause BSD License # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE.; # import numpy as np import os from .acados_model import AcadosModel from .utils import get_acados_path, J_to_idx, J_to_idx_slack class AcadosOcpDims: """ Class containing the dimensions of the optimal control problem. """ def __init__(self): self.__nx = None self.__nu = None self.__nz = 0 self.__np = 0 self.__ny = 0 self.__ny_e = 0 self.__ny_0 = 0 self.__nr = 0 self.__nr_e = 0 self.__nh = 0 self.__nh_e = 0 self.__nphi = 0 self.__nphi_e = 0 self.__nbx = 0 self.__nbx_0 = 0 self.__nbx_e = 0 self.__nbu = 0 self.__nsbx = 0 self.__nsbx_e = 0 self.__nsbu = 0 self.__nsh = 0 self.__nsh_e = 0 self.__nsphi = 0 self.__nsphi_e = 0 self.__ns = 0 self.__ns_e = 0 self.__ng = 0 self.__ng_e = 0 self.__nsg = 0 self.__nsg_e = 0 self.__nbxe_0 = None self.__N = None @property def nx(self): """:math:`n_x` - number of states. Type: int; default: None""" return self.__nx @property def nz(self): """:math:`n_z` - number of algebraic variables. Type: int; default: 0""" return self.__nz @property def nu(self): """:math:`n_u` - number of inputs. Type: int; default: None""" return self.__nu @property def np(self): """:math:`n_p` - number of parameters. Type: int; default: 0""" return self.__np @property def ny(self): """:math:`n_y` - number of residuals in Lagrange term. Type: int; default: 0""" return self.__ny @property def ny_0(self): """:math:`n_{y}^0` - number of residuals in Mayer term. Type: int; default: 0""" return self.__ny_0 @property def ny_e(self): """:math:`n_{y}^e` - number of residuals in Mayer term. Type: int; default: 0""" return self.__ny_e @property def nr(self): """:math:`n_{\pi}` - dimension of the image of the inner nonlinear function in positive definite constraints. Type: int; default: 0""" return self.__nr @property def nr_e(self): """:math:`n_{\pi}^e` - dimension of the image of the inner nonlinear function in positive definite constraints. Type: int; default: 0""" return self.__nr_e @property def nh(self): """:math:`n_h` - number of nonlinear constraints. Type: int; default: 0""" return self.__nh @property def nh_e(self): """:math:`n_{h}^e` - number of nonlinear constraints at terminal shooting node N. Type: int; default: 0""" return self.__nh_e @property def nphi(self): """:math:`n_{\phi}` - number of convex-over-nonlinear constraints. Type: int; default: 0""" return self.__nphi @property def nphi_e(self): """:math:`n_{\phi}^e` - number of convex-over-nonlinear constraints at terminal shooting node N. Type: int; default: 0""" return self.__nphi_e @property def nbx(self): """:math:`n_{b_x}` - number of state bounds. Type: int; default: 0""" return self.__nbx @property def nbxe_0(self): """:math:`n_{be_{x0}}` - number of state bounds at initial shooting node that are equalities. Type: int; default: None""" return self.__nbxe_0 @property def nbx_0(self): """:math:`n_{b_{x0}}` - number of state bounds for initial state. Type: int; default: 0""" return self.__nbx_0 @property def nbx_e(self): """:math:`n_{b_x}` - number of state bounds at terminal shooting node N. Type: int; default: 0""" return self.__nbx_e @property def nbu(self): """:math:`n_{b_u}` - number of input bounds. Type: int; default: 0""" return self.__nbu @property def nsbx(self): """:math:`n_{{sb}_x}` - number of soft state bounds. Type: int; default: 0""" return self.__nsbx @property def nsbx_e(self): """:math:`n_{{sb}^e_{x}}` - number of soft state bounds at terminal shooting node N. Type: int; default: 0""" return self.__nsbx_e @property def nsbu(self): """:math:`n_{{sb}_u}` - number of soft input bounds. Type: int; default: 0""" return self.__nsbu @property def nsg(self): """:math:`n_{{sg}}` - number of soft general linear constraints. Type: int; default: 0""" return self.__nsg @property def nsg_e(self): """:math:`n_{{sg}^e}` - number of soft general linear constraints at terminal shooting node N. Type: int; default: 0""" return self.__nsg_e @property def nsh(self): """:math:`n_{{sh}}` - number of soft nonlinear constraints. Type: int; default: 0""" return self.__nsh @property def nsh_e(self): """:math:`n_{{sh}}^e` - number of soft nonlinear constraints at terminal shooting node N. Type: int; default: 0""" return self.__nsh_e @property def nsphi(self): """:math:`n_{{s\phi}}` - number of soft convex-over-nonlinear constraints. Type: int; default: 0""" return self.__nsphi @property def nsphi_e(self): """:math:`n_{{s\phi}^e}` - number of soft convex-over-nonlinear constraints at terminal shooting node N. Type: int; default: 0""" return self.__nsphi_e @property def ns(self): """:math:`n_{s}` - total number of slacks. Type: int; default: 0""" return self.__ns @property def ns_e(self): """:math:`n_{s}^e` - total number of slacks at terminal shooting node N. Type: int; default: 0""" return self.__ns_e @property def ng(self): """:math:`n_{g}` - number of general polytopic constraints. Type: int; default: 0""" return self.__ng @property def ng_e(self): """:math:`n_{g}^e` - number of general polytopic constraints at terminal shooting node N. Type: int; default: 0""" return self.__ng_e @property def N(self): """:math:`N` - prediction horizon. Type: int; default: None""" return self.__N @nx.setter def nx(self, nx): if isinstance(nx, int) and nx > 0: self.__nx = nx else: raise Exception('Invalid nx value, expected positive integer. Exiting.') @nz.setter def nz(self, nz): if isinstance(nz, int) and nz > -1: self.__nz = nz else: raise Exception('Invalid nz value, expected nonnegative integer. Exiting.') @nu.setter def nu(self, nu): if isinstance(nu, int) and nu > -1: self.__nu = nu else: raise Exception('Invalid nu value, expected nonnegative integer. Exiting.') @np.setter def np(self, np): if isinstance(np, int) and np > -1: self.__np = np else: raise Exception('Invalid np value, expected nonnegative integer. Exiting.') @ny_0.setter def ny_0(self, ny_0): if isinstance(ny_0, int) and ny_0 > -1: self.__ny_0 = ny_0 else: raise Exception('Invalid ny_0 value, expected nonnegative integer. Exiting.') @ny.setter def ny(self, ny): if isinstance(ny, int) and ny > -1: self.__ny = ny else: raise Exception('Invalid ny value, expected nonnegative integer. Exiting.') @ny_e.setter def ny_e(self, ny_e): if isinstance(ny_e, int) and ny_e > -1: self.__ny_e = ny_e else: raise Exception('Invalid ny_e value, expected nonnegative integer. Exiting.') @nr.setter def nr(self, nr): if isinstance(nr, int) and nr > -1: self.__nr = nr else: raise Exception('Invalid nr value, expected nonnegative integer. Exiting.') @nr_e.setter def nr_e(self, nr_e): if isinstance(nr_e, int) and nr_e > -1: self.__nr_e = nr_e else: raise Exception('Invalid nr_e value, expected nonnegative integer. Exiting.') @nh.setter def nh(self, nh): if isinstance(nh, int) and nh > -1: self.__nh = nh else: raise Exception('Invalid nh value, expected nonnegative integer. Exiting.') @nh_e.setter def nh_e(self, nh_e): if isinstance(nh_e, int) and nh_e > -1: self.__nh_e = nh_e else: raise Exception('Invalid nh_e value, expected nonnegative integer. Exiting.') @nphi.setter def nphi(self, nphi): if isinstance(nphi, int) and nphi > -1: self.__nphi = nphi else: raise Exception('Invalid nphi value, expected nonnegative integer. Exiting.') @nphi_e.setter def nphi_e(self, nphi_e): if isinstance(nphi_e, int) and nphi_e > -1: self.__nphi_e = nphi_e else: raise Exception('Invalid nphi_e value, expected nonnegative integer. Exiting.') @nbx.setter def nbx(self, nbx): if isinstance(nbx, int) and nbx > -1: self.__nbx = nbx else: raise Exception('Invalid nbx value, expected nonnegative integer. Exiting.') @nbxe_0.setter def nbxe_0(self, nbxe_0): if isinstance(nbxe_0, int) and nbxe_0 > -1: self.__nbxe_0 = nbxe_0 else: raise Exception('Invalid nbxe_0 value, expected nonnegative integer. Exiting.') @nbx_0.setter def nbx_0(self, nbx_0): if isinstance(nbx_0, int) and nbx_0 > -1: self.__nbx_0 = nbx_0 else: raise Exception('Invalid nbx_0 value, expected nonnegative integer. Exiting.') @nbx_e.setter def nbx_e(self, nbx_e): if isinstance(nbx_e, int) and nbx_e > -1: self.__nbx_e = nbx_e else: raise Exception('Invalid nbx_e value, expected nonnegative integer. Exiting.') @nbu.setter def nbu(self, nbu): if isinstance(nbu, int) and nbu > -1: self.__nbu = nbu else: raise Exception('Invalid nbu value, expected nonnegative integer. Exiting.') @nsbx.setter def nsbx(self, nsbx): if isinstance(nsbx, int) and nsbx > -1: self.__nsbx = nsbx else: raise Exception('Invalid nsbx value, expected nonnegative integer. Exiting.') @nsbx_e.setter def nsbx_e(self, nsbx_e): if isinstance(nsbx_e, int) and nsbx_e > -1: self.__nsbx_e = nsbx_e else: raise Exception('Invalid nsbx_e value, expected nonnegative integer. Exiting.') @nsbu.setter def nsbu(self, nsbu): if isinstance(nsbu, int) and nsbu > -1: self.__nsbu = nsbu else: raise Exception('Invalid nsbu value, expected nonnegative integer. Exiting.') @nsg.setter def nsg(self, nsg): if isinstance(nsg, int) and nsg > -1: self.__nsg = nsg else: raise Exception('Invalid nsg value, expected nonnegative integer. Exiting.') @nsg_e.setter def nsg_e(self, nsg_e): if isinstance(nsg_e, int) and nsg_e > -1: self.__nsg_e = nsg_e else: raise Exception('Invalid nsg_e value, expected nonnegative integer. Exiting.') @nsh.setter def nsh(self, nsh): if isinstance(nsh, int) and nsh > -1: self.__nsh = nsh else: raise Exception('Invalid nsh value, expected nonnegative integer. Exiting.') @nsh_e.setter def nsh_e(self, nsh_e): if isinstance(nsh_e, int) and nsh_e > -1: self.__nsh_e = nsh_e else: raise Exception('Invalid nsh_e value, expected nonnegative integer. Exiting.') @nsphi.setter def nsphi(self, nsphi): if isinstance(nsphi, int) and nsphi > -1: self.__nsphi = nsphi else: raise Exception('Invalid nsphi value, expected nonnegative integer. Exiting.') @nsphi_e.setter def nsphi_e(self, nsphi_e): if isinstance(nsphi_e, int) and nsphi_e > -1: self.__nsphi_e = nsphi_e else: raise Exception('Invalid nsphi_e value, expected nonnegative integer. Exiting.') @ns.setter def ns(self, ns): if isinstance(ns, int) and ns > -1: self.__ns = ns else: raise Exception('Invalid ns value, expected nonnegative integer. Exiting.') @ns_e.setter def ns_e(self, ns_e): if isinstance(ns_e, int) and ns_e > -1: self.__ns_e = ns_e else: raise Exception('Invalid ns_e value, expected nonnegative integer. Exiting.') @ng.setter def ng(self, ng): if isinstance(ng, int) and ng > -1: self.__ng = ng else: raise Exception('Invalid ng value, expected nonnegative integer. Exiting.') @ng_e.setter def ng_e(self, ng_e): if isinstance(ng_e, int) and ng_e > -1: self.__ng_e = ng_e else: raise Exception('Invalid ng_e value, expected nonnegative integer. Exiting.') @N.setter def N(self, N): if isinstance(N, int) and N > 0: self.__N = N else: raise Exception('Invalid N value, expected positive integer. Exiting.') def set(self, attr, value): setattr(self, attr, value) class AcadosOcpCost: """ Class containing the numerical data of the cost: In case of LINEAR_LS: stage cost is :math:`l(x,u,z) = || V_x \, x + V_u \, u + V_z \, z - y_\\text{ref}||^2_W`, terminal cost is :math:`m(x) = || V^e_x \, x - y_\\text{ref}^e||^2_{W^e}` In case of NONLINEAR_LS: stage cost is :math:`l(x,u,z) = || y(x,u,z) - y_\\text{ref}||^2_W`, terminal cost is :math:`m(x) = || y^e(x) - y_\\text{ref}^e||^2_{W^e}` """ def __init__(self): # initial stage self.__cost_type_0 = None self.__W_0 = None self.__Vx_0 = None self.__Vu_0 = None self.__Vz_0 = None self.__yref_0 = None self.__cost_ext_fun_type_0 = 'casadi' # Lagrange term self.__cost_type = 'LINEAR_LS' # cost type self.__W = np.zeros((0,0)) self.__Vx = np.zeros((0,0)) self.__Vu = np.zeros((0,0)) self.__Vz = np.zeros((0,0)) self.__yref = np.array([]) self.__Zl = np.array([]) self.__Zu = np.array([]) self.__zl = np.array([]) self.__zu = np.array([]) self.__cost_ext_fun_type = 'casadi' # Mayer term self.__cost_type_e = 'LINEAR_LS' self.__W_e = np.zeros((0,0)) self.__Vx_e = np.zeros((0,0)) self.__yref_e = np.array([]) self.__Zl_e = np.array([]) self.__Zu_e = np.array([]) self.__zl_e = np.array([]) self.__zu_e = np.array([]) self.__cost_ext_fun_type_e = 'casadi' # initial stage @property def cost_type_0(self): """Cost type at initial shooting node (0) -- string in {EXTERNAL, LINEAR_LS, NONLINEAR_LS} or :code:`None`. Default: :code:`None`. .. note:: Cost at initial stage is the same as for intermediate shooting nodes if not set differently explicitly. .. note:: If :py:attr:`cost_type_0` is set to :code:`None` values in :py:attr:`W_0`, :py:attr:`Vx_0`, :py:attr:`Vu_0`, :py:attr:`Vz_0` and :py:attr:`yref_0` are ignored (set to :code:`None`). """ return self.__cost_type_0 @property def W_0(self): """:math:`W_0` - weight matrix at initial shooting node (0). Default: :code:`None`. """ return self.__W_0 @property def Vx_0(self): """:math:`V_x^0` - x matrix coefficient at initial shooting node (0). Default: :code:`None`. """ return self.__Vx_0 @property def Vu_0(self): """:math:`V_u^0` - u matrix coefficient at initial shooting node (0). Default: :code:`None`. """ return self.__Vu_0 @property def Vz_0(self): """:math:`V_z^0` - z matrix coefficient at initial shooting node (0). Default: :code:`None`. """ return self.__Vz_0 @property def yref_0(self): """:math:`y_\\text{ref}^0` - reference at initial shooting node (0). Default: :code:`None`. """ return self.__yref_0 @property def cost_ext_fun_type_0(self): """Type of external function for cost at initial shooting node (0) -- string in {casadi, generic} or :code:`None` Default: :code:'casadi'. .. note:: Cost at initial stage is the same as for intermediate shooting nodes if not set differently explicitly. """ return self.__cost_ext_fun_type_0 @yref_0.setter def yref_0(self, yref_0): if isinstance(yref_0, np.ndarray): self.__yref_0 = yref_0 else: raise Exception('Invalid yref_0 value, expected numpy array. Exiting.') @W_0.setter def W_0(self, W_0): if isinstance(W_0, np.ndarray) and len(W_0.shape) == 2: self.__W_0 = W_0 else: raise Exception('Invalid cost W_0 value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @Vx_0.setter def Vx_0(self, Vx_0): if isinstance(Vx_0, np.ndarray) and len(Vx_0.shape) == 2: self.__Vx_0 = Vx_0 else: raise Exception('Invalid cost Vx_0 value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @Vu_0.setter def Vu_0(self, Vu_0): if isinstance(Vu_0, np.ndarray) and len(Vu_0.shape) == 2: self.__Vu_0 = Vu_0 else: raise Exception('Invalid cost Vu_0 value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @Vz_0.setter def Vz_0(self, Vz_0): if isinstance(Vz_0, np.ndarray) and len(Vz_0.shape) == 2: self.__Vz_0 = Vz_0 else: raise Exception('Invalid cost Vz_0 value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @cost_ext_fun_type_0.setter def cost_ext_fun_type_0(self, cost_ext_fun_type_0): if cost_ext_fun_type_0 in ['casadi', 'generic']: self.__cost_ext_fun_type_0 = cost_ext_fun_type_0 else: raise Exception('Invalid cost_ext_fun_type_0 value, expected numpy array. Exiting.') # Lagrange term @property def cost_type(self): """ Cost type at intermediate shooting nodes (1 to N-1) -- string in {EXTERNAL, LINEAR_LS, NONLINEAR_LS}. Default: 'LINEAR_LS'. """ return self.__cost_type @property def W(self): """:math:`W` - weight matrix at intermediate shooting nodes (1 to N-1). Default: :code:`np.zeros((0,0))`. """ return self.__W @property def Vx(self): """:math:`V_x` - x matrix coefficient at intermediate shooting nodes (1 to N-1). Default: :code:`np.zeros((0,0))`. """ return self.__Vx @property def Vu(self): """:math:`V_u` - u matrix coefficient at intermediate shooting nodes (1 to N-1). Default: :code:`np.zeros((0,0))`. """ return self.__Vu @property def Vz(self): """:math:`V_z` - z matrix coefficient at intermediate shooting nodes (1 to N-1). Default: :code:`np.zeros((0,0))`. """ return self.__Vz @property def yref(self): """:math:`y_\\text{ref}` - reference at intermediate shooting nodes (1 to N-1). Default: :code:`np.array([])`. """ return self.__yref @property def Zl(self): """:math:`Z_l` - diagonal of Hessian wrt lower slack at intermediate shooting nodes (1 to N-1). Default: :code:`np.array([])`. """ return self.__Zl @property def Zu(self): """:math:`Z_u` - diagonal of Hessian wrt upper slack at intermediate shooting nodes (1 to N-1). Default: :code:`np.array([])`. """ return self.__Zu @property def zl(self): """:math:`z_l` - gradient wrt lower slack at intermediate shooting nodes (1 to N-1). Default: :code:`np.array([])`. """ return self.__zl @property def zu(self): """:math:`z_u` - gradient wrt upper slack at intermediate shooting nodes (1 to N-1). Default: :code:`np.array([])`. """ return self.__zu @property def cost_ext_fun_type(self): """Type of external function for cost at intermediate shooting nodes (1 to N-1). -- string in {casadi, generic} Default: :code:'casadi'. """ return self.__cost_ext_fun_type @cost_type.setter def cost_type(self, cost_type): cost_types = ('LINEAR_LS', 'NONLINEAR_LS', 'EXTERNAL') if cost_type in cost_types: self.__cost_type = cost_type else: raise Exception('Invalid cost_type value. Exiting.') @cost_type_0.setter def cost_type_0(self, cost_type_0): cost_types = ('LINEAR_LS', 'NONLINEAR_LS', 'EXTERNAL') if cost_type_0 in cost_types: self.__cost_type_0 = cost_type_0 else: raise Exception('Invalid cost_type_0 value. Exiting.') @W.setter def W(self, W): if isinstance(W, np.ndarray) and len(W.shape) == 2: self.__W = W else: raise Exception('Invalid cost W value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @Vx.setter def Vx(self, Vx): if isinstance(Vx, np.ndarray) and len(Vx.shape) == 2: self.__Vx = Vx else: raise Exception('Invalid cost Vx value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @Vu.setter def Vu(self, Vu): if isinstance(Vu, np.ndarray) and len(Vu.shape) == 2: self.__Vu = Vu else: raise Exception('Invalid cost Vu value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @Vz.setter def Vz(self, Vz): if isinstance(Vz, np.ndarray) and len(Vz.shape) == 2: self.__Vz = Vz else: raise Exception('Invalid cost Vz value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @yref.setter def yref(self, yref): if isinstance(yref, np.ndarray): self.__yref = yref else: raise Exception('Invalid yref value, expected numpy array. Exiting.') @Zl.setter def Zl(self, Zl): if isinstance(Zl, np.ndarray): self.__Zl = Zl else: raise Exception('Invalid Zl value, expected numpy array. Exiting.') @Zu.setter def Zu(self, Zu): if isinstance(Zu, np.ndarray): self.__Zu = Zu else: raise Exception('Invalid Zu value, expected numpy array. Exiting.') @zl.setter def zl(self, zl): if isinstance(zl, np.ndarray): self.__zl = zl else: raise Exception('Invalid zl value, expected numpy array. Exiting.') @zu.setter def zu(self, zu): if isinstance(zu, np.ndarray): self.__zu = zu else: raise Exception('Invalid zu value, expected numpy array. Exiting.') @cost_ext_fun_type.setter def cost_ext_fun_type(self, cost_ext_fun_type): if cost_ext_fun_type in ['casadi', 'generic']: self.__cost_ext_fun_type = cost_ext_fun_type else: raise Exception('Invalid cost_ext_fun_type value, expected numpy array. Exiting.') # Mayer term @property def cost_type_e(self): """ Cost type at terminal shooting node (N) -- string in {EXTERNAL, LINEAR_LS, NONLINEAR_LS}. Default: 'LINEAR_LS'. """ return self.__cost_type_e @property def W_e(self): """:math:`W_e` - weight matrix at terminal shooting node (N). Default: :code:`np.zeros((0,0))`. """ return self.__W_e @property def Vx_e(self): """:math:`V_x^e` - x matrix coefficient for cost at terminal shooting node (N). Default: :code:`np.zeros((0,0))`. """ return self.__Vx_e @property def yref_e(self): """:math:`y_\\text{ref}^e` - cost reference at terminal shooting node (N). Default: :code:`np.array([])`. """ return self.__yref_e @property def Zl_e(self): """:math:`Z_l^e` - diagonal of Hessian wrt lower slack at terminal shooting node (N). Default: :code:`np.array([])`. """ return self.__Zl_e @property def Zu_e(self): """:math:`Z_u^e` - diagonal of Hessian wrt upper slack at terminal shooting node (N). Default: :code:`np.array([])`. """ return self.__Zu_e @property def zl_e(self): """:math:`z_l^e` - gradient wrt lower slack at terminal shooting node (N). Default: :code:`np.array([])`. """ return self.__zl_e @property def zu_e(self): """:math:`z_u^e` - gradient wrt upper slack at terminal shooting node (N). Default: :code:`np.array([])`. """ return self.__zu_e @property def cost_ext_fun_type_e(self): """Type of external function for cost at intermediate shooting nodes (1 to N-1). -- string in {casadi, generic} Default: :code:'casadi'. """ return self.__cost_ext_fun_type_e @cost_type_e.setter def cost_type_e(self, cost_type_e): cost_types = ('LINEAR_LS', 'NONLINEAR_LS', 'EXTERNAL') if cost_type_e in cost_types: self.__cost_type_e = cost_type_e else: raise Exception('Invalid cost_type_e value. Exiting.') @W_e.setter def W_e(self, W_e): if isinstance(W_e, np.ndarray) and len(W_e.shape) == 2: self.__W_e = W_e else: raise Exception('Invalid cost W_e value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @Vx_e.setter def Vx_e(self, Vx_e): if isinstance(Vx_e, np.ndarray) and len(Vx_e.shape) == 2: self.__Vx_e = Vx_e else: raise Exception('Invalid cost Vx_e value. ' \ + 'Should be 2 dimensional numpy array. Exiting.') @yref_e.setter def yref_e(self, yref_e): if isinstance(yref_e, np.ndarray): self.__yref_e = yref_e else: raise Exception('Invalid yref_e value, expected numpy array. Exiting.') @Zl_e.setter def Zl_e(self, Zl_e): if isinstance(Zl_e, np.ndarray): self.__Zl_e = Zl_e else: raise Exception('Invalid Zl_e value, expected numpy array. Exiting.') @Zu_e.setter def Zu_e(self, Zu_e): if isinstance(Zu_e, np.ndarray): self.__Zu_e = Zu_e else: raise Exception('Invalid Zu_e value, expected numpy array. Exiting.') @zl_e.setter def zl_e(self, zl_e): if isinstance(zl_e, np.ndarray): self.__zl_e = zl_e else: raise Exception('Invalid zl_e value, expected numpy array. Exiting.') @zu_e.setter def zu_e(self, zu_e): if isinstance(zu_e, np.ndarray): self.__zu_e = zu_e else: raise Exception('Invalid zu_e value, expected numpy array. Exiting.') @cost_ext_fun_type_e.setter def cost_ext_fun_type_e(self, cost_ext_fun_type_e): if cost_ext_fun_type_e in ['casadi', 'generic']: self.__cost_ext_fun_type_e = cost_ext_fun_type_e else: raise Exception('Invalid cost_ext_fun_type_e value, expected numpy array. Exiting.') def set(self, attr, value): setattr(self, attr, value) def print_J_to_idx_note(): print("NOTE: J* matrix is converted to zero based vector idx* vector, which is returned here.") class AcadosOcpConstraints: """ class containing the description of the constraints """ def __init__(self): self.__constr_type = 'BGH' self.__constr_type_e = 'BGH' # initial x self.__lbx_0 = np.array([]) self.__ubx_0 = np.array([]) self.__idxbx_0 = np.array([]) self.__idxbxe_0 = np.array([]) # state bounds self.__lbx = np.array([]) self.__ubx = np.array([]) self.__idxbx = np.array([]) # bounds on x at shooting node N self.__lbx_e = np.array([]) self.__ubx_e = np.array([]) self.__idxbx_e = np.array([]) # bounds on u self.__lbu = np.array([]) self.__ubu = np.array([]) self.__idxbu = np.array([]) # polytopic constraints self.__lg = np.array([]) self.__ug = np.array([]) self.__D = np.zeros((0,0)) self.__C = np.zeros((0,0)) # polytopic constraints at shooting node N self.__C_e = np.zeros((0,0)) self.__lg_e = np.array([]) self.__ug_e = np.array([]) # nonlinear constraints self.__lh = np.array([]) self.__uh = np.array([]) # nonlinear constraints at shooting node N self.__uh_e = np.array([]) self.__lh_e = np.array([]) # convex-over-nonlinear constraints self.__lphi = np.array([]) self.__uphi = np.array([]) # nonlinear constraints at shooting node N self.__uphi_e = np.array([]) self.__lphi_e = np.array([]) # SLACK BOUNDS # soft bounds on x self.__lsbx = np.array([]) self.__usbx = np.array([]) self.__idxsbx = np.array([]) # soft bounds on u self.__lsbu = np.array([]) self.__usbu = np.array([]) self.__idxsbu = np.array([]) # soft bounds on x at shooting node N self.__lsbx_e = np.array([]) self.__usbx_e = np.array([]) self.__idxsbx_e= np.array([]) # soft bounds on general linear constraints self.__lsg = np.array([]) self.__usg = np.array([]) self.__idxsg = np.array([]) # soft bounds on nonlinear constraints self.__lsh = np.array([]) self.__ush = np.array([]) self.__idxsh = np.array([]) # soft bounds on nonlinear constraints self.__lsphi = np.array([]) self.__usphi = np.array([]) self.__idxsphi = np.array([]) # soft bounds on general linear constraints at shooting node N self.__lsg_e = np.array([]) self.__usg_e = np.array([]) self.__idxsg_e = np.array([]) # soft bounds on nonlinear constraints at shooting node N self.__lsh_e = np.array([]) self.__ush_e = np.array([]) self.__idxsh_e = np.array([]) # soft bounds on nonlinear constraints at shooting node N self.__lsphi_e = np.array([]) self.__usphi_e = np.array([]) self.__idxsphi_e = np.array([]) # types @property def constr_type(self): """Constraints type for shooting nodes (0 to N-1). string in {BGH, BGP}. Default: BGH; BGP is for convex over nonlinear.""" return self.__constr_type @property def constr_type_e(self): """Constraints type for terminal shooting node N. string in {BGH, BGP}. Default: BGH; BGP is for convex over nonlinear.""" return self.__constr_type_e # initial bounds on x @property def lbx_0(self): """:math:`\\underline{x_0}` - lower bounds on x at initial stage 0. Type: :code:`np.ndarray`; default: :code:`np.array([])`.""" return self.__lbx_0 @property def ubx_0(self): """:math:`\\bar{x_0}` - upper bounds on x at initial stage 0. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__ubx_0 @property def Jbx_0(self): """:math:`J_{bx,0}` - matrix coefficient for bounds on x at initial stage 0. Translated internally to :py:attr:`idxbx_0`""" print_J_to_idx_note() return self.__idxbx_0 @property def idxbx_0(self): """Indices of bounds on x at initial stage 0 -- can be set automatically via x0. Can be set by using :py:attr:`Jbx_0`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxbx_0 @property def idxbxe_0(self): """Indices of bounds on x0 that are equalities -- can be set automatically via :py:attr:`x0`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxbxe_0 # bounds on x @property def lbx(self): """:math:`\\underline{x}` - lower bounds on x at intermediate shooting nodes (1 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__lbx @property def ubx(self): """:math:`\\bar{x}` - upper bounds on x at intermediate shooting nodes (1 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__ubx @property def idxbx(self): """indices of bounds on x (defines :math:`J_{bx}`) at intermediate shooting nodes (1 to N-1). Can be set by using :py:attr:`Jbx`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxbx @property def Jbx(self): """:math:`J_{bx}` - matrix coefficient for bounds on x at intermediate shooting nodes (1 to N-1). Translated internally into :py:attr:`idxbx`.""" print_J_to_idx_note() return self.__idxbx # bounds on x at shooting node N @property def lbx_e(self): """:math:`\\underline{x}^e` - lower bounds on x at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__lbx_e @property def ubx_e(self): """:math:`\\bar{x}^e` - upper bounds on x at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__ubx_e @property def idxbx_e(self): """Indices for bounds on x at terminal shooting node N (defines :math:`J_{bx}^e`). Can be set by using :py:attr:`Jbx_e`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxbx_e @property def Jbx_e(self): """:math:`J_{bx}^e` matrix coefficient for bounds on x at terminal shooting node N. Translated internally into :py:attr:`idxbx_e`.""" print_J_to_idx_note() return self.__idxbx_e # bounds on u @property def lbu(self): """:math:`\\underline{u}` - lower bounds on u at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])` """ return self.__lbu @property def ubu(self): """:math:`\\bar{u}` - upper bounds on u at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])` """ return self.__ubu @property def idxbu(self): """Indices of bounds on u (defines :math:`J_{bu}`) at shooting nodes (0 to N-1). Can be set by using :py:attr:`Jbu`. Type: :code:`np.ndarray`; default: :code:`np.array([])` """ return self.__idxbu @property def Jbu(self): """:math:`J_{bu}` - matrix coefficient for bounds on u at shooting nodes (0 to N-1). Translated internally to :py:attr:`idxbu`. """ print_J_to_idx_note() return self.__idxbu # polytopic constraints @property def C(self): """:math:`C` - C matrix in :math:`\\underline{g} \\leq D \, u + C \, x \\leq \\bar{g}` at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array((0,0))`. """ return self.__C @property def D(self): """:math:`D` - D matrix in :math:`\\underline{g} \\leq D \, u + C \, x \\leq \\bar{g}` at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array((0,0))` """ return self.__D @property def lg(self): """:math:`\\underline{g}` - lower bound for general polytopic inequalities at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])` """ return self.__lg @property def ug(self): """:math:`\\bar{g}` - upper bound for general polytopic inequalities at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__ug # polytopic constraints at shooting node N @property def C_e(self): """:math:`C^e` - C matrix at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array((0,0))`. """ return self.__C_e @property def lg_e(self): """:math:`\\underline{g}^e` - lower bound on general polytopic inequalities at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__lg_e @property def ug_e(self): """:math:`\\bar{g}^e` - upper bound on general polytopic inequalities at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__ug_e # nonlinear constraints @property def lh(self): """:math:`\\underline{h}` - lower bound for nonlinear inequalities at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__lh @property def uh(self): """:math:`\\bar{h}` - upper bound for nonlinear inequalities at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__uh # nonlinear constraints at shooting node N @property def lh_e(self): """:math:`\\underline{h}^e` - lower bound on nonlinear inequalities at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__lh_e @property def uh_e(self): """:math:`\\bar{h}^e` - upper bound on nonlinear inequalities at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__uh_e # convex-over-nonlinear constraints @property def lphi(self): """:math:`\\underline{\phi}` - lower bound for convex-over-nonlinear inequalities at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__lphi @property def uphi(self): """:math:`\\bar{\phi}` - upper bound for convex-over-nonlinear inequalities at shooting nodes (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__uphi # convex-over-nonlinear constraints at shooting node N @property def lphi_e(self): """:math:`\\underline{\phi}^e` - lower bound on convex-over-nonlinear inequalities at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__lphi_e @property def uphi_e(self): """:math:`\\bar{\phi}^e` - upper bound on convex-over-nonlinear inequalities at terminal shooting node N. Type: :code:`np.ndarray`; default: :code:`np.array([])`. """ return self.__uphi_e # SLACK bounds # soft bounds on x @property def lsbx(self): """Lower bounds on slacks corresponding to soft lower bounds on x at stages (1 to N-1); not required - zeros by default""" return self.__lsbx @property def usbx(self): """Lower bounds on slacks corresponding to soft upper bounds on x at stages (1 to N-1); not required - zeros by default""" return self.__usbx @property def idxsbx(self): """Indices of soft bounds on x within the indices of bounds on x at stages (1 to N-1). Can be set by using :py:attr:`Jsbx`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxsbx @property def Jsbx(self): """:math:`J_{sbx}` - matrix coefficient for soft bounds on x at stages (1 to N-1); Translated internally into :py:attr:`idxsbx`.""" print_J_to_idx_note() return self.__idxsbx # soft bounds on u @property def lsbu(self): """Lower bounds on slacks corresponding to soft lower bounds on u at stages (0 to N-1). Not required - zeros by default.""" return self.__lsbu @property def usbu(self): """Lower bounds on slacks corresponding to soft upper bounds on u at stages (0 to N-1); not required - zeros by default""" return self.__usbu @property def idxsbu(self): """Indices of soft bounds on u within the indices of bounds on u at stages (0 to N-1). Can be set by using :py:attr:`Jsbu`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxsbu @property def Jsbu(self): """:math:`J_{sbu}` - matrix coefficient for soft bounds on u at stages (0 to N-1); internally translated into :py:attr:`idxsbu`""" print_J_to_idx_note() return self.__idxsbu # soft bounds on x at shooting node N @property def lsbx_e(self): """Lower bounds on slacks corresponding to soft lower bounds on x at shooting node N. Not required - zeros by default""" return self.__lsbx_e @property def usbx_e(self): """Lower bounds on slacks corresponding to soft upper bounds on x at shooting node N. Not required - zeros by default""" return self.__usbx_e @property def idxsbx_e(self): """Indices of soft bounds on x at shooting node N, within the indices of bounds on x at terminal shooting node N. Can be set by using :py:attr:`Jsbx_e`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxsbx_e @property def Jsbx_e(self): """:math:`J_{sbx}^e` - matrix coefficient for soft bounds on x at terminal shooting node N. Translated internally to :py:attr:`idxsbx_e`""" print_J_to_idx_note() return self.__idxsbx_e # soft general linear constraints @property def lsg(self): """Lower bounds on slacks corresponding to soft lower bounds for general linear constraints at stages (0 to N-1). Type: :code:`np.ndarray`; default: :code:`np.array([])` """ return self.__lsg @property def usg(self): """Lower bounds on slacks corresponding to soft upper bounds for general linear constraints. Not required - zeros by default""" return self.__usg @property def idxsg(self): """Indices of soft general linear constraints within the indices of general linear constraints. Can be set by using :py:attr:`Jsg`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxsg @property def Jsg(self): """:math:`J_{sg}` - matrix coefficient for soft bounds on general linear constraints. Translated internally to :py:attr:`idxsg`""" print_J_to_idx_note() return self.__idxsg # soft nonlinear constraints @property def lsh(self): """Lower bounds on slacks corresponding to soft lower bounds for nonlinear constraints. Not required - zeros by default""" return self.__lsh @property def ush(self): """Lower bounds on slacks corresponding to soft upper bounds for nonlinear constraints. Not required - zeros by default""" return self.__ush @property def idxsh(self): """Indices of soft nonlinear constraints within the indices of nonlinear constraints. Can be set by using :py:attr:`Jbx`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxsh @property def Jsh(self): """:math:`J_{sh}` - matrix coefficient for soft bounds on nonlinear constraints. Translated internally to :py:attr:`idxsh`""" print_J_to_idx_note() return self.__idxsh # soft bounds on convex-over-nonlinear constraints @property def lsphi(self): """Lower bounds on slacks corresponding to soft lower bounds for convex-over-nonlinear constraints. Not required - zeros by default""" return self.__lsphi @property def usphi(self): """Lower bounds on slacks corresponding to soft upper bounds for convex-over-nonlinear constraints. Not required - zeros by default""" return self.__usphi @property def idxsphi(self): """Indices of soft convex-over-nonlinear constraints within the indices of nonlinear constraints. Can be set by using :py:attr:`Jsphi`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxsphi @property def Jsphi(self): """:math:`J_{s, \phi}` - matrix coefficient for soft bounds on convex-over-nonlinear constraints. Translated internally into :py:attr:`idxsphi`.""" print_J_to_idx_note() return self.__idxsphi # soft bounds on general linear constraints at shooting node N @property def lsg_e(self): """Lower bounds on slacks corresponding to soft lower bounds for general linear constraints at shooting node N. Not required - zeros by default""" return self.__lsg_e @property def usg_e(self): """Lower bounds on slacks corresponding to soft upper bounds for general linear constraints at shooting node N. Not required - zeros by default""" return self.__usg_e @property def idxsg_e(self): """Indices of soft general linear constraints at shooting node N within the indices of general linear constraints at shooting node N. Can be set by using :py:attr:`Jsg_e`.""" return self.__idxsg_e @property def Jsg_e(self): """:math:`J_{s,h}^e` - matrix coefficient for soft bounds on general linear constraints at terminal shooting node N. Translated internally to :py:attr:`idxsg_e`""" print_J_to_idx_note() return self.__idxsg_e # soft bounds on nonlinear constraints at shooting node N @property def lsh_e(self): """Lower bounds on slacks corresponding to soft lower bounds for nonlinear constraints at terminal shooting node N. Not required - zeros by default""" return self.__lsh_e @property def ush_e(self): """Lower bounds on slacks corresponding to soft upper bounds for nonlinear constraints at terminal shooting node N. Not required - zeros by default""" return self.__ush_e @property def idxsh_e(self): """Indices of soft nonlinear constraints at shooting node N within the indices of nonlinear constraints at terminal shooting node N. Can be set by using :py:attr:`Jsh_e`.""" return self.__idxsh_e @property def Jsh_e(self): """:math:`J_{s,h}^e` - matrix coefficient for soft bounds on nonlinear constraints at terminal shooting node N; fills :py:attr:`idxsh_e`""" print_J_to_idx_note() return self.__idxsh_e # soft bounds on convex-over-nonlinear constraints at shooting node N @property def lsphi_e(self): """Lower bounds on slacks corresponding to soft lower bounds for convex-over-nonlinear constraints at terminal shooting node N. Not required - zeros by default""" return self.__lsphi_e @property def usphi_e(self): """Lower bounds on slacks corresponding to soft upper bounds for convex-over-nonlinear constraints at terminal shooting node N. Not required - zeros by default""" return self.__usphi_e @property def idxsphi_e(self): """Indices of soft nonlinear constraints at shooting node N within the indices of nonlinear constraints at terminal shooting node N. Can be set by using :py:attr:`Jsphi_e`. Type: :code:`np.ndarray`; default: :code:`np.array([])`""" return self.__idxsphi_e @property def Jsphi_e(self): """:math:`J_{sh}^e` - matrix coefficient for soft bounds on convex-over-nonlinear constraints at shooting node N. Translated internally to :py:attr:`idxsphi_e`""" print_J_to_idx_note() return self.__idxsphi_e @property def x0(self): """:math:`x_0 \\in \mathbb{R}^{n_x}` - initial state -- Translated internally to :py:attr:`idxbx_0`, :py:attr:`lbx_0`, :py:attr:`ubx_0`, :py:attr:`idxbxe_0` """ print("x0 is converted to lbx_0, ubx_0, idxbx_0") print("idxbx_0: ", self.__idxbx_0) print("lbx_0: ", self.__lbx_0) print("ubx_0: ", self.__ubx_0) print("idxbxe_0: ", self.__idxbxe_0) return None # SETTERS @constr_type.setter def constr_type(self, constr_type): constr_types = ('BGH', 'BGP') if constr_type in constr_types: self.__constr_type = constr_type else: raise Exception('Invalid constr_type value. Possible values are:\n\n' \ + ',\n'.join(constr_types) + '.\n\nYou have: ' + constr_type + '.\n\nExiting.') @constr_type_e.setter def constr_type_e(self, constr_type_e): constr_types = ('BGH', 'BGP') if constr_type_e in constr_types: self.__constr_type_e = constr_type_e else: raise Exception('Invalid constr_type_e value. Possible values are:\n\n' \ + ',\n'.join(constr_types) + '.\n\nYou have: ' + constr_type_e + '.\n\nExiting.') # initial x @lbx_0.setter def lbx_0(self, lbx_0): if isinstance(lbx_0, np.ndarray): self.__lbx_0 = lbx_0 else: raise Exception('Invalid lbx_0 value. Exiting.') @ubx_0.setter def ubx_0(self, ubx_0): if isinstance(ubx_0, np.ndarray): self.__ubx_0 = ubx_0 else: raise Exception('Invalid ubx_0 value. Exiting.') @idxbx_0.setter def idxbx_0(self, idxbx_0): if isinstance(idxbx_0, np.ndarray): self.__idxbx_0 = idxbx_0 else: raise Exception('Invalid idxbx_0 value. Exiting.') @Jbx_0.setter def Jbx_0(self, Jbx_0): if isinstance(Jbx_0, np.ndarray): self.__idxbx_0 = J_to_idx(Jbx_0) else: raise Exception('Invalid Jbx_0 value. Exiting.') @idxbxe_0.setter def idxbxe_0(self, idxbxe_0): if isinstance(idxbxe_0, np.ndarray): self.__idxbxe_0 = idxbxe_0 else: raise Exception('Invalid idxbxe_0 value. Exiting.') @x0.setter def x0(self, x0): if isinstance(x0, np.ndarray): self.__lbx_0 = x0 self.__ubx_0 = x0 self.__idxbx_0 = np.arange(x0.size) self.__idxbxe_0 = np.arange(x0.size) else: raise Exception('Invalid x0 value. Exiting.') # bounds on x @lbx.setter def lbx(self, lbx): if isinstance(lbx, np.ndarray): self.__lbx = lbx else: raise Exception('Invalid lbx value. Exiting.') @ubx.setter def ubx(self, ubx): if isinstance(ubx, np.ndarray): self.__ubx = ubx else: raise Exception('Invalid ubx value. Exiting.') @idxbx.setter def idxbx(self, idxbx): if isinstance(idxbx, np.ndarray): self.__idxbx = idxbx else: raise Exception('Invalid idxbx value. Exiting.') @Jbx.setter def Jbx(self, Jbx): if isinstance(Jbx, np.ndarray): self.__idxbx = J_to_idx(Jbx) else: raise Exception('Invalid Jbx value. Exiting.') # bounds on u @lbu.setter def lbu(self, lbu): if isinstance(lbu, np.ndarray): self.__lbu = lbu else: raise Exception('Invalid lbu value. Exiting.') @ubu.setter def ubu(self, ubu): if isinstance(ubu, np.ndarray): self.__ubu = ubu else: raise Exception('Invalid ubu value. Exiting.') @idxbu.setter def idxbu(self, idxbu): if isinstance(idxbu, np.ndarray): self.__idxbu = idxbu else: raise Exception('Invalid idxbu value. Exiting.') @Jbu.setter def Jbu(self, Jbu): if isinstance(Jbu, np.ndarray): self.__idxbu = J_to_idx(Jbu) else: raise Exception('Invalid Jbu value. Exiting.') # bounds on x at shooting node N @lbx_e.setter def lbx_e(self, lbx_e): if isinstance(lbx_e, np.ndarray): self.__lbx_e = lbx_e else: raise Exception('Invalid lbx_e value. Exiting.') @ubx_e.setter def ubx_e(self, ubx_e): if isinstance(ubx_e, np.ndarray): self.__ubx_e = ubx_e else: raise Exception('Invalid ubx_e value. Exiting.') @idxbx_e.setter def idxbx_e(self, idxbx_e): if isinstance(idxbx_e, np.ndarray): self.__idxbx_e = idxbx_e else: raise Exception('Invalid idxbx_e value. Exiting.') @Jbx_e.setter def Jbx_e(self, Jbx_e): if isinstance(Jbx_e, np.ndarray): self.__idxbx_e = J_to_idx(Jbx_e) else: raise Exception('Invalid Jbx_e value. Exiting.') # polytopic constraints @D.setter def D(self, D): if isinstance(D, np.ndarray) and len(D.shape) == 2: self.__D = D else: raise Exception('Invalid constraint D value.' \ + 'Should be 2 dimensional numpy array. Exiting.') @C.setter def C(self, C): if isinstance(C, np.ndarray) and len(C.shape) == 2: self.__C = C else: raise Exception('Invalid constraint C value.' \ + 'Should be 2 dimensional numpy array. Exiting.') @lg.setter def lg(self, lg): if isinstance(lg, np.ndarray): self.__lg = lg else: raise Exception('Invalid lg value. Exiting.') @ug.setter def ug(self, ug): if isinstance(ug, np.ndarray): self.__ug = ug else: raise Exception('Invalid ug value. Exiting.') # polytopic constraints at shooting node N @C_e.setter def C_e(self, C_e): if isinstance(C_e, np.ndarray) and len(C_e.shape) == 2: self.__C_e = C_e else: raise Exception('Invalid constraint C_e value.' \ + 'Should be 2 dimensional numpy array. Exiting.') @lg_e.setter def lg_e(self, lg_e): if isinstance(lg_e, np.ndarray): self.__lg_e = lg_e else: raise Exception('Invalid lg_e value. Exiting.') @ug_e.setter def ug_e(self, ug_e): if isinstance(ug_e, np.ndarray): self.__ug_e = ug_e else: raise Exception('Invalid ug_e value. Exiting.') # nonlinear constraints @lh.setter def lh(self, lh): if isinstance(lh, np.ndarray): self.__lh = lh else: raise Exception('Invalid lh value. Exiting.') @uh.setter def uh(self, uh): if isinstance(uh, np.ndarray): self.__uh = uh else: raise Exception('Invalid uh value. Exiting.') # convex-over-nonlinear constraints @lphi.setter def lphi(self, lphi): if isinstance(lphi, np.ndarray): self.__lphi = lphi else: raise Exception('Invalid lphi value. Exiting.') @uphi.setter def uphi(self, uphi): if isinstance(uphi, np.ndarray): self.__uphi = uphi else: raise Exception('Invalid uphi value. Exiting.') # nonlinear constraints at shooting node N @lh_e.setter def lh_e(self, lh_e): if isinstance(lh_e, np.ndarray): self.__lh_e = lh_e else: raise Exception('Invalid lh_e value. Exiting.') @uh_e.setter def uh_e(self, uh_e): if isinstance(uh_e, np.ndarray): self.__uh_e = uh_e else: raise Exception('Invalid uh_e value. Exiting.') # convex-over-nonlinear constraints at shooting node N @lphi_e.setter def lphi_e(self, lphi_e): if isinstance(lphi_e, np.ndarray): self.__lphi_e = lphi_e else: raise Exception('Invalid lphi_e value. Exiting.') @uphi_e.setter def uphi_e(self, uphi_e): if isinstance(uphi_e, np.ndarray): self.__uphi_e = uphi_e else: raise Exception('Invalid uphi_e value. Exiting.') # SLACK bounds # soft bounds on x @lsbx.setter def lsbx(self, lsbx): if isinstance(lsbx, np.ndarray): self.__lsbx = lsbx else: raise Exception('Invalid lsbx value. Exiting.') @usbx.setter def usbx(self, usbx): if isinstance(usbx, np.ndarray): self.__usbx = usbx else: raise Exception('Invalid usbx value. Exiting.') @idxsbx.setter def idxsbx(self, idxsbx): if isinstance(idxsbx, np.ndarray): self.__idxsbx = idxsbx else: raise Exception('Invalid idxsbx value. Exiting.') @Jsbx.setter def Jsbx(self, Jsbx): if isinstance(Jsbx, np.ndarray): self.__idxsbx = J_to_idx_slack(Jsbx) else: raise Exception('Invalid Jsbx value, expected numpy array. Exiting.') # soft bounds on u @lsbu.setter def lsbu(self, lsbu): if isinstance(lsbu, np.ndarray): self.__lsbu = lsbu else: raise Exception('Invalid lsbu value. Exiting.') @usbu.setter def usbu(self, usbu): if isinstance(usbu, np.ndarray): self.__usbu = usbu else: raise Exception('Invalid usbu value. Exiting.') @idxsbu.setter def idxsbu(self, idxsbu): if isinstance(idxsbu, np.ndarray): self.__idxsbu = idxsbu else: raise Exception('Invalid idxsbu value. Exiting.') @Jsbu.setter def Jsbu(self, Jsbu): if isinstance(Jsbu, np.ndarray): self.__idxsbu = J_to_idx_slack(Jsbu) else: raise Exception('Invalid Jsbu value. Exiting.') # soft bounds on x at shooting node N @lsbx_e.setter def lsbx_e(self, lsbx_e): if isinstance(lsbx_e, np.ndarray): self.__lsbx_e = lsbx_e else: raise Exception('Invalid lsbx_e value. Exiting.') @usbx_e.setter def usbx_e(self, usbx_e): if isinstance(usbx_e, np.ndarray): self.__usbx_e = usbx_e else: raise Exception('Invalid usbx_e value. Exiting.') @idxsbx_e.setter def idxsbx_e(self, idxsbx_e): if isinstance(idxsbx_e, np.ndarray): self.__idxsbx_e = idxsbx_e else: raise Exception('Invalid idxsbx_e value. Exiting.') @Jsbx_e.setter def Jsbx_e(self, Jsbx_e): if isinstance(Jsbx_e, np.ndarray): self.__idxsbx_e = J_to_idx_slack(Jsbx_e) else: raise Exception('Invalid Jsbx_e value. Exiting.') # soft bounds on general linear constraints @lsg.setter def lsg(self, lsg): if isinstance(lsg, np.ndarray): self.__lsg = lsg else: raise Exception('Invalid lsg value. Exiting.') @usg.setter def usg(self, usg): if isinstance(usg, np.ndarray): self.__usg = usg else: raise Exception('Invalid usg value. Exiting.') @idxsg.setter def idxsg(self, idxsg): if isinstance(idxsg, np.ndarray): self.__idxsg = idxsg else: raise Exception('Invalid idxsg value. Exiting.') @Jsg.setter def Jsg(self, Jsg): if isinstance(Jsg, np.ndarray): self.__idxsg = J_to_idx_slack(Jsg) else: raise Exception('Invalid Jsg value, expected numpy array. Exiting.') # soft bounds on nonlinear constraints @lsh.setter def lsh(self, lsh): if isinstance(lsh, np.ndarray): self.__lsh = lsh else: raise Exception('Invalid lsh value. Exiting.') @ush.setter def ush(self, ush): if isinstance(ush, np.ndarray): self.__ush = ush else: raise Exception('Invalid ush value. Exiting.') @idxsh.setter def idxsh(self, idxsh): if isinstance(idxsh, np.ndarray): self.__idxsh = idxsh else: raise Exception('Invalid idxsh value. Exiting.') @Jsh.setter def Jsh(self, Jsh): if isinstance(Jsh, np.ndarray): self.__idxsh = J_to_idx_slack(Jsh) else: raise Exception('Invalid Jsh value, expected numpy array. Exiting.') # soft bounds on convex-over-nonlinear constraints @lsphi.setter def lsphi(self, lsphi): if isinstance(lsphi, np.ndarray): self.__lsphi = lsphi else: raise Exception('Invalid lsphi value. Exiting.') @usphi.setter def usphi(self, usphi): if isinstance(usphi, np.ndarray): self.__usphi = usphi else: raise Exception('Invalid usphi value. Exiting.') @idxsphi.setter def idxsphi(self, idxsphi): if isinstance(idxsphi, np.ndarray): self.__idxsphi = idxsphi else: raise Exception('Invalid idxsphi value. Exiting.') @Jsphi.setter def Jsphi(self, Jsphi): if isinstance(Jsphi, np.ndarray): self.__idxsphi = J_to_idx_slack(Jsphi) else: raise Exception('Invalid Jsphi value, expected numpy array. Exiting.') # soft bounds on general linear constraints at shooting node N @lsg_e.setter def lsg_e(self, lsg_e): if isinstance(lsg_e, np.ndarray): self.__lsg_e = lsg_e else: raise Exception('Invalid lsg_e value. Exiting.') @usg_e.setter def usg_e(self, usg_e): if isinstance(usg_e, np.ndarray): self.__usg_e = usg_e else: raise Exception('Invalid usg_e value. Exiting.') @idxsg_e.setter def idxsg_e(self, idxsg_e): if isinstance(idxsg_e, np.ndarray): self.__idxsg_e = idxsg_e else: raise Exception('Invalid idxsg_e value. Exiting.') @Jsg_e.setter def Jsg_e(self, Jsg_e): if isinstance(Jsg_e, np.ndarray): self.__idxsg_e = J_to_idx_slack(Jsg_e) else: raise Exception('Invalid Jsg_e value, expected numpy array. Exiting.') # soft bounds on nonlinear constraints at shooting node N @lsh_e.setter def lsh_e(self, lsh_e): if isinstance(lsh_e, np.ndarray): self.__lsh_e = lsh_e else: raise Exception('Invalid lsh_e value. Exiting.') @ush_e.setter def ush_e(self, ush_e): if isinstance(ush_e, np.ndarray): self.__ush_e = ush_e else: raise Exception('Invalid ush_e value. Exiting.') @idxsh_e.setter def idxsh_e(self, idxsh_e): if isinstance(idxsh_e, np.ndarray): self.__idxsh_e = idxsh_e else: raise Exception('Invalid idxsh_e value. Exiting.') @Jsh_e.setter def Jsh_e(self, Jsh_e): if isinstance(Jsh_e, np.ndarray): self.__idxsh_e = J_to_idx_slack(Jsh_e) else: raise Exception('Invalid Jsh_e value, expected numpy array. Exiting.') # soft bounds on convex-over-nonlinear constraints at shooting node N @lsphi_e.setter def lsphi_e(self, lsphi_e): if isinstance(lsphi_e, np.ndarray): self.__lsphi_e = lsphi_e else: raise Exception('Invalid lsphi_e value. Exiting.') @usphi_e.setter def usphi_e(self, usphi_e): if isinstance(usphi_e, np.ndarray): self.__usphi_e = usphi_e else: raise Exception('Invalid usphi_e value. Exiting.') @idxsphi_e.setter def idxsphi_e(self, idxsphi_e): if isinstance(idxsphi_e, np.ndarray): self.__idxsphi_e = idxsphi_e else: raise Exception('Invalid idxsphi_e value. Exiting.') @Jsphi_e.setter def Jsphi_e(self, Jsphi_e): if isinstance(Jsphi_e, np.ndarray): self.__idxsphi_e = J_to_idx_slack(Jsphi_e) else: raise Exception('Invalid Jsphi_e value. Exiting.') def set(self, attr, value): setattr(self, attr, value) class AcadosOcpOptions: """ class containing the description of the solver options """ def __init__(self): self.__qp_solver = 'PARTIAL_CONDENSING_HPIPM' # qp solver to be used in the NLP solver self.__hessian_approx = 'GAUSS_NEWTON' # hessian approximation self.__integrator_type = 'ERK' # integrator type self.__tf = None # prediction horizon self.__nlp_solver_type = 'SQP_RTI' # NLP solver self.__globalization = 'FIXED_STEP' self.__nlp_solver_step_length = 1.0 # fixed Newton step length self.__levenberg_marquardt = 0.0 self.__collocation_type = 'GAUSS_LEGENDRE' self.__sim_method_num_stages = 4 # number of stages in the integrator self.__sim_method_num_steps = 1 # number of steps in the integrator self.__sim_method_newton_iter = 3 # number of Newton iterations in simulation method self.__sim_method_jac_reuse = 0 self.__qp_solver_tol_stat = None # QP solver stationarity tolerance self.__qp_solver_tol_eq = None # QP solver equality tolerance self.__qp_solver_tol_ineq = None # QP solver inequality self.__qp_solver_tol_comp = None # QP solver complementarity self.__qp_solver_iter_max = 50 # QP solver max iter self.__qp_solver_cond_N = None # QP solver: new horizon after partial condensing self.__qp_solver_warm_start = 0 self.__nlp_solver_tol_stat = 1e-6 # NLP solver stationarity tolerance self.__nlp_solver_tol_eq = 1e-6 # NLP solver equality tolerance self.__nlp_solver_tol_ineq = 1e-6 # NLP solver inequality self.__nlp_solver_tol_comp = 1e-6 # NLP solver complementarity self.__nlp_solver_max_iter = 100 # NLP solver maximum number of iterations self.__Tsim = None # automatically calculated as tf/N self.__print_level = 0 # print level self.__initialize_t_slacks = 0 # possible values: 0, 1 self.__model_external_shared_lib_dir = None # path to the the .so lib self.__model_external_shared_lib_name = None # name of the the .so lib self.__regularize_method = None self.__time_steps = None self.__shooting_nodes = None self.__exact_hess_cost = 1 self.__exact_hess_dyn = 1 self.__exact_hess_constr = 1 self.__ext_cost_num_hess = 0 self.__alpha_min = 0.05 self.__alpha_reduction = 0.7 self.__line_search_use_sufficient_descent = 0 self.__globalization_use_SOC = 0 self.__full_step_dual = 0 self.__eps_sufficient_descent = 1e-4 @property def qp_solver(self): """QP solver to be used in the NLP solver. String in ('PARTIAL_CONDENSING_HPIPM', 'FULL_CONDENSING_QPOASES', 'FULL_CONDENSING_HPIPM', 'PARTIAL_CONDENSING_QPDUNES', 'PARTIAL_CONDENSING_OSQP'). Default: 'PARTIAL_CONDENSING_HPIPM'. """ return self.__qp_solver @property def hessian_approx(self): """Hessian approximation. String in ('GAUSS_NEWTON', 'EXACT'). Default: 'GAUSS_NEWTON'. """ return self.__hessian_approx @property def integrator_type(self): """ Integrator type. String in ('ERK', 'IRK', 'GNSF', 'DISCRETE', 'LIFTED_IRK'). Default: 'ERK'. """ return self.__integrator_type @property def nlp_solver_type(self): """NLP solver. String in ('SQP', 'SQP_RTI'). Default: 'SQP_RTI'. """ return self.__nlp_solver_type @property def globalization(self): """Globalization type. String in ('FIXED_STEP', 'MERIT_BACKTRACKING'). Default: 'FIXED_STEP'. .. note:: preliminary implementation. """ return self.__globalization @property def collocation_type(self): """Collocation type: relevant for implicit integrators -- string in {GAUSS_RADAU_IIA, GAUSS_LEGENDRE}. Default: GAUSS_LEGENDRE """ return self.__collocation_type @property def regularize_method(self): """Regularization method for the Hessian. String in ('NO_REGULARIZE', 'MIRROR', 'PROJECT', 'PROJECT_REDUC_HESS', 'CONVEXIFY') or :code:`None`. Default: :code:`None`. """ return self.__regularize_method @property def nlp_solver_step_length(self): """ Fixed Newton step length. Type: float > 0. Default: 1.0. """ return self.__nlp_solver_step_length @property def levenberg_marquardt(self): """ Factor for LM regularization. Type: float >= 0 Default: 0.0. """ return self.__levenberg_marquardt @property def sim_method_num_stages(self): """ Number of stages in the integrator. Type: int > 0 or ndarray of ints > 0 of shape (N,). Default: 4 """ return self.__sim_method_num_stages @property def sim_method_num_steps(self): """ Number of steps in the integrator. Type: int > 0 or ndarray of ints > 0 of shape (N,). Default: 1 """ return self.__sim_method_num_steps @property def sim_method_newton_iter(self): """ Number of Newton iterations in simulation method. Type: int > 0 Default: 3 """ return self.__sim_method_newton_iter @property def sim_method_jac_reuse(self): """ Integer determining if jacobians are reused within integrator or ndarray of ints > 0 of shape (N,). 0: False (no reuse); 1: True (reuse) Default: 0 """ return self.__sim_method_jac_reuse @property def qp_solver_tol_stat(self): """ QP solver stationarity tolerance. Default: :code:`None` """ return self.__qp_solver_tol_stat @property def qp_solver_tol_eq(self): """ QP solver equality tolerance. Default: :code:`None` """ return self.__qp_solver_tol_eq @property def qp_solver_tol_ineq(self): """ QP solver inequality. Default: :code:`None` """ return self.__qp_solver_tol_ineq @property def qp_solver_tol_comp(self): """ QP solver complementarity. Default: :code:`None` """ return self.__qp_solver_tol_comp @property def qp_solver_cond_N(self): """QP solver: New horizon after partial condensing. Set to N by default -> no condensing.""" return self.__qp_solver_cond_N @property def qp_solver_warm_start(self): """QP solver: Warm starting. 0: no warm start; 1: warm start; 2: hot start.""" return self.__qp_solver_warm_start @property def qp_solver_iter_max(self): """ QP solver: maximum number of iterations. Type: int > 0 Default: 50 """ return self.__qp_solver_iter_max @property def tol(self): """ NLP solver tolerance. Sets or gets the max of :py:attr:`nlp_solver_tol_eq`, :py:attr:`nlp_solver_tol_ineq`, :py:attr:`nlp_solver_tol_comp` and :py:attr:`nlp_solver_tol_stat`. """ return max([self.__nlp_solver_tol_eq, self.__nlp_solver_tol_ineq,\ self.__nlp_solver_tol_comp, self.__nlp_solver_tol_stat]) @property def qp_tol(self): """ QP solver tolerance. Sets all of the following at once or gets the max of :py:attr:`qp_solver_tol_eq`, :py:attr:`qp_solver_tol_ineq`, :py:attr:`qp_solver_tol_comp` and :py:attr:`qp_solver_tol_stat`. """ return max([self.__qp_solver_tol_eq, self.__qp_solver_tol_ineq,\ self.__qp_solver_tol_comp, self.__qp_solver_tol_stat]) @property def nlp_solver_tol_stat(self): """ NLP solver stationarity tolerance. Type: float > 0 Default: 1e-6 """ return self.__nlp_solver_tol_stat @property def nlp_solver_tol_eq(self): """NLP solver equality tolerance""" return self.__nlp_solver_tol_eq @property def alpha_min(self): """Minimal step size for globalization MERIT_BACKTRACKING, default: 0.05.""" return self.__alpha_min @property def alpha_reduction(self): """Step size reduction factor for globalization MERIT_BACKTRACKING, default: 0.7.""" return self.__alpha_reduction @property def line_search_use_sufficient_descent(self): """ Determines if sufficient descent (Armijo) condition is used in line search. Type: int; 0 or 1; default: 0. """ return self.__line_search_use_sufficient_descent @property def eps_sufficient_descent(self): """ Factor for sufficient descent (Armijo) conditon, see line_search_use_sufficient_descent. Type: float, default: 1e-4. """ return self.__eps_sufficient_descent @property def globalization_use_SOC(self): """ Determines if second order correction (SOC) is done when using MERIT_BACKTRACKING. SOC is done if preliminary line search does not return full step. Type: int; 0 or 1; default: 0. """ return self.__globalization_use_SOC @property def full_step_dual(self): """ Determines if dual variables are updated with full steps (alpha=1.0) when primal variables are updated with smaller step. Type: int; 0 or 1; default: 0. """ return self.__full_step_dual @property def nlp_solver_tol_ineq(self): """NLP solver inequality tolerance""" return self.__nlp_solver_tol_ineq @property def nlp_solver_tol_comp(self): """NLP solver complementarity tolerance""" return self.__nlp_solver_tol_comp @property def nlp_solver_max_iter(self): """ NLP solver maximum number of iterations. Type: int > 0 Default: 100 """ return self.__nlp_solver_max_iter @property def time_steps(self): """ Vector with time steps between the shooting nodes. Set automatically to uniform discretization if :py:attr:`N` and :py:attr:`tf` are provided. Default: :code:`None` """ return self.__time_steps @property def shooting_nodes(self): """ Vector with the shooting nodes, time_steps will be computed from it automatically. Default: :code:`None` """ return self.__shooting_nodes @property def tf(self): """ Prediction horizon Type: float > 0 Default: :code:`None` """ return self.__tf @property def Tsim(self): """ Time horizon for one integrator step. Automatically calculated as :py:attr:`tf`/:py:attr:`N`. Default: :code:`None` """ return self.__Tsim @property def print_level(self): """ Verbosity of printing. Type: int >= 0 Default: 0 """ return self.__print_level @property def model_external_shared_lib_dir(self): """Path to the .so lib""" return self.__model_external_shared_lib_dir @property def model_external_shared_lib_name(self): """Name of the .so lib""" return self.__model_external_shared_lib_name @property def exact_hess_constr(self): """ Used in case of hessian_approx == 'EXACT'.\n Can be used to turn off exact hessian contributions from the constraints module. """ return self.__exact_hess_constr @property def exact_hess_cost(self): """ Used in case of hessian_approx == 'EXACT'.\n Can be used to turn off exact hessian contributions from the cost module. """ return self.__exact_hess_cost @property def exact_hess_dyn(self): """ Used in case of hessian_approx == 'EXACT'.\n Can be used to turn off exact hessian contributions from the dynamics module. """ return self.__exact_hess_dyn @property def ext_cost_num_hess(self): """ Determines if custom hessian approximation for cost contribution is used (> 0).\n Or if hessian contribution is evaluated exactly using CasADi external function (=0 - default). """ return self.__ext_cost_num_hess @qp_solver.setter def qp_solver(self, qp_solver): qp_solvers = ('PARTIAL_CONDENSING_HPIPM', \ 'FULL_CONDENSING_QPOASES', 'FULL_CONDENSING_HPIPM', \ 'PARTIAL_CONDENSING_QPDUNES', 'PARTIAL_CONDENSING_OSQP') if qp_solver in qp_solvers: self.__qp_solver = qp_solver else: raise Exception('Invalid qp_solver value. Possible values are:\n\n' \ + ',\n'.join(qp_solvers) + '.\n\nYou have: ' + qp_solver + '.\n\nExiting.') @regularize_method.setter def regularize_method(self, regularize_method): regularize_methods = ('NO_REGULARIZE', 'MIRROR', 'PROJECT', \ 'PROJECT_REDUC_HESS', 'CONVEXIFY') if regularize_method in regularize_methods: self.__regularize_method = regularize_method else: raise Exception('Invalid regularize_method value. Possible values are:\n\n' \ + ',\n'.join(regularize_methods) + '.\n\nYou have: ' + regularize_method + '.\n\nExiting.') @collocation_type.setter def collocation_type(self, collocation_type): collocation_types = ('GAUSS_RADAU_IIA', 'GAUSS_LEGENDRE') if collocation_type in collocation_types: self.__collocation_type = collocation_type else: raise Exception('Invalid collocation_type value. Possible values are:\n\n' \ + ',\n'.join(collocation_types) + '.\n\nYou have: ' + collocation_type + '.\n\nExiting.') @hessian_approx.setter def hessian_approx(self, hessian_approx): hessian_approxs = ('GAUSS_NEWTON', 'EXACT') if hessian_approx in hessian_approxs: self.__hessian_approx = hessian_approx else: raise Exception('Invalid hessian_approx value. Possible values are:\n\n' \ + ',\n'.join(hessian_approxs) + '.\n\nYou have: ' + hessian_approx + '.\n\nExiting.') @integrator_type.setter def integrator_type(self, integrator_type): integrator_types = ('ERK', 'IRK', 'GNSF', 'DISCRETE', 'LIFTED_IRK') if integrator_type in integrator_types: self.__integrator_type = integrator_type else: raise Exception('Invalid integrator_type value. Possible values are:\n\n' \ + ',\n'.join(integrator_types) + '.\n\nYou have: ' + integrator_type + '.\n\nExiting.') @tf.setter def tf(self, tf): self.__tf = tf @time_steps.setter def time_steps(self, time_steps): if isinstance(time_steps, np.ndarray): if len(time_steps.shape) == 1: self.__time_steps = time_steps else: raise Exception('Invalid time_steps, expected np.ndarray of shape (N,).') else: raise Exception('Invalid time_steps, expected np.ndarray.') @shooting_nodes.setter def shooting_nodes(self, shooting_nodes): if isinstance(shooting_nodes, np.ndarray): if len(shooting_nodes.shape) == 1: self.__shooting_nodes = shooting_nodes else: raise Exception('Invalid shooting_nodes, expected np.ndarray of shape (N+1,).') else: raise Exception('Invalid shooting_nodes, expected np.ndarray.') @Tsim.setter def Tsim(self, Tsim): self.__Tsim = Tsim @globalization.setter def globalization(self, globalization): globalization_types = ('MERIT_BACKTRACKING', 'FIXED_STEP') if globalization in globalization_types: self.__globalization = globalization else: raise Exception('Invalid globalization value. Possible values are:\n\n' \ + ',\n'.join(globalization_types) + '.\n\nYou have: ' + globalization + '.\n\nExiting.') @alpha_min.setter def alpha_min(self, alpha_min): self.__alpha_min = alpha_min @alpha_reduction.setter def alpha_reduction(self, alpha_reduction): self.__alpha_reduction = alpha_reduction @line_search_use_sufficient_descent.setter def line_search_use_sufficient_descent(self, line_search_use_sufficient_descent): if line_search_use_sufficient_descent in [0, 1]: self.__line_search_use_sufficient_descent = line_search_use_sufficient_descent else: raise Exception(f'Invalid value for line_search_use_sufficient_descent. Possible values are 0, 1, got {line_search_use_sufficient_descent}') @globalization_use_SOC.setter def globalization_use_SOC(self, globalization_use_SOC): if globalization_use_SOC in [0, 1]: self.__globalization_use_SOC = globalization_use_SOC else: raise Exception(f'Invalid value for globalization_use_SOC. Possible values are 0, 1, got {globalization_use_SOC}') @full_step_dual.setter def full_step_dual(self, full_step_dual): if full_step_dual in [0, 1]: self.__full_step_dual = full_step_dual else: raise Exception(f'Invalid value for full_step_dual. Possible values are 0, 1, got {full_step_dual}') @eps_sufficient_descent.setter def eps_sufficient_descent(self, eps_sufficient_descent): if isinstance(eps_sufficient_descent, float) and eps_sufficient_descent > 0: self.__eps_sufficient_descent = eps_sufficient_descent else: raise Exception('Invalid eps_sufficient_descent value. eps_sufficient_descent must be a positive float. Exiting') @sim_method_num_stages.setter def sim_method_num_stages(self, sim_method_num_stages): # if isinstance(sim_method_num_stages, int): # self.__sim_method_num_stages = sim_method_num_stages # else: # raise Exception('Invalid sim_method_num_stages value. sim_method_num_stages must be an integer. Exiting.') self.__sim_method_num_stages = sim_method_num_stages @sim_method_num_steps.setter def sim_method_num_steps(self, sim_method_num_steps): # if isinstance(sim_method_num_steps, int): # self.__sim_method_num_steps = sim_method_num_steps # else: # raise Exception('Invalid sim_method_num_steps value. sim_method_num_steps must be an integer. Exiting.') self.__sim_method_num_steps = sim_method_num_steps @sim_method_newton_iter.setter def sim_method_newton_iter(self, sim_method_newton_iter): if isinstance(sim_method_newton_iter, int): self.__sim_method_newton_iter = sim_method_newton_iter else: raise Exception('Invalid sim_method_newton_iter value. sim_method_newton_iter must be an integer. Exiting.') @sim_method_jac_reuse.setter def sim_method_jac_reuse(self, sim_method_jac_reuse): # if sim_method_jac_reuse in (True, False): self.__sim_method_jac_reuse = sim_method_jac_reuse # else: # raise Exception('Invalid sim_method_jac_reuse value. sim_method_jac_reuse must be a Boolean.') @nlp_solver_type.setter def nlp_solver_type(self, nlp_solver_type): nlp_solver_types = ('SQP', 'SQP_RTI') if nlp_solver_type in nlp_solver_types: self.__nlp_solver_type = nlp_solver_type else: raise Exception('Invalid nlp_solver_type value. Possible values are:\n\n' \ + ',\n'.join(nlp_solver_types) + '.\n\nYou have: ' + nlp_solver_type + '.\n\nExiting.') @nlp_solver_step_length.setter def nlp_solver_step_length(self, nlp_solver_step_length): if isinstance(nlp_solver_step_length, float) and nlp_solver_step_length > 0: self.__nlp_solver_step_length = nlp_solver_step_length else: raise Exception('Invalid nlp_solver_step_length value. nlp_solver_step_length must be a positive float. Exiting') @levenberg_marquardt.setter def levenberg_marquardt(self, levenberg_marquardt): if isinstance(levenberg_marquardt, float) and levenberg_marquardt >= 0: self.__levenberg_marquardt = levenberg_marquardt else: raise Exception('Invalid levenberg_marquardt value. levenberg_marquardt must be a positive float. Exiting') @qp_solver_iter_max.setter def qp_solver_iter_max(self, qp_solver_iter_max): if isinstance(qp_solver_iter_max, int) and qp_solver_iter_max > 0: self.__qp_solver_iter_max = qp_solver_iter_max else: raise Exception('Invalid qp_solver_iter_max value. qp_solver_iter_max must be a positive int. Exiting') @qp_solver_cond_N.setter def qp_solver_cond_N(self, qp_solver_cond_N): if isinstance(qp_solver_cond_N, int) and qp_solver_cond_N >= 0: self.__qp_solver_cond_N = qp_solver_cond_N else: raise Exception('Invalid qp_solver_cond_N value. qp_solver_cond_N must be a positive int. Exiting') @qp_solver_warm_start.setter def qp_solver_warm_start(self, qp_solver_warm_start): if qp_solver_warm_start in [0, 1, 2]: self.__qp_solver_warm_start = qp_solver_warm_start else: raise Exception('Invalid qp_solver_warm_start value. qp_solver_warm_start must be 0 or 1 or 2. Exiting') @qp_tol.setter def qp_tol(self, qp_tol): if isinstance(qp_tol, float) and qp_tol > 0: self.__qp_solver_tol_eq = qp_tol self.__qp_solver_tol_ineq = qp_tol self.__qp_solver_tol_stat = qp_tol self.__qp_solver_tol_comp = qp_tol else: raise Exception('Invalid qp_tol value. qp_tol must be a positive float. Exiting') @qp_solver_tol_stat.setter def qp_solver_tol_stat(self, qp_solver_tol_stat): if isinstance(qp_solver_tol_stat, float) and qp_solver_tol_stat > 0: self.__qp_solver_tol_stat = qp_solver_tol_stat else: raise Exception('Invalid qp_solver_tol_stat value. qp_solver_tol_stat must be a positive float. Exiting') @qp_solver_tol_eq.setter def qp_solver_tol_eq(self, qp_solver_tol_eq): if isinstance(qp_solver_tol_eq, float) and qp_solver_tol_eq > 0: self.__qp_solver_tol_eq = qp_solver_tol_eq else: raise Exception('Invalid qp_solver_tol_eq value. qp_solver_tol_eq must be a positive float. Exiting') @qp_solver_tol_ineq.setter def qp_solver_tol_ineq(self, qp_solver_tol_ineq): if isinstance(qp_solver_tol_ineq, float) and qp_solver_tol_ineq > 0: self.__qp_solver_tol_ineq = qp_solver_tol_ineq else: raise Exception('Invalid qp_solver_tol_ineq value. qp_solver_tol_ineq must be a positive float. Exiting') @qp_solver_tol_comp.setter def qp_solver_tol_comp(self, qp_solver_tol_comp): if isinstance(qp_solver_tol_comp, float) and qp_solver_tol_comp > 0: self.__qp_solver_tol_comp = qp_solver_tol_comp else: raise Exception('Invalid qp_solver_tol_comp value. qp_solver_tol_comp must be a positive float. Exiting') @tol.setter def tol(self, tol): if isinstance(tol, float) and tol > 0: self.__nlp_solver_tol_eq = tol self.__nlp_solver_tol_ineq = tol self.__nlp_solver_tol_stat = tol self.__nlp_solver_tol_comp = tol else: raise Exception('Invalid tol value. tol must be a positive float. Exiting') @nlp_solver_tol_stat.setter def nlp_solver_tol_stat(self, nlp_solver_tol_stat): if isinstance(nlp_solver_tol_stat, float) and nlp_solver_tol_stat > 0: self.__nlp_solver_tol_stat = nlp_solver_tol_stat else: raise Exception('Invalid nlp_solver_tol_stat value. nlp_solver_tol_stat must be a positive float. Exiting') @nlp_solver_tol_eq.setter def nlp_solver_tol_eq(self, nlp_solver_tol_eq): if isinstance(nlp_solver_tol_eq, float) and nlp_solver_tol_eq > 0: self.__nlp_solver_tol_eq = nlp_solver_tol_eq else: raise Exception('Invalid nlp_solver_tol_eq value. nlp_solver_tol_eq must be a positive float. Exiting') @nlp_solver_tol_ineq.setter def nlp_solver_tol_ineq(self, nlp_solver_tol_ineq): if isinstance(nlp_solver_tol_ineq, float) and nlp_solver_tol_ineq > 0: self.__nlp_solver_tol_ineq = nlp_solver_tol_ineq else: raise Exception('Invalid nlp_solver_tol_ineq value. nlp_solver_tol_ineq must be a positive float. Exiting') @nlp_solver_tol_comp.setter def nlp_solver_tol_comp(self, nlp_solver_tol_comp): if isinstance(nlp_solver_tol_comp, float) and nlp_solver_tol_comp > 0: self.__nlp_solver_tol_comp = nlp_solver_tol_comp else: raise Exception('Invalid nlp_solver_tol_comp value. nlp_solver_tol_comp must be a positive float. Exiting') @nlp_solver_max_iter.setter def nlp_solver_max_iter(self, nlp_solver_max_iter): if isinstance(nlp_solver_max_iter, int) and nlp_solver_max_iter > 0: self.__nlp_solver_max_iter = nlp_solver_max_iter else: raise Exception('Invalid nlp_solver_max_iter value. nlp_solver_max_iter must be a positive int. Exiting') @print_level.setter def print_level(self, print_level): if isinstance(print_level, int) and print_level >= 0: self.__print_level = print_level else: raise Exception('Invalid print_level value. print_level takes one of the values >=0. Exiting') @model_external_shared_lib_dir.setter def model_external_shared_lib_dir(self, model_external_shared_lib_dir): if isinstance(model_external_shared_lib_dir, str) : self.__model_external_shared_lib_dir = model_external_shared_lib_dir else: raise Exception('Invalid model_external_shared_lib_dir value. Str expected.' \ + '.\n\nYou have: ' + type(model_external_shared_lib_dir) + '.\n\nExiting.') @model_external_shared_lib_name.setter def model_external_shared_lib_name(self, model_external_shared_lib_name): if isinstance(model_external_shared_lib_name, str) : if model_external_shared_lib_name[-3:] == '.so' : raise Exception('Invalid model_external_shared_lib_name value. Remove the .so extension.' \ + '.\n\nYou have: ' + type(model_external_shared_lib_name) + '.\n\nExiting.') else : self.__model_external_shared_lib_name = model_external_shared_lib_name else: raise Exception('Invalid model_external_shared_lib_name value. Str expected.' \ + '.\n\nYou have: ' + type(model_external_shared_lib_name) + '.\n\nExiting.') @exact_hess_constr.setter def exact_hess_constr(self, exact_hess_constr): if exact_hess_constr in [0, 1]: self.__exact_hess_constr = exact_hess_constr else: raise Exception('Invalid exact_hess_constr value. exact_hess_constr takes one of the values 0, 1. Exiting') @exact_hess_cost.setter def exact_hess_cost(self, exact_hess_cost): if exact_hess_cost in [0, 1]: self.__exact_hess_cost = exact_hess_cost else: raise Exception('Invalid exact_hess_cost value. exact_hess_cost takes one of the values 0, 1. Exiting') @exact_hess_dyn.setter def exact_hess_dyn(self, exact_hess_dyn): if exact_hess_dyn in [0, 1]: self.__exact_hess_dyn = exact_hess_dyn else: raise Exception('Invalid exact_hess_dyn value. exact_hess_dyn takes one of the values 0, 1. Exiting') @ext_cost_num_hess.setter def ext_cost_num_hess(self, ext_cost_num_hess): if ext_cost_num_hess in [0, 1]: self.__ext_cost_num_hess = ext_cost_num_hess else: raise Exception('Invalid ext_cost_num_hess value. ext_cost_num_hess takes one of the values 0, 1. Exiting') def set(self, attr, value): setattr(self, attr, value) class AcadosOcp: """ Class containing the full description of the optimal control problem. This object can be used to create an :py:class:`acados_template.acados_ocp_solver.AcadosOcpSolver`. The class has the following properties that can be modified to formulate a specific OCP, see below: - :py:attr:`dims` of type :py:class:`acados_template.acados_ocp.AcadosOcpDims` - :py:attr:`model` of type :py:class:`acados_template.acados_model.AcadosModel` - :py:attr:`cost` of type :py:class:`acados_template.acados_ocp.AcadosOcpCost` - :py:attr:`constraints` of type :py:class:`acados_template.acados_ocp.AcadosOcpConstraints` - :py:attr:`solver_options` of type :py:class:`acados_template.acados_ocp.AcadosOcpOptions` - :py:attr:`acados_include_path` (set automatically) - :py:attr:`acados_lib_path` (set automatically) - :py:attr:`parameter_values` - used to initialize the parameters (can be changed) """ def __init__(self, acados_path=''): """ Keyword arguments: acados_path -- path of your acados installation """ if acados_path == '': acados_path = get_acados_path() self.dims = AcadosOcpDims() """Dimension definitions, type :py:class:`acados_template.acados_ocp.AcadosOcpDims`""" self.model = AcadosModel() """Model definitions, type :py:class:`acados_template.acados_model.AcadosModel`""" self.cost = AcadosOcpCost() """Cost definitions, type :py:class:`acados_template.acados_ocp.AcadosOcpCost`""" self.constraints = AcadosOcpConstraints() """Constraints definitions, type :py:class:`acados_template.acados_ocp.AcadosOcpConstraints`""" self.solver_options = AcadosOcpOptions() """Solver Options, type :py:class:`acados_template.acados_ocp.AcadosOcpOptions`""" self.acados_include_path = f'{acados_path}/include' """Path to acados include directory, type: string""" self.acados_lib_path = f'{acados_path}/lib' """Path to where acados library is located, type: string""" import numpy self.cython_include_dirs = numpy.get_include() self.__parameter_values = np.array([]) self.__problem_class = 'OCP' self.code_export_directory = 'c_generated_code' """Path to where code will be exported. Default: `c_generated_code`.""" @property def parameter_values(self): """:math:`p` - initial values for parameter - can be updated stagewise""" return self.__parameter_values @parameter_values.setter def parameter_values(self, parameter_values): if isinstance(parameter_values, np.ndarray): self.__parameter_values = parameter_values else: raise Exception('Invalid parameter_values value. ' + f'Expected numpy array, got {type(parameter_values)}.') def set(self, attr, value): # tokenize string tokens = attr.split('_', 1) if len(tokens) > 1: setter_to_call = getattr(getattr(self, tokens[0]), 'set') else: setter_to_call = getattr(self, 'set') setter_to_call(tokens[1], value) return