Ejemplo n.º 1
0
 def _substitute_symbols(self, expr, variables, parameters):
     if isinstance(expr, (int, float)):
         return expr
     for sym in symvar(expr):
         [child, name] = self.symbol_dict[sym.getName()]
         if name in child._variables:
             expr = substitute(expr, sym, variables[child.label, name])
         elif name in child._parameters:
             expr = substitute(expr, sym, parameters[child.label, name])
     return expr
Ejemplo n.º 2
0
    def set_path(self, path, r2r=False):
        """Define an analytic expression of the geometric path.

        Note: The path must be defined as a function of self.s[0].

        Args:
            path (list of SXMatrix): An expression of the geometric path as
                a function of self.s[0]. Its dimension must equal self.sys.ny
            r2r (boolean): Reparameterize path such that a rest to rest
                transition is performed

        Example:
            >>> S = FlatSystem(2, 4)
            >>> P = PathFollowing(S)
            >>> P.set_path([P.s[0], P.s[0]])
        """
        if isinstance(path, list):
            path = cas.vertcat(path)
        if r2r:
            path = cas.substitute(path, self.s[0], self._r2r())
        self.path[:, 0] = path
        dot_s = cas.vertcat([self.s[1:], 0])
        for i in range(1, self.sys.order + 1):
            self.path[:, i] = cas.mul(
                cas.jacobian(self.path[:, i - 1], self.s), dot_s)
Ejemplo n.º 3
0
    def check_ss_feasibility(self, tol=1e-6):
        """Returns whether path is steady state feasible.

        Args:
            tol (double): tolerance for feasibility check (default = 1e-6)

        Raises:
            warning when bounds may be too strict
        """
        for i, f in enumerate(self.constraints):
            F = cas.substitute(f[0], self.sys.y, self.path)
            F1 = cas.SXFunction([self.s], [F - f[1]])
            F2 = cas.SXFunction([self.s], [-F + f[2]])
            F1.init()
            F2.init()
            c1 = np.array([evalf(F1, np.hstack([s, [1e-50] * (self.sys.order)])).T
                           for s in self.prob['s']])
            c2 = np.array([evalf(F2, np.hstack([s, [1e-50] * (self.sys.order)])).T
                           for s in self.prob['s']])
            if np.any(c1 + tol < 0):
                warnings.warn("lower bound of constraint %d may be to strict" % i)
                return False
            elif np.any(c2 + tol < 0):
                warnings.warn("upper bound of constraint %d may be to strict" % i)
                return False
        return True
Ejemplo n.º 4
0
def sx_to_double(sx1,variables,values):
	val1 = casadi.substitute(sx1,variables,values)
	val1 = casadi.getValue(val1)
	import numpy
	if numpy.isnan(val1):
		print 'sx_to_double:Warning: casadi.getValue returned nan! \
Make sure "sx1" is fully specified once \
"values" have been substituted for "variables"'
	return val1
Ejemplo n.º 5
0
    def _get_solution(self):
        """Get the solution from the solver output

        Fills the dictionary self.sol with the information:
            * 's': The optimal s as a function of time
            * 't': The time vector
            * 'states': Numerical values of the states defined in self.sys

        TODO: perform accurate integration to determine time
        TODO: Do exact interpolation
        """
        solver = self.prob['solver']
        N = self.options['N']
        x_opt = np.array(solver.getOutput("x")).ravel()
        b_opt = np.reshape(x_opt, (N + 1, -1), order='F')
        self.sol['b'] = b_opt

        # Determine time on a sufficiently fine spatial grid
        s0 = np.linspace(0, 1, 1001)
        delta = s0[1] - s0[0]

        pieces = [lambda s, b=b_opt, ss=ss, j=j:
                  sum([bb * (s - ss) ** i / fact[i]
                       for i, bb in enumerate(b[j])])
                  for j, ss in enumerate(self.prob['s'][:-1])]
        conds = lambda s0: [np.logical_and(self.prob['s'][i] <= s0,
                                           s0 <= self.prob['s'][i+1])
                            for i in range(N)]
        b0_opt = np.piecewise(s0, conds(s0), pieces)
        b0_opt[b0_opt < 0] = 0
        time = np.cumsum(np.hstack([0, 2 * delta / (np.sqrt(b0_opt[:-1]) +
                                    np.sqrt(b0_opt[1:]))]))

        # Resample to constant time-grid
        t = np.arange(time[0], time[-1], self.options['Ts'])
        st = np.interp(t, time, s0)
        # Evaluate solution on equidistant time grid
        b_opt = np.c_[[np.piecewise(st, conds(st), pieces, b=b_opt[:, i:])
                       for i in range(self.sys.order)]].T
        st = np.matrix(st)

        # Determine s and derivatives from b_opt
        b, Ds = self._make_path()[1:]
        Ds_f = cas.SXFunction([b], [Ds])  # derivatives of s wrt b
        Ds_f.init()
        s_opt = np.hstack((st.T, np.array([evalf(Ds_f, bb).toArray().ravel()
                                          for bb in b_opt])))
        self.sol['s'] = np.asarray(s_opt)
        self.sol['t'] = t
        # Evaluate the states
        f = cas.SXFunction([self.s], [cas.substitute(cas.vertcat(self.sys.x.values()),
                                           self.sys.y, self.path)])
        f_val = np.array([evalf(f, s.T).toArray().ravel() for s in s_opt])
        self.sol['states'] = dict([(k, f_val[:, i]) for i, k in
                          enumerate(self.sys.x.keys())])
Ejemplo n.º 6
0
 def _check_for_lineq(self):
     g = []
     for con in self.global_constraints:
         lb, ub = con[1], con[2]
         g = vertcat(g, con[0] - lb)
         if not isinstance(lb, np.ndarray):
             lb, ub = [lb], [ub]
         for k, _ in enumerate(lb):
             if lb[k] != ub[k]:
                 return False, None, None
     sym, jac = [], []
     for child, q_i in self.q_i.items():
         for name, ind in q_i.items():
             var = self.distr_problem.father.get_variables(child, name, spline=False, symbolic=True, substitute=False)
             jj = jacobian(g, var)
             jac = horzcat(jac, jj[:, ind])
             sym.append(var)
     for nghb in self.q_ij.keys():
         for child, q_ij in self.q_ij[nghb].items():
             for name, ind in q_ij.items():
                 var = self.distr_problem.father.get_variables(child, name, spline=False, symbolic=True, substitute=False)
                 jj = jacobian(g, var)
                 jac = horzcat(jac, jj[:, ind])
                 sym.append(var)
     for sym in symvar(jac):
         if sym not in self.par_global.values():
             return False, None, None
     par = struct_symMX(self.par_global_struct)
     A, b = jac, -g
     for s in sym:
         A = substitute(A, s, np.zeros(s.shape))
         b = substitute(b, s, np.zeros(s.shape))
     dep_b = [s.name() for s in symvar(b)]
     dep_A = [s.name() for s in symvar(b)]
     for name, sym in self.par_global.items():
         if sym.name() in dep_b:
             b = substitute(b, sym, par[name])
         if sym.name() in dep_A:
             A = substitute(A, sym, par[name])
     A = Function('A', [par], [A]).expand()
     b = Function('b', [par], [b]).expand()
     return True, A, b
Ejemplo n.º 7
0
 def _check_for_lineq(self):
     g = []
     for con in self.constraints:
         lb, ub = con[1], con[2]
         g = vertcat([g, con[0] - lb])
         if not isinstance(lb, np.ndarray):
             lb, ub = [lb], [ub]
         for k in range(len(lb)):
             if lb[k] != ub[k]:
                 return False, None, None
     sym, jac = [], []
     for child, q_i in self.q_i.items():
         for name, ind in q_i.items():
             var = child.get_variable(name, spline=False)
             jj = jacobian(g, var)
             jac = horzcat([jac, jj[:, ind]])
             sym.append(var)
     for nghb in self.q_ij.keys():
         for child, q_ij in self.q_ij[nghb].items():
             for name, ind in q_ij.items():
                 var = child.get_variable(name, spline=False)
                 jj = jacobian(g, var)
                 jac = horzcat([jac, jj[:, ind]])
                 sym.append(var)
     for sym in symvar(jac):
         if sym not in self.par_i.values():
             return False, None, None
     par = struct_symMX(self.par_struct)
     A, b = jac, -g
     for s in sym:
         A = substitute(A, s, np.zeros(s.shape))
         b = substitute(b, s, np.zeros(s.shape))
     dep_b = [s.getName() for s in symvar(b)]
     dep_A = [s.getName() for s in symvar(b)]
     for name, sym in self.par_i.items():
         if sym.getName() in dep_b:
             b = substitute(b, sym, par[name])
         if sym.getName() in dep_A:
             A = substitute(A, sym, par[name])
     A = MXFunction('A', [par], [A]).expand()
     b = MXFunction('b', [par], [b]).expand()
     return True, A, b
Ejemplo n.º 8
0
    def _make_objective(self):
        """Construct objective function from the problem definition

        Make time optimal objective function and add a regularization to
        ensure a unique solution. When additional objective terms are defined
        these are added to the objective as well.

        TODO: Improve accuracy of integration
        """
        order = self.sys.order
        N = self.options['N']
        b = self.prob['vars']
        # ds = 1.0/(N+1)
        obj = 2 * sum(np.diff(self.prob['s']) / (cas.sqrt(b[:N, 0]) + cas.sqrt(b[1:, 0])))
        # reg = sum(cas.sqrt((b[1:, order - 1] - b[:N, order - 1]) ** 2))
        reg = sum((b[2:, order - 1] - 2 * b[1:N, order - 1] +
                    b[:(N - 1), order - 1]) ** 2)
        for f in self.objective['Lagrange']:
            path, bs = self._make_path()[0:2]
            # S = np.arange(0, 1, 1.0/(N+1))
            S = self.prob['s']
            b = self.prob['vars']
            L = cas.substitute(f, self.sys.y, path)
            L = 2 * sum(cas.vertcat([
                            cas.substitute(
                                L,
                                cas.vertcat([self.s[0], bs]),
                                cas.vertcat([S[j], b[j, :].T])
                            ) for j in range(1, N + 1)
                        ])
                    * np.diff(self.prob['s']) / (cas.sqrt(b[:N, 0]) + cas.sqrt(b[1:, 0]))
                )
            # L = sum(cas.vertcat([cas.substitute(L, cas.vertcat([self.s[0],
            #     [bs[i] for i in range(0, bs.numel())]]),
            #     cas.vertcat([S[j], [b[j, i] for i in range(0, self.sys.order)]]))
            #     for j in range(0, N + 1)]))
            obj = obj + L
        self.prob['obj'] = obj + self.options['reg'] * reg
Ejemplo n.º 9
0
    def _make_path(self):
        """Rewrite the path as a function of the optimization variables.

        Substitutes the time derivatives of s in the expression of the path by
        expressions that are function of b and its path derivatives by
        repeatedly applying the chainrule

        Returns:
            * SXMatrix. The substituted path
            * SXMatrix. b and the path derivatives
            * SXMatrix. The derivatives of s as a function of b
        """
        b = cas.ssym("b", self.sys.order)
        db = cas.vertcat((b[1:], 0))
        Ds = cas.SXMatrix.nan(self.sys.order)  # Time derivatives of s
        Ds[0] = cas.sqrt(b[0])
        Ds[1] = b[1] / 2
        # Apply chainrule for finding higher order derivatives
        for i in range(1, self.sys.order - 1):
            Ds[i + 1] = (cas.mul(cas.jacobian(Ds[i], b), db) * self.s[1] +
                       cas.jacobian(Ds[i], self.s[1]) * Ds[1])
        Ds = cas.substitute(Ds, self.s[1], cas.sqrt(b[0]))
        return cas.substitute(self.path, self.s[1:], Ds), b, Ds
Ejemplo n.º 10
0
    def get_function(self, function_name):
        if function_name in self.functions:
            return self.functions[function_name]

        try:
            tree = self.root.classes[function_name]
        except KeyError:
            raise Exception('Unknown function {}'.format(function_name))

        inputs = []
        outputs = []
        tmp = []
        for s in tree.symbols.values():
            src = self.get_mx(s)
            if 'input' in s.prefixes:
                inputs.append(src)
            elif 'output' in s.prefixes:
                outputs.append(src)
            else:
                tmp.append(src)

        # Store current variable values
        values = {}
        for variable in inputs:
            values[variable] = variable

        # Process statements in order
        for statement in tree.statements:
            src = self.get_mx(statement)
            for assignment in src:
                [values[assignment.left]] = ca.substitute([assignment.right], list(values.keys()), list(values.values()))

        output_expr = ca.substitute([values[output] for output in outputs], tmp, [values[t] for t in tmp])
        func = ca.Function(tree.name, inputs, output_expr)
        self.functions[function_name] = func

        return func
Ejemplo n.º 11
0
 def _make_constraints(self):
     """
     Parse the constraints and put them in the correct format
     """
     N = self.options['N']
     con = self._ode(self.prob['vars'])
     lb = np.alen(con) * [0]
     ub = np.alen(con) * [0]
     S = self.prob['s']
     b = self.prob['vars']
     path, bs = self._make_path()[0:2]
     sbs = cas.vertcat([self.s[0], bs])
     Sb = cas.horzcat([S, b]).T
     for f in self.constraints:
         F = cas.substitute(f[0], self.sys.y, path)
         if f[3] is None:
             F = cas.vertcat([cas.substitute(F, sbs, Sb[:, j])
                              for j in xrange(N + 1)])
             Flb = f[1](S) if hasattr(f[1], '__call__') else [f[1]] * len(S)
             Fub = f[2](S) if hasattr(f[2], '__call__') else [f[2]] * len(S)
             con.append(F)
             lb.extend(Flb)
             ub.extend(Fub)
         else:
             F = cas.vertcat([cas.substitute(F, sbs, Sb[:, j])
                              for j in f[3]])
             con.append(F)
             lb.extend([f[1]])
             ub.extend([f[2]])
     if self.options['bc']:
         con.append(b[0, 0])
         lb.append(0)
         ub.append(1e-50)
         con.append(b[-1, 0])
         lb.append(0)
         ub.append(1e-50)
     self.prob['con'] = [con, lb, ub]
Ejemplo n.º 12
0
def tangent_approx(f: SYM, x: SYM, a: SYM = None, assert_linear: bool = False) -> Dict[str, SYM]:
    """
    Create a tangent approximation of a non-linear function f(x) about point a
    using a block lower triangular solver

    0 = f(x) = f(a) + J*x   # taylor series about a (if f(x) linear in x, then globally valid)
    J*x = -f(a)             # solve for x
    x = -J^{-1}f(a)         # but inverse is slow, so we use solve
    where J = df/dx
    """
    # find f(a)
    if a is None:
        a = ca.DM.zeros(x.numel(), 1)
    f_a = ca.substitute(f, x, a)  # f(a)
    J = ca.jacobian(f, x)
    if assert_linear and ca.depends_on(J, x):
        raise AssertionError('not linear')
    # solve is smart enough to to convert to blt if necessary
    return ca.solve(J, -f_a)
Ejemplo n.º 13
0
    def update_expressions(self):
        """
        Update OCP expressions using current parameter values.
        """
        ocp_expressions = [self.ocp.initial, self.ocp.ode, self.ocp.alg,
                           self.ocp.path, self.ocp.point, self.ocp.mterm,
                           self.ocp.lterm]
        parameters = casadi.vertcat([p.var() for p in self._parameters])
        parameter_values = [p.getStart() for p in self._parameters]
        [self.initial,
         self.ode,
         self.alg,
         self.path,
         self.point,
         self.mterm,
         self.lterm] = casadi.substitute(ocp_expressions, [parameters],
                                         [parameter_values])

        # Transform ODE RHS into residual
        if self._casadi_blt:
            self.ode = N.array(casadi.der(self.ocp.x)) - self.ode
Ejemplo n.º 14
0
 def _get_solution(self):
     solver = self.prob['solver']
     N = self.options['N']
     Nc = self.options['Nc']
     x_opt = np.array(solver.getOutput("x")).ravel()
     delta = np.diff(self.prob['s'])
     b_opt = np.reshape(x_opt[:self.sys.order * (N + 1)], (N + 1, -1), order='F')
     h_opt = np.reshape(x_opt[self.sys.order * (N + 1):], (Nc, -1), order='F')
     time = np.cumsum(np.hstack([0, 2 * delta / (np.sqrt(b_opt[:-1, 0]) +
                                              np.sqrt(b_opt[1:, 0]))]))
     # Resample to constant time-grid
     t = np.linspace(time[0], time[-1], self.options['Nt'])
     b_opt = np.array([np.interp(t, time, b) for b in b_opt.T]).T
     # Get s and derivatives from b_opt
     s = np.interp(t, time, self.prob['s'])
     b, Ds = self._make_path()[1:]
     Ds_f = cas.SXFunction([b], [Ds])  # derivatives of s wrt b
     Ds_f.init()
     s_opt = np.vstack((s, np.vstack([evalf(Ds_f, bb).toArray().ravel()
                                       for bb in b_opt]).T)).T
     self.sol['s'] = s_opt
     self.sol['h'] = h_opt
     self.sol['b'] = b_opt
     self.sol['t'] = t
     # Evaluate the states
     basisH = self._make_basis()
     B = [np.dot(basisH(s_opt[:, 0]), h_opt)]
     for i in range(1, self.h.size2()):
         # B.append(np.matrix(np.dot(basisH.derivative(s_opt[:, 0], i), h_opt)))
         Bi, p = basisH.derivative(i)
         B.append(np.matrix(np.dot(np.dot(Bi(s_opt[:, 0]), p), h_opt)))
     f = cas.SXFunction([cas.vertcat([self.s, cas.vec(self.h)])], [cas.substitute(self.sys.x.values(),
                                        self.sys.y, self.path)])
     f_val = np.array([evalf(f, s.T).toArray().ravel() for s in np.hstack((s_opt, np.hstack(B)))])
     self.sol['states'] = dict([(k, f_val[:, i]) for i, k in
                       enumerate(self.sys.x.keys())])
Ejemplo n.º 15
0
    def initialize(self, config_file=None):
        """
        Initialize state vector with default values

        :param config_file: Path to an initialization file.
        """
        if config_file:
            # TODO read start and stop time from config_file and call:
            # self.setup_experiment(start,stop)
            # for now, assume that setup_experiment was called beforehand
            raise NotImplementedError

        # Set values of parameters defined in the model into the state vector
        for var in self.__pymoca_model.parameters:
            # First check to see if parameter is already set (this allows child classes to override model defaults)
            if np.isfinite(self.get_var(var.symbol.name())):
                continue

            # Also test to see if the value is constant
            if isinstance(var.value, ca.MX) and not var.value.is_constant():
                continue

            # Try to extract the value
            try:
                # Extract the value as a python type
                val = var.python_type(var.value)
            except ValueError:
                # var.value is a float NaN being cast to non-float
                continue
            else:
                # If val is finite, we set it
                if np.isfinite(val):
                    logger.debug(
                        'SimulationProblem: Setting parameter {} = {}'.format(
                            var.symbol.name(), val))
                    self.set_var(var.symbol.name(), val)

        # Assemble initial residuals and set values from start attributes into the state vector
        constrained_residuals = []
        minimized_residuals = []
        for var in itertools.chain(self.__pymoca_model.states,
                                   self.__pymoca_model.alg_states):
            var_name = var.symbol.name()
            var_nominal = self.get_variable_nominal(var_name)

            # Attempt to cast var.start to python type
            mx_start = ca.MX(var.start)
            if mx_start.is_constant():
                # cast var.start to python type
                start_val = var.python_type(mx_start.to_DM())
            else:
                # var.start is a symbolic expression with unknown value
                start_val = None

            if start_val == 0.0 and not var.fixed:
                # To make initialization easier, we allow setting initial states by providing timeseries
                # with names that match a symbol in the model. We only check for this matching if the start
                # and fixed attributes were left as default
                try:
                    start_val = self.initial_state()[var_name]
                except KeyError:
                    pass
                else:
                    # An initial state was found- add it to the constrained residuals
                    logger.debug(
                        'Initialize: Added {} = {} to initial equations (found matching timeseries).'
                        .format(var_name, start_val))
                    # Set var to be fixed
                    var.fixed = True

            # Attempt to set start_val in the state vector. Default to zero if unknown.
            try:
                self.set_var(var_name,
                             start_val if start_val is not None else 0.0)
            except KeyError:
                logger.warning(
                    'Initialize: {} not found in state vector. Initial value of {} not set.'
                    .format(var_name, start_val))

            # Add a residual for the difference between the state and its starting expression
            start_expr = start_val if start_val is not None else var.start
            if var.fixed:
                # require residual = 0
                constrained_residuals.append(
                    (var.symbol - start_expr) / var_nominal)
            else:
                # minimize residual
                minimized_residuals.append(
                    (var.symbol - start_expr) / var_nominal)

        # Default start var for ders is zero
        for der_var in self.__mx['derivatives']:
            self.set_var(der_var.name(), 0.0)

        # Warn for nans in state vector (verify we didn't miss anything)
        self.__warn_for_nans()

        # Optionally encourage a steady-state initial condition
        if getattr(self, 'encourage_steady_state_initial_conditions', False):
            # add penalty for der(var) != 0.0
            for d in self.__mx['derivatives']:
                logger.debug('Added {} to the minimized residuals.'.format(
                    d.name()))
                minimized_residuals.append(d)

        # Make minimized_residuals into a single symbolic object
        minimized_residual = ca.vertcat(*minimized_residuals)

        # Assemble symbolics needed to make a function describing the initial condition of the model
        # We constrain every entry in this MX to zero
        equality_constraints = ca.vertcat(self.__dae_residual,
                                          self.__initial_residual,
                                          *constrained_residuals)

        # The variables that need a mutually consistent initial condition
        X = ca.vertcat(*self.__sym_list[:self.__states_end_index])

        # Make a list of unscaled symbols and a list of their scaled equivalent
        unscaled_symbols = []
        scaled_symbols = []
        for sym_name, nominal in self.__nominals.items():
            # Add the symbol to the lists
            symbol = self.__sym_dict[sym_name]
            unscaled_symbols.append(symbol)
            scaled_symbols.append(symbol * nominal)

        # Make the lists symbolic
        unscaled_symbols = ca.vertcat(*unscaled_symbols)
        scaled_symbols = ca.vertcat(*scaled_symbols)

        # Substitute unscaled terms for scaled terms
        equality_constraints = ca.substitute(equality_constraints,
                                             unscaled_symbols, scaled_symbols)
        minimized_residual = ca.substitute(minimized_residual,
                                           unscaled_symbols, scaled_symbols)

        logger.debug('SimulationProblem: Initial Equations are ' +
                     str(equality_constraints))
        logger.debug('SimulationProblem: Minimized Residuals are ' +
                     str(minimized_residual))

        # State bounds can be symbolic, written in terms of parameters. After all
        # parameter values are known, we evaluate the numeric values of bounds.
        symbolic_bounds = ca.vertcat(*[
            ca.horzcat(v.min, v.max) for v in itertools.chain(
                self.__pymoca_model.states, self.__pymoca_model.alg_states,
                self.__pymoca_model.der_states)
        ])
        bound_evaluator = ca.Function('bound_evaluator',
                                      self.__mx['parameters'],
                                      [symbolic_bounds])

        # Evaluate bounds using values of parameters
        n_parameters = len(self.__mx['parameters'])
        if n_parameters > 0:
            [evaluated_bounds
             ] = bound_evaluator.call(self.__state_vector[-n_parameters:])
        else:
            [evaluated_bounds] = bound_evaluator.call([])

        # Construct arrays of state bounds (used in the initialize() nlp, but not in __do_step rootfinder)
        self.__lbx = evaluated_bounds[:, 0]
        self.__ubx = evaluated_bounds[:, 1]

        # Constrain model equation residuals to zero
        lbg = np.zeros(equality_constraints.size1())
        ubg = np.zeros(equality_constraints.size1())

        # Construct objective function from the input residual
        objective_function = ca.dot(minimized_residual, minimized_residual)

        # Construct nlp and solver to find initial state using ipopt
        parameters = ca.vertcat(*self.__mx['time'],
                                *self.__mx['constant_inputs'],
                                *self.__mx['parameters'])
        nlp = {
            'x': X,
            'f': objective_function,
            'g': equality_constraints,
            'p': parameters
        }
        solver = ca.nlpsol('solver', 'ipopt', nlp, self.solver_options())

        # Construct guess
        guess = ca.vertcat(
            *np.nan_to_num(self.__state_vector[:self.__states_end_index]))

        # Find initial state
        initial_state = solver(x0=guess,
                               lbx=self.__lbx,
                               ubx=self.__ubx,
                               lbg=lbg,
                               ubg=ubg,
                               p=self.__state_vector[self.__states_end_index:])

        # If unsuccessful, stop.
        return_status = solver.stats()['return_status']
        if return_status not in {
                'Solve_Succeeded', 'Solved_To_Acceptable_Level'
        }:
            raise Exception(
                'Initialization Failed with return status "{}"'.format(
                    return_status))

        # Update state vector with initial conditions
        self.__state_vector[:self.__states_end_index] = initial_state[
            'x'][:self.__states_end_index].T

        # make a copy of the initialized initial state vector in case we want to run the model again
        self.__initialized_state_vector = copy.deepcopy(self.__state_vector)

        # Warn for nans in state vector after initialization
        self.__warn_for_nans()
Ejemplo n.º 16
0
    def __init__(self, **kwargs):
        # Check arguments
        assert ('model_folder' in kwargs)

        # Log pymoca version
        logger.debug("Using pymoca {}.".format(pymoca.__version__))

        # Transfer model from the Modelica .mo file to CasADi using pymoca
        if 'model_name' in kwargs:
            model_name = kwargs['model_name']
        else:
            if hasattr(self, 'model_name'):
                model_name = self.model_name
            else:
                model_name = self.__class__.__name__

        # Load model from pymoca backend
        self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
            kwargs['model_folder'], model_name, self.compiler_options())

        # Extract the CasADi MX variables used in the model
        self.__mx = {}
        self.__mx['time'] = [self.__pymoca_model.time]
        self.__mx['states'] = [v.symbol for v in self.__pymoca_model.states]
        self.__mx['derivatives'] = [
            v.symbol for v in self.__pymoca_model.der_states
        ]
        self.__mx['algebraics'] = [
            v.symbol for v in self.__pymoca_model.alg_states
        ]
        self.__mx['parameters'] = [
            v.symbol for v in self.__pymoca_model.parameters
        ]
        self.__mx['constant_inputs'] = []
        self.__mx['lookup_tables'] = []

        # TODO: implement delayed feedback
        delayed_feedback_variables = []

        for v in self.__pymoca_model.inputs:
            if v.symbol.name() in delayed_feedback_variables:
                # Delayed feedback variables are local to each ensemble, and
                # therefore belong to the collection of algebraic variables,
                # rather than to the control inputs.
                self.__mx['algebraics'].append(v.symbol)
            else:
                if v.symbol.name() in kwargs.get('lookup_tables', []):
                    self.__mx['lookup_tables'].append(v.symbol)
                else:
                    # All inputs are constant inputs
                    self.__mx['constant_inputs'].append(v.symbol)

        # Log variables in debug mode
        if logger.getEffectiveLevel() == logging.DEBUG:
            logger.debug("SimulationProblem: Found states {}".format(', '.join(
                [var.name() for var in self.__mx['states']])))
            logger.debug("SimulationProblem: Found derivatives {}".format(
                ', '.join([var.name() for var in self.__mx['derivatives']])))
            logger.debug("SimulationProblem: Found algebraics {}".format(
                ', '.join([var.name() for var in self.__mx['algebraics']])))
            logger.debug("SimulationProblem: Found constant inputs {}".format(
                ', '.join([var.name()
                           for var in self.__mx['constant_inputs']])))
            logger.debug("SimulationProblem: Found parameters {}".format(
                ', '.join([var.name() for var in self.__mx['parameters']])))

        # Initialize an AliasDict for nominals and types
        self.__nominals = AliasDict(self.alias_relation)
        self.__python_types = AliasDict(self.alias_relation)
        for v in itertools.chain(self.__pymoca_model.states,
                                 self.__pymoca_model.alg_states,
                                 self.__pymoca_model.inputs):
            sym_name = v.symbol.name()

            # Store the types in an AliasDict
            self.__python_types[sym_name] = v.python_type

            # If the nominal is 0.0 or 1.0 or -1.0, ignore: get_variable_nominal returns a default of 1.0
            # TODO: handle nominal vectors (update() will need to load them)
            if ca.MX(v.nominal).is_zero() or ca.MX(
                    v.nominal - 1).is_zero() or ca.MX(v.nominal + 1).is_zero():
                continue
            else:
                if ca.MX(v.nominal).size1() != 1:
                    logger.error(
                        'Vector Nominals not supported yet. ({})'.format(
                            sym_name))
                self.__nominals[sym_name] = ca.fabs(v.nominal)
                if logger.getEffectiveLevel() == logging.DEBUG:
                    logger.debug(
                        "SimulationProblem: Setting nominal value for variable {} to {}"
                        .format(sym_name, self.__nominals[sym_name]))

        # Initialize DAE and initial residuals
        variable_lists = [
            'states', 'der_states', 'alg_states', 'inputs', 'constants',
            'parameters'
        ]
        function_arguments = [self.__pymoca_model.time] + [
            ca.veccat(*[
                v.symbol for v in getattr(self.__pymoca_model, variable_list)
            ]) for variable_list in variable_lists
        ]

        self.__dae_residual = self.__pymoca_model.dae_residual_function(
            *function_arguments)

        self.__initial_residual = self.__pymoca_model.initial_residual_function(
            *function_arguments)
        if self.__initial_residual is None:
            self.__initial_residual = ca.MX()

        # Construct state vector
        self.__sym_list = self.__mx['states'] + self.__mx['algebraics'] + self.__mx['derivatives'] + \
            self.__mx['time'] + self.__mx['constant_inputs'] + self.__mx['parameters']
        self.__state_vector = np.full(len(self.__sym_list), np.nan)

        # A very handy index
        self.__states_end_index = len(self.__mx['states']) + \
            len(self.__mx['algebraics']) + len(self.__mx['derivatives'])

        # Construct a dict to look up symbols by name (or iterate over)
        self.__sym_dict = OrderedDict(
            ((sym.name(), sym) for sym in self.__sym_list))

        # Assemble some symbolics, including those needed for a backwards Euler derivative approximation
        X = ca.vertcat(*self.__sym_list[:self.__states_end_index])
        X_prev = ca.vertcat(*[
            ca.MX.sym(sym.name() + '_prev')
            for sym in self.__sym_list[:self.__states_end_index]
        ])
        dt = ca.MX.sym("delta_t")

        # Make a list of derivative approximations using backwards Euler formulation
        derivative_approximation_residuals = []
        for index, derivative_state in enumerate(self.__mx['derivatives']):
            derivative_approximation_residuals.append(
                derivative_state - (X[index] - X_prev[index]) / dt)

        # Append residuals for derivative approximations
        dae_residual = ca.vertcat(self.__dae_residual,
                                  *derivative_approximation_residuals)

        # TODO: implement lookup_tables

        # Make a list of unscaled symbols and a list of their scaled equivalent
        unscaled_symbols = []
        scaled_symbols = []
        for sym_name, nominal in self.__nominals.items():
            index = self.__get_state_vector_index(sym_name)
            # If the symbol is a state, Add the symbol to the lists
            if index <= self.__states_end_index:
                unscaled_symbols.append(X[index])
                scaled_symbols.append(X[index] * nominal)

                # Also scale previous states
                unscaled_symbols.append(X_prev[index])
                scaled_symbols.append(X_prev[index] * nominal)

        # Substitute unscaled terms for scaled terms
        dae_residual = ca.substitute(dae_residual,
                                     ca.vertcat(*unscaled_symbols),
                                     ca.vertcat(*scaled_symbols))

        if logger.getEffectiveLevel() == logging.DEBUG:
            logger.debug('SimulationProblem: DAE Residual is ' +
                         str(dae_residual))

        if X.size1() != dae_residual.size1():
            logger.error(
                'Formulation Error: Number of states ({}) does not equal number of equations ({})'
                .format(X.size1(), dae_residual.size1()))

        # Construct function parameters
        parameters = ca.vertcat(dt, X_prev,
                                *self.__sym_list[self.__states_end_index:])

        # Construct a function res_vals that returns the numerical residuals of a numerical state
        self.__res_vals = ca.Function("res_vals", [X, parameters],
                                      [dae_residual])

        # Use rootfinder() to make a function that takes a step forward in time by trying to zero res_vals()
        options = {'nlpsol': 'ipopt', 'nlpsol_options': self.solver_options()}
        self.__do_step = ca.rootfinder("next_state", "nlpsol", self.__res_vals,
                                       options)

        # Call parent class for default behaviour.
        super().__init__()
Ejemplo n.º 17
0
def load_model(model_folder: str, model_name: str, compiler_options: Dict[str, str]) -> CachedModel:
    """
    Loads a precompiled CasADi model into a CachedModel instance.

    :param model_folder: Folder where the precompiled CasADi model is located.
    :param model_name: Name of the model.
    :param compiler_options: Dictionary of compiler options.

    :returns: CachedModel instance.
    """

    db_file = os.path.join(model_folder, model_name + ".pymoca_cache")

    if compiler_options.get('mtime_check', True):
        # Mtime check
        cache_mtime = os.path.getmtime(db_file)
        for folder in [model_folder] + compiler_options.get('library_folders', []):
            for root, dir, files in os.walk(folder, followlinks=True):
                for item in fnmatch.filter(files, "*.mo"):
                    filename = os.path.join(root, item)
                    if os.path.getmtime(filename) > cache_mtime:
                        raise InvalidCacheError("Cache out of date")

    # Create empty model object
    model = CachedModel()

    # Load metadata
    with open(db_file, 'rb') as f:
        db = pickle.load(f)

        if db['version'] != __version__:
            raise InvalidCacheError('Cache generated for a different version of pymoca')

        # Check compiler options. We ignore the library folders, as they have
        # already been checked, and checking them will impede platform
        # portability of the cache.
        exclude_options = ['library_folders']
        old_opts = {k: v for k, v in db['options'].items() if k not in exclude_options}
        new_opts = {k: v for k, v in compiler_options.items() if k not in exclude_options}

        if old_opts != new_opts:
            raise InvalidCacheError('Cache generated for different compiler options')

        # Pickles are platform independent, but dynamic libraries are not
        if compiler_options.get('codegen', False):
            if db['library_os'] != os.name:
                raise InvalidCacheError('Cache generated for incompatible OS')

        # Include references to the shared libraries
        for o in ['dae_residual', 'initial_residual', 'variable_metadata', 'delay_arguments']:
            if isinstance(db[o], str):
                # Path to codegen'd library
                f = ca.external(o, db[o])
            else:
                # Pickled CasADi Function; use as is
                assert isinstance(db[o], ca.Function)
                f = db[o]

            setattr(model, '_' + o + '_function', f)

        # Load variables per category
        variables_with_metadata = ['states', 'alg_states', 'inputs', 'parameters', 'constants']
        variable_dict = {}
        for key in variables_with_metadata:
            variables = getattr(model, key)
            for i, d in enumerate(db[key]):
                variable = Variable.from_dict(d)
                variables.append(variable)
                variable_dict[variable.symbol.name()] = variable

        model.der_states = [Variable.from_dict(d) for d in db['der_states']]
        model.outputs = db['outputs']
        model.delay_states = db['delay_states']
        model.alias_relation = db['alias_relation']

        # Evaluate variable metadata:
        parameter_vector = ca.veccat(*[v.symbol for v in model.parameters])
        metadata = dict(zip(variables_with_metadata, model.variable_metadata_function(parameter_vector)))
        independent_metadata = dict(zip(
            variables_with_metadata,
            (np.array(x) for x in model.variable_metadata_function(ca.veccat(*[np.nan for v in model.parameters])))))

        for k, key in enumerate(variables_with_metadata):
            m = db[key + "__metadata_dependent"]
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(CASADI_ATTRIBUTES):
                    if m[i, j]:
                        setattr(variable, tmp, metadata[key][i, j])
                    else:
                        setattr(variable, tmp, independent_metadata[key][i, j])

        # Evaluate delay arguments:
        if model.delay_states:
            args = [model.time,
                    ca.veccat(*model._symbols(model.states)),
                    ca.veccat(*model._symbols(model.der_states)),
                    ca.veccat(*model._symbols(model.alg_states)),
                    ca.veccat(*model._symbols(model.inputs)),
                    ca.veccat(*model._symbols(model.constants)),
                    ca.veccat(*model._symbols(model.parameters))]
            delay_arguments_raw = model.delay_arguments_function(*args)

            nan_args = [ca.repmat(np.nan, *arg.size()) for arg in args]
            independent_delay_arguments_raw = model.delay_arguments_function(*nan_args)

            delay_expressions_raw = delay_arguments_raw[::2]
            delay_durations_raw = delay_arguments_raw[1::2]
            independent_delay_durations_raw = independent_delay_arguments_raw[1::2]

            assert 1 == len({len(delay_expressions_raw), len(delay_durations_raw),
                len(independent_delay_durations_raw)})

            all_symbols = [model.time,
                           *model._symbols(model.states),
                           *model._symbols(model.der_states),
                           *model._symbols(model.alg_states),
                           *model._symbols(model.inputs),
                           *model._symbols(model.constants),
                           *model._symbols(model.parameters)]

            duration_dependencies = db['__delay_duration_dependent']

            # Get rid of false dependency symbols not used in any delay
            # durations. This significantly reduces the work the (slow)
            # substitute() calls have to do later on.
            actual_deps = sorted(set(np.array(duration_dependencies).ravel()))

            actual_dep_symbols = [np.nan] * len(all_symbols)
            for i in actual_deps:
                actual_dep_symbols[i] = all_symbols[i]

            delay_durations_simplified = ca.Function(
                'replace_false_deps',
                all_symbols,
                delay_durations_raw).call(
                    actual_dep_symbols)

            # Get rid of remaining hidden dependencies in the delay durations
            for i, expr in enumerate(delay_expressions_raw):
                if duration_dependencies[i]:
                    dur = delay_durations_simplified[i]

                    if len(duration_dependencies[i]) < len(actual_deps):
                        deps = set(ca.symvar(dur))
                        actual_deps = {all_symbols[j] for j in duration_dependencies[i]}
                        false_deps = deps - actual_deps

                        if false_deps:
                            [dur] = ca.substitute(
                                [dur],
                                list(false_deps),
                                [np.nan] * len(false_deps))
                    else:
                        # Already removed all false dependencies
                        pass
                else:
                    dur = independent_delay_durations_raw[i]

                model.delay_arguments.append(DelayArgument(expr, dur))

        # Try to coerce parameters into their Python types
        for p in model.parameters:
            for attr in CASADI_ATTRIBUTES:
                v = getattr(p, attr)
                v_mx = ca.MX(v)
                if v_mx.is_constant() and v_mx.is_regular():
                    setattr(p, attr, p.python_type(v))

    # Done
    return model
Ejemplo n.º 18
0
    def exit_classDefinition(self, tree: etree._Element):  # noqa: too-complex
        dae = HybridDae()
        dae.t = self.scope['time']
        self.model[tree] = dae

        # handle component declarations
        for var_name, v in self.scope['var'].items():
            variability = self.scope['prop'][var_name]['variability']
            if variability == 'continuous':
                if var_name in self.scope['states']:
                    dae.x = ca.vertcat(dae.x, v)
                    dae.dx = ca.vertcat(dae.dx, self.der(v))
                else:
                    dae.y = ca.vertcat(dae.y, v)
            elif variability == 'discrete':
                dae.m = ca.vertcat(dae.m, v)
            elif variability == 'parameter':
                dae.p = ca.vertcat(dae.p, v)
            elif variability == 'constant':
                dae.p = ca.vertcat(dae.p, v)
            else:
                raise ValueError('unknown variability', variability)

        for eq in self.scope['eqs']:
            if isinstance(eq, self.sym):
                dae.f_x = ca.vertcat(dae.f_x, eq)

        # build reinit expression and discrete equations
        dae.f_i = dae.x
        dae.f_m = dae.m
        for eq in self.scope['when_eqs']:
            w = eq['cond']
            for then_eq in eq['then']:
                if isinstance(then_eq, tuple):
                    if then_eq[0] == 'reinit':
                        sub_var = then_eq[1]
                        sub_expr = ca.if_else(self.edge(w), then_eq[2],
                                              sub_var)
                        dae.f_i = ca.substitute(dae.f_i, sub_var, sub_expr)
                elif isinstance(then_eq, self.sym):
                    # this is a discrete variable assignment
                    # so it should be a casadi subtraction y = x
                    assert then_eq.is_op(ca.OP_SUB) and then_eq.n_dep() == 2
                    sub_var = then_eq.dep(0)
                    sub_expr = ca.if_else(self.edge(w), then_eq.dep(1),
                                          sub_var)
                    dae.f_m = ca.substitute(dae.f_m, sub_var, sub_expr)

        dae.t = self.scope['time']
        dae.prop.update(self.scope['prop'])
        c_dict = self.scope['c']
        for k in c_dict.keys():
            dae.c = ca.vertcat(dae.c, k)
            dae.pre_c = ca.vertcat(dae.pre_c, self.pre_cond(k))
            dae.f_c = ca.vertcat(dae.f_c, c_dict[k])

        for left, right in [('f_c', 'c'), ('c', 'pre_c'), ('dx', 'x'),
                            ('f_m', 'm')]:
            vl = getattr(dae, left)
            vr = getattr(dae, right)
            if vl.shape != vr.shape:
                raise ValueError('{:s} and {:s} must have the same shape:'
                                 '\n{:s}: {:s}\t{:s}: {:s}'.format(
                                     left, right, left, str(dae.f_m), right,
                                     str(dae.m)))

        dae.ng = ca.vertcat(*self.scope['ng'])
        dae.nu = ca.vertcat(*self.scope['nu'])

        n_eq = dae.f_x.shape[0] + dae.f_m.shape[0]
        n_var = dae.x.shape[0] + dae.m.shape[0] + dae.y.shape[0]
        if n_eq != n_var:
            raise ValueError('must have equal number of equations '
                             '{:d} and unknowns {:d}\n:{:s}'.format(
                                 n_eq, n_var, str(dae)))
        self.scope_stack.pop()
Ejemplo n.º 19
0
 def _construct_upd_z_nlp(self):
     # construct variables
     self._var_struct_updz = struct([entry('z_i', struct=self.q_i_struct),
                                     entry('z_ij', struct=self.q_ij_struct)])
     var = struct_symMX(self._var_struct_updz)
     z_i = self.q_i_struct(var['z_i'])
     z_ij = self.q_ij_struct(var['z_ij'])
     # construct parameters
     self._par_struct_updz = struct([entry('x_i', struct=self.q_i_struct),
                                     entry('x_j', struct=self.q_ij_struct),
                                     entry('l_i', struct=self.q_i_struct),
                                     entry('l_ij', struct=self.q_ij_struct),
                                     entry('t'), entry('T'), entry('rho'),
                                     entry('par', struct=self.par_struct)])
     par = struct_symMX(self._par_struct_updz)
     x_i, x_j = self.q_i_struct(par['x_i']), self.q_ij_struct(par['x_j'])
     l_i, l_ij = self.q_i_struct(par['l_i']), self.q_ij_struct(par['l_ij'])
     t, T, rho = par['t'], par['T'], par['rho']
     t0 = t/T
     # transform spline variables: only consider future piece of spline
     tf = lambda cfs, basis: shift_knot1_fwd(cfs, basis, t0)
     self._transform_spline([x_i, z_i, l_i], tf, self.q_i)
     self._transform_spline([x_j, z_ij, l_ij], tf, self.q_ij)
     # construct constraints
     constraints, lb, ub = [], [], []
     for con in self.constraints:
         c = con[0]
         for sym in symvar(c):
             for label, child in self.group.items():
                 if sym.getName() in child.symbol_dict:
                     name = child.symbol_dict[sym.getName()][1]
                     v = z_i[label, name]
                     ind = self.q_i[child][name]
                     sym2 = MX.zeros(sym.size())
                     sym2[ind] = v
                     sym2 = reshape(sym2, sym.shape)
                     c = substitute(c, sym, sym2)
                     break
             for nghb in self.q_ij.keys():
                 for label, child in nghb.group.items():
                     if sym.getName() in child.symbol_dict:
                         name = child.symbol_dict[sym.getName()][1]
                         v = z_ij[nghb.label, label, name]
                         ind = self.q_ij[nghb][child][name]
                         sym2 = MX.zeros(sym.size())
                         sym2[ind] = v
                         sym2 = reshape(sym2, sym.shape)
                         c = substitute(c, sym, sym2)
                         break
             for name, s in self.par_i.items():
                 if s.getName() == sym.getName():
                     c = substitute(c, sym, par['par', name])
         constraints.append(c)
         lb.append(con[1])
         ub.append(con[2])
     self.lb_updz, self.ub_updz = lb, ub
     # construct objective
     obj = 0.
     for child, q_i in self.q_i.items():
         for name in q_i.keys():
             x = x_i[child.label, name]
             z = z_i[child.label, name]
             l = l_i[child.label, name]
             obj += mul(l.T, x-z) + 0.5*rho*mul((x-z).T, (x-z))
     for nghb in self.q_ij.keys():
         for child, q_ij in self.q_ij[nghb].items():
             for name in q_ij.keys():
                 x = x_j[str(nghb), child.label, name]
                 z = z_ij[str(nghb), child.label, name]
                 l = l_ij[str(nghb), child.label, name]
                 obj += mul(l.T, x-z) + 0.5*rho*mul((x-z).T, (x-z))
     # construct problem
     prob, compile_time = self.father.create_nlp(var, par, obj,
                                                 constraints, self.options)
     self.problem_upd_z = prob
Ejemplo n.º 20
0
 def convert_expr_from_time_to_tau(self, expr, t_k, t_kp1):
     t = self.t
     tau = self.tau
     h = t_kp1 - t_k
     return substitute(expr, t, tau * h + t_k)
Ejemplo n.º 21
0
    def _nlp_solver(self, initial_cond, final_term_cost, trajectory_target, constraints):
        '''Setting up the non linear problem settings, manually discretizing each time step'''

        # Start with an empty NLP
        w = []      # free variables vector
        w_g = []    # initial guess
        lbw = []    # lower bounds of inputs
        ubw = []    # upper bounds of inputs 
        J = 0       # initial value of cost func
        g = []      # constraints vector
        lbg = []    # lower bounds of constraints
        ubg = []    # upper bound of constraints

        # Defining a new time variable because we need MX here
        self.t = cs.MX.sym("t", 1)
        self.traj, self.traj_dot = self.format_trajectory(trajectory_target, self.t)

        # With SEA we have double the state variable and double the initial condition. 
        # We assume to start on the equilibrium
        if self.sea and self.SEAdynamics:
            initial_cond = initial_cond * 2

        # Finally, we integrate all over the timeframe
        dt = self.T / self.N

        # Defining the variables needed for the integration
        Xk = cs.MX.sym('X0', self.num_state_var * 2)
        # Adding it to the vector that has to be optimized
        w += [Xk]
        # Setting the initial condition
        w_g += initial_cond
        lbw += initial_cond
        ubw += initial_cond

        for k in range(self.N):
            # Variable for the control
            Uk = cs.MX.sym('U_' + str(k), self.num_joints)  # generate the k-th control command, nx1 dimension
            w += [Uk] 
            self.add_input_constraints(w_g, lbw, ubw)

            # Integrate till the end of the interval
            Fk = self.F(x0=Xk, p=Uk, time=dt * k)  # This is the actual integration!
            Xk_next = Fk['xf'] 
            J = J + Fk['qf']

            # Add custom constraints
            if constraints is not None:
                self.add_custom_constraints(g, lbg, ubg, Xk, Uk, dt * k, constraints)

            # Defining a new state variable
            Xk = cs.MX.sym('X'+str(k+1), self.num_state_var * 2)
            w += [Xk]
            self.add_state_constraints(lbw, ubw, w_g, initial_cond)

            # Imposing equality constraint
            g += [Xk_next - Xk] # Imposing the state variables to work under the differential equation constraints
            lbg += [0] * (2 * self.num_state_var) 
            ubg += [0] * (2 * self.num_state_var)

        # Setting constraints on the last timestep
        if constraints is not None:
            self.add_custom_constraints(g, lbg, ubg, Xk, None, self.T, constraints)

        # If we are optimizing for minimum T, we set its boundaries
        if isinstance(self.T, cs.casadi.MX):
            w += [self.T]
            w_g += [1.0]
            lbw += [0.05]
            ubw += [float('inf')]

        # Add a final term cost. If not specified is 0.
        if final_term_cost is not None:
            if self.sea and self.SEAdynamics:
                Q_dd = self.q_ddot_val(Xk[0:self.num_joints], Xk[self.num_joints:2 * self.num_joints],
                                       Xk[2*self.num_joints:3*self.num_joints], Xk[3*self.num_joints:4*self.num_joints], np.array([0]*self.num_joints))
            else:
                Q_dd = self.q_ddot_val(Xk[0:self.num_joints], Xk[self.num_joints:2 * self.num_joints], np.array([0]*self.num_joints))
            EE_pos = self.ee_pos(Xk[0:self.num_joints])
            
            J = J + final_term_cost(Xk[0:self.num_joints] - cs.substitute(self.traj, self.t, self.T),
                                    Xk[self.num_joints:2*self.num_joints] - cs.substitute(self.traj_dot, self.t, self.T),
                                    Q_dd,
                                    EE_pos,
                                    Uk)

        # Adding constraint on final timestep
        if self.final_constraints is not None:
            self.add_custom_constraints(g, lbg, ubg, Xk, None, self.T, self.final_constraints, final=True)

        # Define the problem to be solved
        problem = {'f': J, 'x': cs.vertcat(*w), 'g': cs.vertcat(*g)}
        # NLP solver options
        opts = {}
        opts["ipopt"] = {'max_iter': self.max_iter}
        # Define the solver and add boundary conditions
        solver = cs.nlpsol('solver', 'ipopt', problem, opts)
        self.solver = solver(x0=w_g, lbx=lbw, ubx=ubw, lbg=lbg, ubg=ubg)
Ejemplo n.º 22
0
 def evalX(self, x, K):
     [X] = cs.substitute([self._X], [self._x, self._K], [x, K])
     return cs.evalf(X)
Ejemplo n.º 23
0
 def optim(x):
     #for x_i, value in zip(x, paramsFlat):
         #print '%s: %s' % (x_i, value)
     ret = casadi.substitute(ll, params, casadi.vertcat(*x))
     return float(ret)
Ejemplo n.º 24
0
    a = casadi.SX.sym
    #def __init__(self, mu, v, b, c, lamb, gamma, alpha, beta, omega, d, e):
    asVals=False
    params1 = Params('GARCH', 1, 2, 2, 0, 0, .0, 0, .10, .8, .001/STEPS, 2.1, 2.1, asVals=asVals)
    params2 = Params('GARCH', 2, 2, 2, 0, 0, .0, 0, .06, .92, .001/STEPS, 2.1, 6.8, asVals=True)

    with Timer('ll calc'):
        ll, (p_1, p_2, h_1, h_2) = loglikilihood(r, params1, params2)
    if not asVals:
        with Timer('subs model'):
            ll = params1.subsForModel(ll)
            #ll = params2.subsForModel(ll)
        params = casadi.vertcat(params1.vertcat) #, params2.vertcat)
        param_vals = casadi.vertcat(params1.vertcatvals) #, params2.vertcatvals)
        with Timer('subs ll'):
            print casadi.substitute(ll, params, param_vals)
        #with Timer('calc hess'):
        #    hess = casadi.hessian(ll, params)
        #with Timer('subs hess'):
        #    hessret = casadi.substitute(hess[0], params, param_vals)
        #    hessret2 = casadi.substitute(hess[1], params, param_vals)
        with Timer('calc jacob'):
            jacob = casadi.jacobian(ll, params)
        with Timer('subs jacob'):
            jacobret = casadi.substitute(jacob, params, param_vals)
        #import pdb; pdb.set_trace()

        bounds = []
        paramsFlat = [params[x] for x in xrange(params.shape[0])]
        for count, value in enumerate(paramsFlat):
            bounds.append(params1.bound(value))
Ejemplo n.º 25
0
def rpm_sweep(prop_name, v0, kv0, i0_motor0, V_inf0, R_motor0):
    prop_data = prop_db[prop_name]
    key0 = list(prop_data['dynamic'].keys())[0]
    my_prop = prop_data['dynamic'][key0]['data']
    CT_lut = ca.interpolant('CT', 'bspline', [my_prop.index], my_prop.CT)
    CP_lut = ca.interpolant('CP', 'bspline', [my_prop.index], my_prop.CP)
    eta_lut = ca.interpolant('CP', 'bspline', [my_prop.index], my_prop.eta)
    eta_prop_lut = ca.interpolant('eta', 'bspline', [my_prop.index],
                                  my_prop.eta)
    Q_prop_lut = ca.substitute(Q_prop, CP, CP_lut(J))
    T_prop_lut = ca.substitute(T_prop, CT, CT_lut(J))

    states = ca.vertcat(rpm_prop)
    params = ca.vertcat(rho, r_prop_in, v_batt, Kv_motor, i0_motor, V_inf,
                        R_motor)

    p0 = [1.225, prop_data['D'] / 2, v0, kv0, i0_motor0, V_inf0, R_motor0]

    f_Q_prop = ca.Function('Q_prop', [states, params], [Q_prop_lut])
    f_Q_motor = ca.Function('Q_motor', [states, params], [Q_motor])
    f_T_prop = ca.Function('T_prop', [states, params], [T_prop_lut])
    f_eta_motor = ca.Function('eta_motor', [states, params], [eta_motor])
    f_eta_prop = ca.Function('eta_prop', [states, params], [eta_prop_lut(J)])
    f_eta = ca.Function('eta', [states, params], [eta_prop_lut(J) * eta_motor])

    rpm = np.array([np.linspace(0, 4000, 1000)])

    plt.figure()
    plt.subplot(311)
    plt.plot(rpm.T, f_Q_motor(rpm, p0).T, label='motor')
    plt.plot(rpm.T, f_Q_prop(rpm, p0).T, label='prop')
    plt.title('prop motor matching')
    plt.legend()
    plt.ylabel('torque, N-m')
    plt.grid(True)

    plt.subplot(312)
    plt.plot(rpm.T, f_T_prop(rpm, p0).T, label='prop')
    plt.legend()
    plt.ylabel('thrust, N')
    plt.grid(True)

    plt.subplot(313)
    plt.plot(rpm.T, f_eta_motor(rpm, p0).T, label='motor')
    plt.plot(rpm.T, f_eta_prop(rpm, p0).T, label='prop')
    plt.plot(rpm.T, f_eta(rpm, p0).T, label='total')
    plt.xlabel('prop angular velocity, rpm')
    plt.ylabel('efficiency')
    plt.grid(True)
    plt.legend()
    plt.gca().set_ylim(0, 1)
    plt.show()

    ## scipy.sparse.csc_matrix is said to be better by casadi author
    f_eta_prop_array = np.array(f_eta_prop(rpm, p0))[0]
    max_eta_prop = np.nanmax(f_eta_prop_array)
    index = np.where(f_eta_prop_array == max_eta_prop)
    omega = rpm[0][index][0]
    T = np.array(f_T_prop(omega, p0))[0]
    Q = np.array(f_Q_prop(omega, p0))[0]

    display(Math(r'\eta_{prop, max} = ' + str(max_eta_prop) \
        + r'\ at\ \Omega =\ ' + str(omega) + r'\ \frac{rev}{min}'))

    ## TODO get ideal motor properties in a dictionary and store them, including motor eta
    data = {
        'prop_name': prop_name,
        'max_eta_prop': max_eta_prop,
        'omega': omega,
        'T': T,
        'Q': Q
    }

    store(data)
Ejemplo n.º 26
0
    def defineOCP(self,
                  ocp,
                  DT=20,
                  controlCost=0,
                  xOpt=[],
                  uOpt=[],
                  finalStateCost=1,
                  deltaUCons=[]):

        self.ocp = ocp
        ocp = self.ocp
        self.DT = DT
        self.n_k = int(self.ocp.tf / self.DT)
        self.controlCost = controlCost

        stateScaling = C.vertcat([
            ocp.variable(ocp.x[k].getName()).nominal
            for k in range(ocp.x.size())
        ])
        algStateScaling = C.vertcat([
            ocp.variable(ocp.z[k].getName()).nominal
            for k in range(ocp.z.size())
        ])
        controlScaling = C.vertcat([
            ocp.variable(ocp.u[k].getName()).nominal
            for k in range(ocp.u.size())
        ])

        xOpt = xOpt / stateScaling
        uOpt = uOpt / controlScaling
        self.xOpt = xOpt
        self.uOpt = uOpt

        self.stateScaling = C.vertcat([
            ocp.variable(ocp.x[k].getName()).nominal
            for k in range(ocp.x.size())
        ])
        self.algStateScaling = C.vertcat([
            ocp.variable(ocp.z[k].getName()).nominal
            for k in range(ocp.z.size())
        ])
        self.controlScaling = C.vertcat([
            ocp.variable(ocp.u[k].getName()).nominal
            for k in range(ocp.u.size())
        ])

        odeS = C.substitute(
            ocp.ode(ocp.x), C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ])) / stateScaling
        algS = C.substitute(
            ocp.alg, C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ]))
        ltermS = C.substitute(
            ocp.lterm, C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ]))

        sysIn = C.daeIn(x=ocp.x, z=ocp.z, p=ocp.u, t=ocp.t)
        sysOut = C.daeOut(ode=odeS, alg=algS, quad=ltermS)
        odeF = C.SXFunction(sysIn, sysOut)
        odeF.init()

        C.Integrator.loadPlugin("idas")
        G = C.Integrator("idas", odeF)
        G.setOption("reltol", self.INTG_REL_TOL)  #for CVODES and IDAS
        G.setOption("abstol", self.INTG_ABS_TOL)  #for CVODES and IDAS
        G.setOption("max_multistep_order", 5)  #for CVODES and IDAS
        G.setOption("max_step_size", self.IDAS_MAX_STEP_SIZE)  #for IDAS only
        G.setOption("tf", self.DT)
        self.G = G

        #==============================================================================
        #        G.setOption('verbose',True)
        #        G.addMonitor('res')
        #        G.addMonitor('inputs')
        #        G.addMonitor('outputs')
        #G.addMonitor('djacB')
        #         G.addMonitor('bjacB')
        #         G.addMonitor('jtimesB')
        #         G.addMonitor('psetup')
        #         G.addMonitor('psetupB')
        #         G.addMonitor('psolveB')
        #         G.addMonitor('resB')
        #         G.addMonitor('resS')
        #         G.addMonitor('rhsQB')
        #==============================================================================
        G.init()

        self.n_u = self.ocp.u.size()
        self.n_x = self.ocp.x.size()
        self.n_v = self.n_u * self.n_k + self.n_x * self.n_k

        self.V = C.MX.sym("V", int(self.n_v), 1)
        self.U, self.X = self.splitVariables(self.V)

        uMin = C.vertcat([
            self.ocp.variable(self.ocp.u[i].getName()).min.getValue()
            for i in range(self.n_u)
        ]) / controlScaling
        uMax = C.vertcat([
            self.ocp.variable(self.ocp.u[i].getName()).max.getValue()
            for i in range(self.n_u)
        ]) / controlScaling
        UMIN = C.vertcat([uMin for k in range(self.n_k)])
        UMAX = C.vertcat([uMax for k in range(self.n_k)])

        xMin = C.vertcat([
            self.ocp.variable(self.ocp.x[i].getName()).min.getValue()
            for i in range(self.n_x)
        ]) / stateScaling
        xMax = C.vertcat([
            self.ocp.variable(self.ocp.x[i].getName()).max.getValue()
            for i in range(self.n_x)
        ]) / stateScaling
        XMIN = C.vertcat([xMin for k in range(self.n_k)])
        XMAX = C.vertcat([xMax for k in range(self.n_k)])

        if len(deltaUCons) > 0:
            addDeltaUCons = True
            deltaUCons = deltaUCons / self.controlScaling
        else:
            addDeltaUCons = False

        pathIn = C.daeIn(x=ocp.x, z=ocp.z, p=ocp.u, t=ocp.t)

        pathVarNames = [sv.getName() for sv in ocp.beq(ocp.path)]
        pathScaling = C.vertcat([ocp.nominal(pv) for pv in pathVarNames])

        pathS = C.substitute(
            ocp.beq(ocp.beq(ocp.path)), C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ])) / pathScaling
        pathConstraints = C.SXFunction(pathIn, [pathS])

        pathMax = C.vertcat([
            ocp.variable(pathVarNames[i]).max.getValue()
            for i in range(ocp.path.size())
        ]) / pathScaling
        pathMin = C.vertcat([
            ocp.variable(pathVarNames[i]).min.getValue()
            for i in range(ocp.path.size())
        ]) / pathScaling
        pathConstraints.setOption("name", "PATH")
        pathConstraints.init()

        pathConstraints.setInput(xOpt, 'x')
        pathConstraints.setInput([], 'z')
        pathConstraints.setInput(uOpt, 'p')
        pathConstraints.setInput(0, 't')
        pathConstraints.evaluate()
        pathOpt = pathConstraints.getOutput()

        optimalValues = {}

        print 'min <= (name,optimal,nominal) <= max'
        for i in range(self.n_x):
            print ocp.variable(
                ocp.x[i].getName()).min.getValue(), ' <= (', ocp.x[i].getName(
                ), ',', xOpt[i] * stateScaling[i], ',', stateScaling[
                    i], ') <= ', ocp.variable(
                        ocp.x[i].getName()).max.getValue()
            optimalValues[ocp.x[i].getName()] = xOpt[i] * stateScaling[i]

        for i in range(self.n_u):
            print ocp.variable(
                ocp.u[i].getName()).min.getValue(), ' <= (', ocp.u[i].getName(
                ), ',', uOpt[i] * controlScaling[i], ',', controlScaling[
                    i], ')  <= ', ocp.variable(
                        ocp.u[i].getName()).max.getValue()
            if addDeltaUCons:
                print -deltaUCons[i] * controlScaling[i], ' <= (Delta(', ocp.u[
                    i].getName(), ')/DTMPC,', 0, ',', controlScaling[
                        i], ')  <= ', deltaUCons[i] * controlScaling[i]

            optimalValues[ocp.u[i].getName()] = uOpt[i] * controlScaling[i]

        for i in range(len(pathVarNames)):
            print ocp.variable(pathVarNames[i]).min.getValue(
            ), ' <= (', pathVarNames[i], ',', pathOpt[i] * pathScaling[
                i], ',', pathScaling[i], ') <= ', ocp.variable(
                    pathVarNames[i]).max.getValue()
            optimalValues[pathVarNames[i]] = pathOpt[i] * pathScaling[i]

        plotTags = [ocp.x[i].getName() for i in range(ocp.x.size())]
        plotTags = plotTags + [ocp.u[i].getName() for i in range(ocp.u.size())]
        plotTags = plotTags + [sv.getName() for sv in ocp.beq(ocp.path)]

        self.plotTags = plotTags
        self.optimalValues = optimalValues

        # Constraint functions
        g = []
        g_min = []
        g_max = []

        self.XU0 = C.MX.sym("XU0", self.n_x + self.n_u, 1)
        Z = self.XU0[0:self.n_x]
        U0 = self.XU0[self.n_x:self.n_x + self.n_u]

        # Build up a graph of integrator calls
        obj = 0
        zf = C.vertcat([
            ocp.variable(ocp.z[k].getName()).start for k in range(ocp.z.size())
        ]) / algStateScaling
        for k in range(self.n_k):
            Z, QF, zf = C.integratorOut(
                G(C.integratorIn(x0=Z, p=self.U[k], z0=zf)), "xf", "qf", "zf")

            errU = self.U[k] - U0
            obj = obj + QF + C.mul(C.mul(errU.T, controlCost), errU)
            U0 = self.U[k]

            #          include MS constraints!
            g.append(Z - self.X[k])
            g_min.append(NP.zeros(self.n_x))
            g_max.append(NP.zeros(self.n_x))
            Z = self.X[k]

            [pathCons] = pathConstraints.call(
                C.daeIn(t=[], x=self.X[k], z=zf, p=self.U[k]))
            g.append(pathCons)  ## be carefull on giving all inputs
            g_max.append(pathMax)
            g_min.append(pathMin)
            if addDeltaUCons:
                g.append(errU)
                g_max.append(deltaUCons * DT)
                g_min.append(-deltaUCons * DT)

        #errU = (self.U[-1]-uOpt)
        #errX = self.X[-1]-xOpt
        #obj = obj + finalStateCost*C.mul((errX).trans(),(errX))+C.mul(C.mul(errU.T,controlCost),errU)
        self.obj = obj

        ### Constrains
        g = C.vertcat(g)

        nlp = C.MXFunction(C.nlpIn(x=self.V, p=self.XU0), C.nlpOut(f=obj, g=g))
        nlp.init()
        self.odeF = odeF
        self.nlp = nlp

        solver = C.NlpSolver('ipopt', nlp)

        # remove the comment to implement the hessian
        solver.setOption('hessian_approximation',
                         'limited-memory')  # comment for exact hessian
        solver.setOption('print_user_options', 'no')

        solver.setOption("tol", self.IPOPT_tol)  # IPOPT tolerance
        solver.setOption("dual_inf_tol",
                         self.IPOPT_dual_inf_tol)  #  dual infeasibility
        solver.setOption("constr_viol_tol",
                         self.IPOPT_constr_viol_tol)  # primal infeasibility
        solver.setOption("compl_inf_tol",
                         self.IPOPT_compl_inf_tol)  # complementarity
        #        solver.setOption("acceptable_tol",0.01)
        #        solver.setOption("acceptable_obj_change_tol",1e-6)
        #        solver.setOption("acceptable_constr_viol_tol",1e-6)
        solver.setOption("max_iter",
                         self.IPOPT_max_iter)  # IPOPT maximum iterations
        solver.setOption("print_level", self.IPOPT_print_level)
        solver.setOption("max_cpu_time",
                         self.IPOPT_max_cpu_time)  # IPOPT maximum iterations

        solver.init()

        ### Variable Bounds and initial guess
        solver.setInput(C.vertcat([UMIN, XMIN]), 'lbx')  # u_L
        solver.setInput(C.vertcat([UMAX, XMAX]), 'ubx')  # u_U
        solver.setInput(C.vertcat(g_min), 'lbg')  # g_L
        solver.setInput(C.vertcat(g_max), 'ubg')  # g_U

        self.solver = solver

        u0N = C.vertcat([
            self.ocp.variable(self.ocp.u[i].getName()).initialGuess.getValue()
            for i in range(self.n_u)
        ]) / controlScaling
        x0N = C.vertcat([
            self.ocp.variable(self.ocp.x[i].getName()).initialGuess.getValue()
            for i in range(self.n_x)
        ]) / stateScaling

        USOL, XSOL = self.forwardSimulation(x0N, u0N)

        self.USOL = USOL
        self.XSOL = XSOL
Ejemplo n.º 27
0
    def plotNLP(self, x0, DTplot=10, additionalplottags=None):

        plotH = []
        plotAdd = []
        plotT = []
        plotTags = self.plotTags

        ocp = self.ocp
        algStateScaling = self.algStateScaling
        controlScaling = self.controlScaling
        stateScaling = self.stateScaling
        DT = self.DT
        Z = x0 / self.stateScaling
        odeF = self.odeF
        xOpt = self.xOpt
        uOpt = self.uOpt

        C.Integrator.loadPlugin("idas")
        G = C.Integrator("idas", odeF)
        G.setOption("reltol", self.INTG_REL_TOL)  #for CVODES and IDAS
        G.setOption("abstol", self.INTG_ABS_TOL)  #for CVODES and IDAS
        G.setOption("max_multistep_order", 5)  #for CVODES and IDAS
        G.setOption("max_step_size", self.IDAS_MAX_STEP_SIZE)  #for IDAS only
        G.setOption("tf", DTplot)
        G.init()

        pathIn = C.daeIn(x=ocp.x, z=ocp.z, p=ocp.u, t=ocp.t)

        if additionalplottags is None:
            Config = ConfigParser.ConfigParser()
            Config.read('config.ini')
            additionalplottags = Config.get('MultipleShooting',
                                            'additionalplottags')
            additionalplottags = additionalplottags.split(',')

        self.additionalplottags = additionalplottags

        addTagScale = C.vertcat([ocp.nominal(pv) for pv in additionalplottags])
        tagsFsx = C.substitute(
            C.vertcat([ocp.beq(sv) for sv in additionalplottags]),
            C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ]))
        tagsF = C.SXFunction(pathIn, [tagsFsx])
        tagsF.init()
        tagsF.setInput(xOpt, 'x')
        tagsF.setInput([], 'z')
        tagsF.setInput(uOpt, 'p')
        tagsF.setInput(0, 't')
        tagsF.evaluate()
        addTagsOpt = tagsF.getOutput()

        for i in range(len(additionalplottags)):
            self.optimalValues[additionalplottags[i]] = addTagsOpt[i]

        sxPlotTags = C.vertcat([ocp.beq(tag) for tag in plotTags])
        pathS = C.substitute(
            sxPlotTags, C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ]))
        pathF = C.SXFunction(pathIn, [pathS, tagsFsx])
        pathF.init()

        nPlot = NP.int(NP.round(self.DT / DTplot))
        zf = C.vertcat([
            ocp.variable(ocp.z[k].getName()).start for k in range(ocp.z.size())
        ]) / algStateScaling
        for k in range(self.n_k):
            try:
                for jp in range(nPlot):
                    #Z,QF,zf = C.integratorOut(G(C.integratorIn(x0=Z,p=self.USOL[k],z0=zf)),"xf","qf","zf")
                    G.setInput(Z, 'x0')
                    G.setInput(self.USOL[k], 'p')
                    G.evaluate()
                    Z = G.getOutput('xf')
                    QF = G.getOutput('qf')
                    zf = G.getOutput('zf')
                    pathF.setInput(Z, 'x')
                    pathF.setInput(zf, 'z')
                    pathF.setInput(self.USOL[k], 'p')
                    t = k * DT + jp * DTplot
                    pathF.setInput(t, 't')
                    pathF.evaluate()
                    p = pathF.getOutput(0)
                    pAdd = pathF.getOutput(1)
                    plotH.append(p)
                    plotAdd.append(pAdd)
                    plotT.append(t)

                Z = self.XSOL[k]
            except:
                print 'something bad happened', sys.exc_info()[0]
                break

        plotDic = {}
        plotDic['t'] = plotT
        for si in range(len(plotTags)):
            plotDic[plotTags[si]] = [i[si] for i in plotH]
        for si in range(len(additionalplottags)):
            plotDic[additionalplottags[si]] = [i[si] for i in plotAdd]

        self.plotDic = plotDic
Ejemplo n.º 28
0
def multiple_shooting(model,
                      problem,
                      time_vector,
                      options=None,
                      unpack_objective=False):

    opts = {}
    opts['integration_opts'] = None
    opts['leq0_only'] = False
    opts['with_initial_cnss'] = True
    opts['with_final_cnss'] = True

    if not options is None:
        for k in options.keys():
            if k in opts:
                opts[k] = options[k]
            else:
                warn('Option not recognized : ' + k)

    nSteps = time_vector.numel() - 1
    tsym = model.t
    dtsym = model.dt

    # collect the parameters
    if model.p != []:
        pnum = len(model.p)
        pindxs = list(range(pnum))
        psyms = cs.vertcat(*[v.sym for v in model.p])

        pdata = []
        for k in range(pnum):
            pdata.append({'lob': cs.repmat(cs.reshape(model.p[k].lob,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.p[k].lob).numel())))[:nSteps+1],\
                          'upb': cs.repmat(cs.reshape(model.p[k].upb,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.p[k].upb).numel())))[:nSteps+1],\
                          'val': cs.repmat(cs.reshape(model.p[k].val,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.p[k].val).numel())))[:nSteps+1],\
                          'lbl': model.p[k].lbl+"<p>",\
                          'nme': model.p[k].nme
                          })

        plbl = ['<ocp_p>'] * pnum
        for i in range(pnum):
            if not model.p[i].lbl is None: plbl[i] += model.p[i].lbl

    else:
        pdata = []
        pnum = 0
        pindxs = []
        psyms = cs.MX()
        plbl = []

    # collect the states
    if model.x != []:
        xnum = len(model.x)
        xindxs = list(range(pnum, pnum + xnum))
        xsyms = cs.vertcat(*[v.sym for v in model.x])

        xdata = []
        for k in range(xnum):
            xdata.append({'lob': cs.repmat(cs.reshape(model.x[k].lob,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.x[k].lob).numel())))[:nSteps+1],\
                          'upb': cs.repmat(cs.reshape(model.x[k].upb,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.x[k].upb).numel())))[:nSteps+1],\
                          'val': cs.repmat(cs.reshape(model.x[k].val,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.x[k].val).numel())))[:nSteps+1],\
                          'lbl': model.x[k].lbl+"<x>",\
                          'nme': model.x[k].nme
                          })

        xlbl = ['<ocp_x>'] * xnum
        for i in range(xnum):
            if not model.x[i].lbl is None: xlbl[i] += model.x[i].lbl

    else:
        xdata = []
        xnum = 0
        xindxs = []
        xsyms = cs.MX()
        xlbl = []

    # collect the dicrete transition states
    if model.y != []:
        ynum = len(model.y)
        yindxs = list(range(pnum + xnum, pnum + xnum + ynum))
        ysyms = cs.vertcat(*[v.sym for v in model.y])

        ydata = []
        for k in range(ynum):
            ydata.append({'lob': cs.repmat(cs.reshape(model.y[k].lob,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.y[k].lob).numel())))[:nSteps+1],\
                          'upb': cs.repmat(cs.reshape(model.y[k].upb,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.y[k].upb).numel())))[:nSteps+1],\
                          'val': cs.repmat(cs.reshape(model.y[k].val,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.y[k].val).numel())))[:nSteps+1],\
                          'lbl': model.y[k].lbl+"<y>",\
                          'nme': model.y[k].nme
                          })

        ylbl = ['<ocp_y>'] * ynum
        for i in range(ynum):
            if not model.y[i].lbl is None: ylbl[i] += model.y[i].lbl

    else:
        ydata = []
        ynum = 0
        yindxs = []
        ysyms = cs.MX()
        ylbl = []

    # collect the slack variables
    if model.a != []:
        anum = len(model.a)
        aindxs = list(range(pnum + xnum + ynum, pnum + xnum + ynum + anum))
        asyms = cs.vertcat(*[v.sym for v in model.a])

        adata = []
        for k in range(anum):
            adata.append({'lob': cs.repmat(cs.reshape(model.a[k].lob,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.a[k].lob).numel())))[:nSteps+1],\
                          'upb': cs.repmat(cs.reshape(model.a[k].upb,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.a[k].upb).numel())))[:nSteps+1],\
                          'val': cs.repmat(cs.reshape(model.a[k].val,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.a[k].val).numel())))[:nSteps+1],\
                          'lbl': model.a[k].lbl+"<a>",\
                          'nme': model.a[k].nme
                          })

        albl = ['<ocp_a>'] * anum
        for i in range(anum):
            if not model.a[i].lbl is None: albl[i] += model.a[i].lbl

    else:
        adata = []
        anum = 0
        aindxs = []
        asyms = cs.MX()
        albl = []

    # collect the continuous controls
    if model.u != []:
        unum = len(model.u)
        uindxs = list(
            range(pnum + xnum + ynum + anum, pnum + xnum + ynum + anum + unum))
        usyms = cs.vertcat(*[v.sym for v in model.u])

        udata = []
        for k in range(unum):
            udata.append({'lob': cs.repmat(cs.reshape(model.u[k].lob,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.u[k].lob).numel())))[:nSteps+1],\
                          'upb': cs.repmat(cs.reshape(model.u[k].upb,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.u[k].upb).numel())))[:nSteps+1],\
                          'val': cs.repmat(cs.reshape(model.u[k].val,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.u[k].val).numel())))[:nSteps+1],\
                          'lbl': model.u[k].lbl+"<u>",\
                          'nme': model.u[k].nme
                          })

        ulbl = ['<ocp_u>'] * unum
        for i in range(unum):
            if not model.u[i].lbl is None: ulbl[i] += model.u[i].lbl

    else:
        udata = []
        unum = 0
        uindxs = []
        usyms = []
        ulbl = []

    # collect discrete controls
    if model.v != []:
        vnum = len(model.v)
        vindxs = list(
            range(pnum + xnum + ynum + anum + unum,
                  pnum + xnum + ynum + anum + unum + vnum))
        vsyms = cs.vertcat(*[v.sym for v in model.v])

        vdata = []
        for k in range(vnum):
            vdata.append({'lob': cs.repmat(cs.reshape(model.v[k].lob,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.v[k].lob).numel())))[:nSteps+1],\
                          'upb': cs.repmat(cs.reshape(model.v[k].upb,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.v[k].upb).numel())))[:nSteps+1],\
                          'val': cs.repmat(cs.reshape(model.v[k].val,1,-1),1,int(cs.ceil((nSteps+1)/cs.DM(model.v[k].val).numel())))[:nSteps+1],\
                          'lbl': model.v[k].lbl+"<v>",\
                          'nme': model.v[k].nme
                          })

        vlbl = ['<ocp_v>'] * vnum
        for i in range(vnum):
            if not model.v[i].lbl is None: vlbl[i] += model.v[i].lbl

    else:
        vdata = []
        vnum = 0
        vindxs = []
        vsyms = []
        vlbl = []

    # collect the external inputs
    if model.i != []:
        isyms = cs.vertcat(*[v.sym for v in model.i])
        idata = cs.vertcat(*[v.val.T for v in model.i])
    else:
        isyms = cs.DM()
        idata = cs.DM(0, nSteps + 1)

    # create the set of variables for multiple shooting
    syms = cs.vertcat(psyms, xsyms, ysyms, asyms, usyms, vsyms)
    vardata = pdata + xdata + ydata + adata + udata + vdata
    numVars = len(vardata)

    # construct the problem variables
    problem.var['sym'] = cs.MX.sym(
        'V', nSteps * numVars + pnum + xnum + ynum + anum, 1)
    problem.var['lob'] = cs.DM(
        cs.reshape(cs.vertcat(*[vardata[k]['lob'] for k in range(numVars)]),
                   -1, 1)[:nSteps * numVars + anum + ynum + xnum + pnum])
    problem.var['upb'] = cs.DM(
        cs.reshape(cs.vertcat(*[vardata[k]['upb'] for k in range(numVars)]),
                   -1, 1)[:nSteps * numVars + anum + ynum + xnum + pnum])
    problem.var['val'] = cs.DM(
        cs.reshape(cs.vertcat(*[vardata[k]['val'] for k in range(numVars)]),
                   -1, 1)[:nSteps * numVars + anum + ynum + xnum + pnum])
    problem.var['dsc'] = ([False] *
                          (pnum + xnum + ynum + anum + unum) + [True] * vnum
                          ) * nSteps + [False] * (pnum + xnum + ynum + anum)
    problem.var['nme'] = [
        v['nme'] for v in pdata + xdata + ydata + adata + udata + vdata
    ] * nSteps + [v['nme'] for v in pdata + xdata + ydata + adata]
    problem.var['lbl'] = [
        v['lbl'] for v in pdata + xdata + ydata + adata + udata + vdata
    ] * nSteps + [v['lbl'] for v in pdata + xdata + ydata + adata]
    problem.var['lam'] = None

    # collect data for multiple shooting
    shooting = {}
    shooting['t'] = time_vector.T
    shooting['dt'] = shooting['t'][1:] - shooting['t'][:-1]
    shooting['i'] = idata

    shooting['p'] = cs.reshape(
        problem.var['sym'][[
            numVars * i + j for i in range(nSteps + 1) for j in pindxs
        ]], (pnum, nSteps + 1))
    shooting['x'] = cs.reshape(
        problem.var['sym'][[
            numVars * i + j for i in range(nSteps + 1) for j in xindxs
        ]], (xnum, nSteps + 1))
    shooting['y'] = cs.reshape(
        problem.var['sym'][[
            numVars * i + j for i in range(nSteps + 1) for j in yindxs
        ]], (ynum, nSteps + 1))
    shooting['a'] = cs.reshape(
        problem.var['sym'][[
            numVars * i + j for i in range(nSteps + 1) for j in aindxs
        ]], (anum, nSteps + 1))
    shooting['u'] = cs.reshape(
        problem.var['sym'][[
            numVars * i + j for i in range(nSteps) for j in uindxs
        ]], (unum, nSteps))
    shooting['v'] = cs.reshape(
        problem.var['sym'][[
            numVars * i + j for i in range(nSteps) for j in vindxs
        ]], (vnum, nSteps))

    # collect the constraints
    labels_tmp = []
    problem.cns = {
        'exp': cs.MX(),
        'lob': cs.DM(),
        'upb': cs.DM(),
        'lbl': [],
        'lam': None
    }

    # add path constraints
    if model.pcns != []:

        if not opts['leq0_only']:
            pcnsf = cs.Function('pcns', [
                cs.vertcat(
                    tsym,
                    psyms,
                    xsyms,
                    ysyms,
                    asyms,
                    isyms,
                    usyms,
                    vsyms,
                    dtsym,
                )
            ], [cs.vertcat(*[c.exp for c in model.pcns])])
            pcnsf = pcnsf.map(nSteps)

            problem.cns['exp'] = cs.vertcat(
                problem.cns['exp'],
                pcnsf(
                    cs.vertcat(shooting['t'][:-1], shooting['x'][:, :-1],
                               shooting['y'][:, :-1], shooting['a'][:, :-1],
                               shooting['p'][:, :-1], shooting['i'][:, :-1],
                               shooting['u'], shooting['v'], shooting['dt'])))

            problem.cns['lob'] = cs.vertcat(
                problem.cns['lob'],
                cs.repmat(cs.vertcat(*[c.lob for c in model.pcns]),
                          (1, nSteps)))
            problem.cns['upb'] = cs.vertcat(
                problem.cns['upb'],
                cs.repmat(cs.vertcat(*[c.upb for c in model.pcns]),
                          (1, nSteps)))

            labels_tmp += [
                model.pcns[i].lbl + '<ocp_pc' + str(i) + '>'
                for i in range(len(model.pcns))
            ]
        else:
            pcnsexp = cs.vertcat(*([c.lob - c.exp for c in model.pcns if c.lob > -cs.DM.inf()] +\
                                   [c.exp - c.upb for c in model.pcns if c.upb <  cs.DM.inf()]))

            pcnsf = cs.Function('pcns', [
                cs.vertcat(tsym, psyms, xsyms, ysyms, asyms, isyms, usyms,
                           vsyms, dtsym)
            ], [pcnsexp])
            pcnsf = pcnsf.map(nSteps)

            problem.cns['exp'] = cs.vertcat(
                problem.cns['exp'],
                pcnsf(
                    cs.vertcat(shooting['t'][:-1], shooting['x'][:, :-1],
                               shooting['y'][:, :-1], shooting['a'][:, :-1],
                               shooting['p'][:, :-1], shooting['i'][:, :-1],
                               shooting['u'], shooting['v'], shooting['dt'])))
            problem.cns['lob'] = cs.vertcat(
                problem.cns['lob'], -cs.DM.inf(pcnsexp.shape[0], nSteps))
            problem.cns['upb'] = cs.vertcat(
                problem.cns['upb'], cs.DM.zeros(pcnsexp.shape[0], nSteps))

            labels_tmp += [model.pcns[i].lbl + '<ocp_pc'+str(i)+'>' for i,c in enumerate(model.pcns) if c.lob > -cs.DM.inf()] +\
                          [model.pcns[i].lbl + '<ocp_pc'+str(i)+'>' for i,c in enumerate(model.pcns) if c.upb <  cs.DM.inf()]

    # parameters are treated like states with zero dynamics
    if model.p != []:
        # add constraints
        problem.cns['exp'] = cs.vertcat(
            problem.cns['exp'], shooting['p'][:, :-1] - shooting['p'][:, 1:])
        problem.cns['lob'] = cs.vertcat(problem.cns['lob'],
                                        cs.DM.zeros(pnum, nSteps))
        problem.cns['upb'] = cs.vertcat(problem.cns['upb'],
                                        cs.DM.zeros(pnum, nSteps))

        # collect shooting labels
        labels_tmp += ['<ocp_par' + str(i) + '>' for i in range(pnum)]

    # dynamic constraints
    if model.ode != []:
        # define integrator
        intg = integrate_ode(
            xsyms, cs.vertcat(tsym, ysyms, asyms, psyms, isyms, usyms, vsyms),
            model.ode, dtsym, opts['integration_opts'])

        # evaluate integration
        intg = intg.map(nSteps)
        intgval = intg.call({
            'x0':
            shooting['x'][:, :-1],
            'p':
            cs.vertcat(shooting['t'][:-1], shooting['y'][:, :-1],
                       shooting['a'][:, :-1], shooting['p'][:, :-1],
                       shooting['i'][:, :-1], shooting['u'], shooting['v'],
                       shooting['dt'])
        })

        # add integration constraints
        if not opts['leq0_only']:
            problem.cns['exp'] = cs.vertcat(
                problem.cns['exp'], shooting['x'][:, 1:] - intgval['xf'])
            problem.cns['lob'] = cs.vertcat(problem.cns['lob'],
                                            cs.DM.zeros(xnum, nSteps))
            problem.cns['upb'] = cs.vertcat(problem.cns['upb'],
                                            cs.DM.zeros(xnum, nSteps))

            # classify the resulting constraints
            tmp_order_a = get_order(cs.vertcat(*model.ode), [
                syms[i] for i in range(pnum + xnum + ynum + anum + unum + vnum)
            ])  # global order
            tmp_order_c = get_order(
                cs.vertcat(*model.ode),
                [syms[i] for i in range(pnum + xnum + ynum + anum + unum)
                 ])  # order considering the continuous variables only
            labels_tmp += [
                '<ocp_int' + str(i) + '>' + '<semi_cvx>' *
                int(tmp_order_a[i] > 1) * int(tmp_order_c[i] <= 1) +
                '<non-convex>' * int(tmp_order_c[i] > 1) for i in range(xnum)
            ]

        else:
            problem.cns['exp'] = cs.vertcat(
                problem.cns['exp'], shooting['x'][:, 1:] - intgval['xf'],
                intgval['xf'] - shooting['x'][:, 1:])
            problem.cns['lob'] = cs.vertcat(problem.cns['lob'],
                                            -cs.DM.inf(2 * xnum, nSteps))
            problem.cns['upb'] = cs.vertcat(problem.cns['upb'],
                                            cs.DM.zeros(2 * xnum, nSteps))

            # classify the resulting constraints
            tmp_order_a = get_order(cs.vertcat(*model.ode), [
                syms[i] for i in range(pnum + xnum + ynum + anum + unum + vnum)
            ])  # global order
            tmp_order_c = get_order(
                cs.vertcat(*model.ode),
                [syms[i] for i in range(pnum + xnum + ynum + anum + unum)
                 ])  # order considering the continuous variables only
            labels_tmp += 2 * [
                '<ocp_int' + str(i) + '>' + '<semi_cvx>' *
                int(tmp_order_a[i] > 1) * int(tmp_order_c[i] <= 1) +
                '<non-convex>' * int(tmp_order_c[i] > 1) for i in range(xnum)
            ]

    # constraints for discrete transitions
    if model.itr != []:
        # define discrete transition
        itr = cs.Function('itr', [
            cs.vertcat(tsym, psyms, xsyms, ysyms, asyms, isyms, usyms, vsyms,
                       dtsym)
        ], [cs.vertcat(*model.itr)])

        # evaluate the discrete transitions
        itr = itr.map(nSteps)
        trans = itr(
            cs.vertcat(shooting['t'][:-1], shooting['x'][:, :-1],
                       shooting['y'][:, :-1], shooting['a'][:, :-1],
                       shooting['p'][:, :-1], shooting['i'][:, :-1],
                       shooting['u'], shooting['v'], shooting['dt']))

        # add discrete transition constraints
        if not opts['leq0_only']:
            problem.cns['exp'] = cs.vertcat(
                problem.cns['exp'],
                shooting['y'][:, 1:] - shooting['y'][:, :-1] - trans)
            problem.cns['lob'] = cs.vertcat(problem.cns['lob'],
                                            cs.DM.zeros(trans.size()))
            problem.cns['upb'] = cs.vertcat(problem.cns['upb'],
                                            cs.DM.zeros(trans.size()))

            # classify the resulting constraints
            tmp_order_a = get_order(cs.vertcat(*model.itr), [
                syms[i] for i in range(pnum + xnum + ynum + anum + unum + vnum)
            ])  # global order
            tmp_order_c = get_order(
                cs.vertcat(*model.itr),
                [syms[i] for i in range(pnum + xnum + ynum + anum + unum)
                 ])  # order considering the continuous variables only
            labels_tmp += [
                '<ocp_dst' + str(i) + '>' + '<semi_cvx>' *
                int(tmp_order_a[i] > 1) * int(tmp_order_c[i] <= 1) +
                '<non-convex>' * int(tmp_order_c[i] > 1) for i in range(ynum)
            ]

        else:

            problem.cns['exp'] = cs.vertcat(
                problem.cns['exp'],
                shooting['y'][:, 1:] - shooting['y'][:, :-1] - trans,
                -shooting['y'][:, 1:] + shooting['y'][:, :-1] + trans)
            problem.cns['lob'] = cs.vertcat(
                problem.cns['lob'],
                -cs.DM.inf(trans.shape[0] * 2, trans.shape[1]))
            problem.cns['upb'] = cs.vertcat(
                problem.cns['upb'],
                cs.DM.zeros(trans.shape[0] * 2, trans.shape[1]))

            # classify the resulting constraints
            tmp_order_a = get_order(cs.vertcat(*model.itr), [
                syms[i] for i in range(pnum + xnum + ynum + anum + unum + vnum)
            ])  # global order
            tmp_order_c = get_order(
                cs.vertcat(*model.itr),
                [syms[i] for i in range(pnum + xnum + ynum + anum + unum)
                 ])  # order considering the continuous variables only
            labels_tmp += 2 * [
                '<ocp_dst' + str(i) + '>' + '<semi_cvx>' *
                int(tmp_order_a[i] > 1) * int(tmp_order_c[i] <= 1) +
                '<non-convex>' * int(tmp_order_c[i] > 1) for i in range(ynum)
            ]

    # reshape the constraints vector and add labels
    problem.cns['exp'] = cs.reshape(problem.cns['exp'], (-1, 1))
    problem.cns['lob'] = cs.reshape(problem.cns['lob'], (-1, 1))
    problem.cns['upb'] = cs.reshape(problem.cns['upb'], (-1, 1))
    problem.cns['lbl'] = labels_tmp * nSteps

    # add initial constraints
    if model.icns != [] and opts['with_initial_cnss']:
        if not opts['leq0_only']:
            problem.cns['exp'] = cs.vertcat(cs.substitute(cs.vertcat(*[c.exp for c in model.icns]),\
                                                    cs.vertcat(psyms,xsyms,ysyms,asyms,isyms),\
                                                    cs.vertcat(shooting['p'][:,0],shooting['x'][:,0],shooting['y'][:,0],shooting['a'][:,0],shooting['i'][:,0])),\
                                      problem.cns['exp'])

            problem.cns['lob'] = cs.vertcat(
                cs.vertcat(*[c.lob for c in model.icns]), problem.cns['lob'])
            problem.cns['upb'] = cs.vertcat(
                cs.vertcat(*[c.upb for c in model.icns]), problem.cns['upb'])
            problem.cns['lbl'] = [
                model.icns[i].lbl + '<ocp_ic' + str(i) + '>'
                for i in range(len(model.icns))
            ] + problem.cns['lbl']
        else:
            icnsexp = cs.vertcat(*([c.lob - c.exp for c in model.icns if c.lob > -cs.DM.inf()]+\
                                   [c.exp - c.upb for c in model.icns if c.upb <  cs.DM.inf()]))

            problem.cns['exp'] = cs.vertcat(cs.substitute(icnsexp,\
                                                    cs.vertcat(psyms,xsyms,ysyms,asyms,isyms),\
                                                    cs.vertcat(shooting['p'][:,0],shooting['x'][:,0],shooting['y'][:,0],shooting['a'][:,0],shooting['i'][:,0])),\
                                      problem.cns['exp'])

            problem.cns['lob'] = cs.vertcat(-cs.DM.inf(icnsexp.shape[0], 1),
                                            problem.cns['lob'])
            problem.cns['upb'] = cs.vertcat(cs.DM.zeros(icnsexp.shape[0], 1),
                                            problem.cns['upb'])
            problem.cns['lbl'] = [c.lbl + '<ocp_ic'+str(i)+'>' for i,c in enumerate(model.icns) if c.lob > -cs.DM.inf()] +\
                           [c.lbl + '<ocp_ic'+str(i)+'>' for i,c in enumerate(model.icns) if c.upb <  cs.DM.inf()] +\
                           problem.cns['lbl']

    # add final constraints
    if model.fcns != [] and opts['with_final_cnss']:
        if not opts['leq0_only']:
            problem.cns['exp'] = cs.vertcat(problem.cns['exp'],\
                                      cs.substitute(cs.vertcat(*[c.exp for c in model.fcns]),\
                                                    cs.vertcat(psyms,xsyms,ysyms,asyms,isyms),\
                                                    cs.vertcat(shooting['p'][:,-1],shooting['x'][:,-1],shooting['y'][:,-1],shooting['a'][:,-1],shooting['i'][:,-1])))

            problem.cns['lob'] = cs.vertcat(
                problem.cns['lob'], cs.vertcat(*[c.lob for c in model.fcns]))
            problem.cns['upb'] = cs.vertcat(
                problem.cns['upb'], cs.vertcat(*[c.upb for c in model.fcns]))
            problem.cns['lbl'] = problem.cns['lbl'] + [
                model.fcns[i].lbl + '<ocp_fc' + str(i) + '>'
                for i in range(len(model.fcns))
            ]

        else:
            fcnsexp = cs.vertcat(*([c.lob - c.exp for c in model.fcns if c.lob > -cs.DM.inf()]+\
                                   [c.exp - c.upb for c in model.fcns if c.upb <  cs.DM.inf()]))

            problem.cns['exp'] = cs.vertcat(problem.cns['exp'],\
                                      cs.substitute(fcnsexp,\
                                                    cs.vertcat(psyms,xsyms,ysyms,asyms,isyms),\
                                                    cs.vertcat(shooting['p'][:,-1],shooting['x'][:,-1],shooting['y'][:,-1],shooting['a'][:,-1],shooting['i'][:,-1])))

            problem.cns['lob'] = cs.vertcat(problem.cns['lob'],
                                            -cs.DM.inf(fcnsexp.shape[0], 1))
            problem.cns['upb'] = cs.vertcat(problem.cns['upb'],
                                            cs.DM.zeros(fcnsexp.shape[0], 1))
            problem.cns['lbl'] = problem.cns['lbl'] +\
                            [c.lbl + '<ocp_ic'+str(i)+'>' for i,c in enumerate(model.fcns) if c.lob > -cs.DM.inf()] +\
                            [c.lbl + '<ocp_ic'+str(i)+'>' for i,c in enumerate(model.fcns) if c.upb <  cs.DM.inf()]

    ## Construct the objective
    if unpack_objective:
        problem.obj = {'exp': cs.MX.zeros(shooting['t'].numel()), 'val': None}
    else:
        problem.obj = {'exp': cs.MX(0.0), 'val': None}

    # add lagrangian objective
    if not model.lag is None:

        # define integrator
        l0 = cs.MX.sym('l0')
        intg = integrate_ode(
            l0,
            cs.vertcat(tsym, psyms, xsyms, ysyms, asyms, isyms, usyms, vsyms),
            [model.lag], dtsym, opts['integration_opts']).map(nSteps)

        # evaluate integration
        tmp = intg.call({'x0':cs.DM.zeros(nSteps).T,\
                         'p':cs.vertcat(shooting['t'][:-1],shooting['x'][:,:-1],shooting['y'][:,:-1],shooting['a'][:,:-1],shooting['p'][:,:-1],shooting['i'][:,:-1],shooting['u'],shooting['v'],shooting['dt'])})['xf']

        if unpack_objective:
            problem.obj['exp'][:-1] += tmp.T
        else:
            problem.obj['exp'] += cs.sum2(tmp)

    # add discrete penalties
    if not model.ipn is None:
        ipnf = cs.Function('ipn', [
            cs.vertcat(tsym, psyms, xsyms, ysyms, asyms, isyms, usyms, vsyms,
                       dtsym)
        ], [model.ipn]).map(nSteps)
        tmp = ipnf(
            cs.vertcat(shooting['t'][:-1], shooting['x'][:, :-1],
                       shooting['y'][:, :-1], shooting['a'][:, :-1],
                       shooting['p'][:, :-1], shooting['i'][:, :-1],
                       shooting['u'], shooting['v'], shooting['dt']))

        if unpack_objective:
            problem.obj['exp'][:-1] += tmp.T
        else:
            problem.obj['exp'] += cs.sum2(tmp)

    # add mayer objective
    if not model.may is None:

        tmp = cs.vertcat(problem.obj['exp'],cs.substitute(model.may,\
                                                          cs.vertcat(tsym,psyms,xsyms,ysyms,asyms,isyms),\
                                                          cs.vertcat(shooting['t'][:,-1],shooting['p'][:,-1],shooting['x'][:,-1],shooting['y'][:,-1],shooting['a'][:,-1],shooting['i'][:,-1])))
        tmp = cs.substitute(model.may,\
                            cs.vertcat(tsym,psyms,xsyms,ysyms,asyms,isyms),\
                            cs.vertcat(shooting['t'][:,-1],shooting['p'][:,-1],shooting['x'][:,-1],shooting['y'][:,-1],shooting['a'][:,-1],shooting['i'][:,-1]))

        if unpack_objective:
            problem.obj['exp'][-1] += tmp
        else:
            problem.obj['exp'] += tmp

    # add sos1 constraints
    if not model.sos1 is None:
        problem.sos1 = []
        varsname = [v['nme'] for v in vardata]
        for c in range(len(model.sos1)):
            indxs = [
                i for i in range(len(varsname))
                if varsname[i] in model.sos1[c].group
            ]
            if model.sos1[c].weights != []:
                problem.sos1.append({'g': [[t*numVars + i for i in indxs] for t in range(nSteps)],\
                                     'w': [cs.substitute(model.sos1[c].weights,cs.vertcat(tsym,psyms,xsyms,ysyms,asyms,isyms),cs.vertcat(shooting['t'][:,-1],shooting['p'][:,-1],shooting['x'][:,-1],shooting['y'][:,-1],shooting['a'][:,-1],shooting['i'][:,-1])) for t in range(nSteps)]})
            else:
                problem.sos1.append({
                    'g':
                    [[t * numVars + i for i in indxs] for t in range(nSteps)],
                    'w': [None] * nSteps
                })

    return
    def _create_jacobian_functions(self):
        """
        Calculate the Jacobian functions of a DAE represented by an 
        OptimizationProblem object. The DAE is represented by
        
        F(dx,x,u,c,t) = 0
    
        The matrices are computed by evaluating Jacobians with CasADi. 
        (That is, no numerical finite differences are used in the 
        linearization.)
    
        The following functions are created:
    
        dF_dxdot:
            The derivative with respect to the derivative of the 
            state variables.
    
        dF_dx:
            The derivative with respect to the state variables.
    
        dF_dc:
            The derivative function with respect to the algebraic 
            variables.
    
        dF_du:
            The derivative with respect to the control signals.
        """
        #Make sure every parameter has a value
        self.op.calculateValuesForDependentParameters()

        self._mvar_vectors = {'dx': N.array([self.op.getVariable('der(' + \
                                            name + ')') for name in self._state_names]),
                              'x': N.array([self.op.getVariable(name) \
                                           for name in self._state_names]),
                              'u': N.array([self.op.getVariable(name) \
                                           for name in self._all_input_names]),
                              'c': N.array([self.op.getVariable(name) \
                                           for name in self._alg_var_names])}

        self._nvar = {
            'dx': len(self._mvar_vectors["dx"]),
            'x': len(self._mvar_vectors["x"]),
            'u': len(self._mvar_vectors["u"]),
            'c': len(self._mvar_vectors["c"])
        }

        # Sort parameters
        par_kinds = [
            self.op.BOOLEAN_CONSTANT, self.op.BOOLEAN_PARAMETER_DEPENDENT,
            self.op.BOOLEAN_PARAMETER_INDEPENDENT, self.op.INTEGER_CONSTANT,
            self.op.INTEGER_PARAMETER_DEPENDENT,
            self.op.INTEGER_PARAMETER_INDEPENDENT, self.op.REAL_CONSTANT,
            self.op.REAL_PARAMETER_INDEPENDENT,
            self.op.REAL_PARAMETER_DEPENDENT
        ]

        pars = reduce(
            list.__add__,
            [list(self.op.getVariables(par_kind)) for par_kind in par_kinds])
        self._mvar_vectors['p_fixed'] = [
            par for par in pars if not self.op.get_attr(par, "free")
        ]

        # Create named symbolic variable structure
        named_mvar_struct = OrderedDict()
        named_mvar_struct["time"] = [self.op.getTimeVariable()]
        named_mvar_struct["dx"] = \
            [mvar.getVar() for mvar in self._mvar_vectors['dx']]
        named_mvar_struct["x"] = \
            [mvar.getVar() for mvar in self._mvar_vectors['x']]
        named_mvar_struct["c"] = \
            [mvar.getVar() for mvar in self._mvar_vectors['c']]
        named_mvar_struct["u"] = \
            [mvar.getVar() for mvar in self._mvar_vectors['u']]

        # Substitute named variables with vector variables in expressions
        named_vars = reduce(list.__add__, named_mvar_struct.values())
        self._mvar_struct = OrderedDict()
        self._mvar_struct["time"] = casadi.MX.sym("time")
        self._mvar_struct["dx"] = casadi.MX.sym("dx", self._nvar['dx'])
        self._mvar_struct["x"] = casadi.MX.sym("x", self._nvar['x'])
        self._mvar_struct["c"] = casadi.MX.sym("c", self._nvar['c'])
        self._mvar_struct["u"] = casadi.MX.sym("u", self._nvar['u'])
        svector_vars = [self._mvar_struct["time"]]

        # Create map from name to variable index and type
        self._name_map = {}
        for vt in ["dx", "x", "c", "u"]:
            i = 0
            for var in self._mvar_vectors[vt]:
                name = var.getName()
                self._name_map[name] = (i, vt)
                svector_vars.append(self._mvar_struct[vt][i])
                i = i + 1

        # DAEResidual in terms of the substituted variables
        self._dae = casadi.substitute([self.op.getDaeResidual()], named_vars,
                                      svector_vars)
        # Get parameter values
        par_vars = [par.getVar() for par in self._mvar_vectors['p_fixed']]
        par_vals = [
            self.op.get_attr(par, "_value")
            for par in self._mvar_vectors['p_fixed']
        ]

        # Substitute non-free parameters in expressions for their values
        DAE = casadi.substitute(self._dae, par_vars, par_vals)
        # Defines the DAEResidual Function
        self.Fdae = casadi.MXFunction([
            self._mvar_struct["time"], self._mvar_struct["dx"],
            self._mvar_struct["x"], self._mvar_struct["c"],
            self._mvar_struct["u"]
        ], DAE)

        self.Fdae.init()
        # Define derivatives
        self.dF_dxdot = self.Fdae.jacobian(1, 0)
        self.dF_dxdot.init()
        self.dF_dx = self.Fdae.jacobian(2, 0)
        self.dF_dx.init()
        self.dF_dc = self.Fdae.jacobian(3, 0)
        self.dF_dc.init()
        self.dF_du = self.Fdae.jacobian(4, 0)
        self.dF_du.init()
Ejemplo n.º 30
0
    def simplify(self, options):
        if options.get('replace_parameter_expressions', False):
            logger.info("Replacing parameter expressions")

            simple_parameters, symbols, values = [], [], []
            for p in self.parameters:
                value = ca.MX(p.value)
                if value.is_constant():
                    simple_parameters.append(p)
                else:
                    symbols.append(p.symbol)
                    values.append(value)

            self.parameters = simple_parameters

            if len(values) > 0:
                # Resolve expressions that include other, non-simple parameter
                # expressions.
                converged = False
                while not converged:
                    new_values = ca.substitute(values, symbols, values)
                    converged = ca.is_equal(ca.veccat(*values),
                                            ca.veccat(*new_values),
                                            CASADI_COMPARISON_DEPTH)
                    values = new_values

                if len(self.equations) > 0:
                    self.equations = ca.substitute(self.equations, symbols,
                                                   values)
                if len(self.initial_equations) > 0:
                    self.initial_equations = ca.substitute(
                        self.initial_equations, symbols, values)

                # Replace parameter expressions in metadata
                for variable in itertools.chain(self.states, self.alg_states,
                                                self.inputs, self.parameters,
                                                self.constants):
                    for attribute in CASADI_ATTRIBUTES:
                        value = getattr(variable, attribute)
                        if isinstance(value,
                                      ca.MX) and not value.is_constant():
                            [value] = ca.substitute([value], symbols, values)
                            setattr(variable, attribute, value)

        if options.get('replace_constant_expressions', False):
            logger.info("Replacing constant expressions")

            simple_constants, symbols, values = [], [], []
            for c in self.constants:
                value = ca.MX(c.value)
                if value.is_constant():
                    simple_constants.append(c)
                else:
                    symbols.append(c.symbol)
                    values.append(value)

            self.constants = simple_constants

            if len(values) > 0:
                # Resolve expressions that include other, non-simple parameter
                # expressions.
                converged = False
                while not converged:
                    new_values = ca.substitute(values, symbols, values)
                    converged = ca.is_equal(ca.veccat(*values),
                                            ca.veccat(*new_values),
                                            CASADI_COMPARISON_DEPTH)
                    values = new_values

                if len(self.equations) > 0:
                    self.equations = ca.substitute(self.equations, symbols,
                                                   values)
                if len(self.initial_equations) > 0:
                    self.initial_equations = ca.substitute(
                        self.initial_equations, symbols, values)

                # Replace constant expressions in metadata
                for variable in itertools.chain(self.states, self.alg_states,
                                                self.inputs, self.parameters,
                                                self.constants):
                    for attribute in CASADI_ATTRIBUTES:
                        value = getattr(variable, attribute)
                        if isinstance(value,
                                      ca.MX) and not value.is_constant():
                            [value] = ca.substitute([value], symbols, values)
                            setattr(variable, attribute, value)

        if options.get('eliminate_constant_assignments', False):
            logger.info("Elimating constant variable assignments")

            alg_states = OrderedDict([(s.symbol.name(), s)
                                      for s in self.alg_states])

            reduced_equations = []
            for eq in self.equations:
                if eq.is_symbolic() and eq.name() in alg_states:
                    constant = alg_states.pop(eq.name())
                    constant.value = 0.0

                    self.constants.append(constant)

                    # Skip this equation
                    continue

                if eq.n_dep() == 2 and (eq.is_op(ca.OP_SUB)
                                        or eq.is_op(ca.OP_ADD)):
                    if eq.dep(0).is_symbolic() and eq.dep(0).name(
                    ) in alg_states and eq.dep(1).is_constant():
                        variable = eq.dep(0)
                        value = eq.dep(1)
                    elif eq.dep(1).is_symbolic() and eq.dep(1).name(
                    ) in alg_states and eq.dep(0).is_constant():
                        variable = eq.dep(1)
                        value = eq.dep(0)
                    else:
                        variable = None
                        value = None

                    if variable is not None:
                        constant = alg_states.pop(variable.name())

                        if eq.is_op(ca.OP_SUB):
                            constant.value = value
                        else:
                            constant.value = -value

                        self.constants.append(constant)

                        # Skip this equation
                        continue

                # Keep this equation
                reduced_equations.append(eq)

            # Eliminate alias variables
            self.alg_states = list(alg_states.values())
            self.equations = reduced_equations

        if options.get('replace_parameter_values', False):
            logger.info("Replacing parameter values")

            # N.B. Any parameter expression elimination must be done first.
            unspecified_parameters, symbols, values = [], [], []
            for p in self.parameters:
                if ca.MX(p.value).is_constant() and ca.MX(
                        p.value).is_regular():
                    symbols.append(p.symbol)
                    values.append(p.value)
                else:
                    unspecified_parameters.append(p)

            if len(self.equations) > 0:
                self.equations = ca.substitute(self.equations, symbols, values)
            if len(self.initial_equations) > 0:
                self.initial_equations = ca.substitute(self.initial_equations,
                                                       symbols, values)
            self.parameters = unspecified_parameters

            # Replace parameter values in metadata
            for variable in itertools.chain(self.states, self.alg_states,
                                            self.inputs, self.parameters,
                                            self.constants):
                for attribute in CASADI_ATTRIBUTES:
                    value = getattr(variable, attribute)
                    if isinstance(value, ca.MX) and not value.is_constant():
                        [value] = ca.substitute([value], symbols, values)
                        setattr(variable, attribute, value)

        if options.get('replace_constant_values', False):
            logger.info("Replacing constant values")

            # N.B. Any parameter expression elimination must be done first.
            symbols = self._symbols(self.constants)
            values = [v.value for v in self.constants]
            if len(self.equations) > 0:
                self.equations = ca.substitute(self.equations, symbols, values)
            if len(self.initial_equations) > 0:
                self.initial_equations = ca.substitute(self.initial_equations,
                                                       symbols, values)
            self.constants = []

            # Replace constant values in metadata
            for variable in itertools.chain(self.states, self.alg_states,
                                            self.inputs, self.parameters,
                                            self.constants):
                for attribute in CASADI_ATTRIBUTES:
                    value = getattr(variable, attribute)
                    if isinstance(value, ca.MX) and not value.is_constant():
                        [value] = ca.substitute([value], symbols, values)
                        setattr(variable, attribute, value)

        if options.get('eliminable_variable_expression', None) is not None:
            logger.info(
                "Elimating variables that match the regular expression {}".
                format(options['eliminable_variable_expression']))

            p = re.compile(options['eliminable_variable_expression'])

            alg_states = OrderedDict([(s.symbol.name(), s)
                                      for s in self.alg_states])

            variables = []
            values = []

            reduced_equations = []
            for eq in self.equations:
                if eq.is_symbolic() and eq.name() in alg_states and p.match(
                        eq.name()):
                    variables.append(eq)
                    values.append(0.0)
                    del alg_states[eq.name()]
                    # Skip this equation
                    continue

                if eq.n_dep() == 2 and (eq.is_op(ca.OP_SUB)
                                        or eq.is_op(ca.OP_ADD)):
                    if eq.dep(0).is_symbolic() and eq.dep(0).name(
                    ) in alg_states and p.match(eq.dep(0).name()):
                        variable = eq.dep(0)
                        value = eq.dep(1)
                    elif eq.dep(1).is_symbolic() and eq.dep(1).name(
                    ) in alg_states and p.match(eq.dep(1).name()):
                        variable = eq.dep(1)
                        value = eq.dep(0)
                    else:
                        variable = None
                        value = None

                    if variable is not None:
                        del alg_states[variable.name()]

                        variables.append(variable)
                        if eq.is_op(ca.OP_SUB):
                            values.append(value)
                        else:
                            values.append(-value)

                        # Skip this equation
                        continue

                # Keep this equation
                reduced_equations.append(eq)

            # Eliminate alias variables
            self.alg_states = list(alg_states.values())
            self.equations = reduced_equations

            if len(self.equations) > 0:
                self.equations = ca.substitute(self.equations, variables,
                                               values)
            if len(self.initial_equations) > 0:
                self.initial_equations = ca.substitute(self.initial_equations,
                                                       variables, values)

        if options.get('expand_vectors', False):
            logger.info("Expanding vectors")

            symbols = []
            values = []

            for l in [
                    'states', 'der_states', 'alg_states', 'inputs',
                    'parameters', 'constants'
            ]:
                old_vars = getattr(self, l)
                new_vars = []
                for old_var in old_vars:
                    if old_var.symbol.numel() > 1:
                        rows = []
                        for i in range(old_var.symbol.size1()):
                            cols = []
                            for j in range(old_var.symbol.size2()):
                                if old_var.symbol.size1(
                                ) > 1 and old_var.symbol.size2() > 1:
                                    component_symbol = ca.MX.sym(
                                        '{}[{},{}]'.format(
                                            old_var.symbol.name(), i, j))
                                elif old_var.symbol.size1() > 1:
                                    component_symbol = ca.MX.sym(
                                        '{}[{}]'.format(
                                            old_var.symbol.name(), i))
                                elif old_var.symbol.size2() > 1:
                                    component_symbol = ca.MX.sym(
                                        '{}[{}]'.format(
                                            old_var.symbol.name(), j))
                                else:
                                    raise AssertionError
                                component_var = Variable(
                                    component_symbol, old_var.python_type)
                                for attribute in CASADI_ATTRIBUTES:
                                    value = ca.MX(getattr(old_var, attribute))
                                    if value.size1() == old_var.symbol.size1(
                                    ) and value.size2(
                                    ) == old_var.symbol.size2():
                                        setattr(component_var, attribute,
                                                value[i, j])
                                    elif value.size1() == old_var.symbol.size1(
                                    ):
                                        setattr(component_var, attribute,
                                                value[i])
                                    elif value.size2() == old_var.symbol.size2(
                                    ):
                                        setattr(component_var, attribute,
                                                value[j])
                                    else:
                                        assert value.size1() == 1
                                        assert value.size2() == 1
                                        setattr(component_var, attribute,
                                                value)
                                cols.append(component_var)
                            rows.append(cols)
                        symbols.append(old_var.symbol)
                        values.append(
                            ca.vertcat(*[
                                ca.horzcat(*[v.symbol for v in row])
                                for row in rows
                            ]))
                        new_vars.extend(itertools.chain.from_iterable(rows))
                    else:
                        new_vars.append(old_var)
                setattr(self, l, new_vars)

            if len(self.equations) > 0:
                self.equations = ca.substitute(self.equations, symbols, values)
                self.equations = list(
                    itertools.chain.from_iterable(
                        ca.vertsplit(ca.vec(eq)) for eq in self.equations))
            if len(self.initial_equations) > 0:
                self.initial_equations = ca.substitute(self.initial_equations,
                                                       symbols, values)
                self.initial_equations = list(
                    itertools.chain.from_iterable(
                        ca.vertsplit(ca.vec(eq))
                        for eq in self.initial_equations))

            # Replace values in metadata
            for variable in itertools.chain(self.states, self.alg_states,
                                            self.inputs, self.parameters,
                                            self.constants):
                for attribute in CASADI_ATTRIBUTES:
                    value = getattr(variable, attribute)
                    if isinstance(value, ca.MX) and not value.is_constant():
                        [value] = ca.substitute([value], symbols, values)
                        setattr(variable, attribute, value)

        if options.get('factor_and_simplify_equations', False):
            # Operations that preserve the equivalence of an equation
            # TODO: There may be more, but this is the most frequent set
            unary_ops = ca.OP_NEG, ca.OP_FABS, ca.OP_SQRT
            binary_ops = ca.OP_MUL, ca.OP_DIV

            # Recursive factor and simplify function
            def factor_and_simplify(eq):
                # These are ops that can simply be dropped
                if eq.n_dep() == 1 and eq.op() in unary_ops:
                    return factor_and_simplify(eq.dep())

                # These are binary ops and can get a little tricky
                # For now, we just drop constant divisors or multipliers
                elif eq.n_dep() == 2 and eq.op() in binary_ops:
                    if eq.dep(1).is_constant():
                        return factor_and_simplify(eq.dep(0))
                    elif eq.dep(0).is_constant() and eq.op() == ca.OP_MUL:
                        return factor_and_simplify(eq.dep(1))

                # If no hits, return unmodified
                return eq

            # Do the simplifications
            simplified_equations = [
                factor_and_simplify(eq) for eq in self.equations
            ]

            # Debugging output
            if logger.getEffectiveLevel() == logging.DEBUG:
                changed_equations = [
                    (o, s)
                    for o, s in zip(self.equations, simplified_equations)
                    if o is not s
                ]
                for orig, simp in changed_equations:
                    logger.debug('Equation {} simplified to {}'.format(
                        orig, simp))

            # Store changes
            self.equations = simplified_equations

        if options.get('detect_aliases', False):
            logger.info("Detecting aliases")

            states = OrderedDict([(s.symbol.name(), s) for s in self.states])
            der_states = OrderedDict([(s.symbol.name(), s)
                                      for s in self.der_states])
            alg_states = OrderedDict([(s.symbol.name(), s)
                                      for s in self.alg_states])
            inputs = OrderedDict([(s.symbol.name(), s) for s in self.inputs])
            parameters = OrderedDict([(s.symbol.name(), s)
                                      for s in self.parameters])

            all_states = OrderedDict()
            all_states.update(states)
            all_states.update(der_states)
            all_states.update(alg_states)
            all_states.update(inputs)
            all_states.update(parameters)

            alias_rel = AliasRelation()

            # For now, we only eliminate algebraic states.
            do_not_eliminate = set(
                list(der_states) + list(states) + list(inputs) +
                list(parameters))

            reduced_equations = []
            for eq in self.equations:
                if eq.n_dep() == 2 and (eq.is_op(ca.OP_SUB)
                                        or eq.is_op(ca.OP_ADD)):
                    if eq.dep(0).is_symbolic() and eq.dep(1).is_symbolic():
                        if eq.dep(0).name() in alg_states:
                            alg_state = eq.dep(0)
                            other_state = eq.dep(1)
                        elif eq.dep(1).name() in alg_states:
                            alg_state = eq.dep(1)
                            other_state = eq.dep(0)
                        else:
                            alg_state = None
                            other_state = None

                        # If both states are algebraic, we need to decide which to eliminate
                        if eq.dep(0).name() in alg_states and eq.dep(
                                1).name() in alg_states:
                            # Most of the time it does not matter which one we eliminate.
                            # The exception is if alg_state has already been aliased to a
                            # variable in do_not_eliminate. If this is the case, setting the
                            # states in the default order will cause the new canonical variable
                            # to be other_state, unseating (and eliminating) the current
                            # canonical variable (which is in do_not_eliminate).
                            if alias_rel.canonical_signed(
                                    alg_state.name())[0] in do_not_eliminate:
                                # swap the states
                                other_state, alg_state = alg_state, other_state

                        if alg_state is not None:
                            # Check to see if we are linking two entries in do_not_eliminate
                            if alias_rel.canonical_signed(alg_state.name())[0] in do_not_eliminate and \
                               alias_rel.canonical_signed(other_state.name())[0] in do_not_eliminate:
                                # Don't do anything for now, we only eliminate alg_states
                                pass

                            else:
                                # Eliminate alg_state by aliasing it to other_state
                                if eq.is_op(ca.OP_SUB):
                                    alias_rel.add(other_state.name(),
                                                  alg_state.name())
                                else:
                                    alias_rel.add(other_state.name(),
                                                  '-' + alg_state.name())

                                # To keep equations balanced, drop this equation
                                continue

                # Keep this equation
                reduced_equations.append(eq)

            # Eliminate alias variables
            variables, values = [], []
            for canonical, aliases in alias_rel:
                canonical_state = all_states[canonical]

                python_type = canonical_state.python_type
                start = canonical_state.start
                m, M = canonical_state.min, canonical_state.max
                nominal = canonical_state.nominal
                fixed = canonical_state.fixed

                for alias in aliases:
                    if alias[0] == '-':
                        sign = -1
                        alias = alias[1:]
                    else:
                        sign = 1

                    alias_state = all_states[alias]

                    variables.append(alias_state.symbol)
                    values.append(sign * canonical_state.symbol)

                    # If any of the aliases has a nonstandard type, apply it to
                    # the canonical state as well
                    if alias_state.python_type != float:
                        python_type = alias_state.python_type

                    # If any of the aliases has a nondefault start value, apply it to
                    # the canonical state as well
                    alias_state_start = ca.MX(alias_state.start)
                    if alias_state_start.is_regular(
                    ) and not alias_state_start.is_zero():
                        start = sign * alias_state.start

                    # The intersection of all bound ranges applies
                    m = ca.fmax(
                        m, alias_state.min if sign == 1 else -alias_state.max)
                    M = ca.fmin(
                        M, alias_state.max if sign == 1 else -alias_state.min)

                    # Take the largest nominal of all aliases
                    nominal = ca.fmax(nominal, alias_state.nominal)

                    # If any of the aliases is fixed, the canonical state is as well
                    fixed = ca.fmax(fixed, alias_state.fixed)

                    del all_states[alias]

                canonical_state.aliases = aliases
                canonical_state.python_type = python_type
                canonical_state.start = start
                canonical_state.min = m
                canonical_state.max = M
                canonical_state.nominal = nominal
                canonical_state.fixed = fixed

            self.states = [v for k, v in all_states.items() if k in states]
            self.der_states = [
                v for k, v in all_states.items() if k in der_states
            ]
            self.alg_states = [
                v for k, v in all_states.items() if k in alg_states
            ]
            self.inputs = [v for k, v in all_states.items() if k in inputs]
            self.parameters = [
                v for k, v in all_states.items() if k in parameters
            ]
            self.equations = reduced_equations

            if len(self.equations) > 0:
                self.equations = ca.substitute(self.equations, variables,
                                               values)
            if len(self.initial_equations) > 0:
                self.initial_equations = ca.substitute(self.initial_equations,
                                                       variables, values)

        if options.get('reduce_affine_expression', False):
            logger.info("Collapsing model into an affine expression")

            for equation_list in ['equations', 'initial_equations']:
                equations = getattr(self, equation_list)
                if len(equations) > 0:
                    states = ca.veccat(*self._symbols(
                        itertools.chain(self.states, self.der_states,
                                        self.alg_states, self.inputs)))
                    constants = ca.veccat(*self._symbols(self.constants))
                    parameters = ca.veccat(*self._symbols(self.parameters))

                    equations = ca.veccat(*equations)

                    Af = ca.Function('Af', [states, constants, parameters],
                                     [ca.jacobian(equations, states)])
                    bf = ca.Function('bf', [states, constants, parameters],
                                     [equations])

                    # Work around CasADi issue #172
                    if len(self.constants) == 0 or not ca.depends_on(
                            equations, constants):
                        constants = 0
                    else:
                        logger.warning(
                            'Not all constants have been eliminated.  As a result, the affine DAE expression will use a symbolic matrix, as opposed to a numerical sparse matrix.'
                        )
                    if len(self.parameters) == 0 or not ca.depends_on(
                            equations, parameters):
                        parameters = 0
                    else:
                        logger.warning(
                            'Not all parameters have been eliminated.  As a result, the affine DAE expression will use a symbolic matrix, as opposed to a numerical sparse matrix.'
                        )

                    A = Af(0, constants, parameters)
                    b = bf(0, constants, parameters)

                    # Replace veccat'ed states with brand new state vectors so as to avoid the value copy operations induced by veccat.
                    self._states_vector = ca.MX.sym(
                        'states_vector',
                        sum([s.numel() for s in self._symbols(self.states)]))
                    self._der_states_vector = ca.MX.sym(
                        'der_states_vector',
                        sum([
                            s.numel() for s in self._symbols(self.der_states)
                        ]))
                    self._alg_states_vector = ca.MX.sym(
                        'alg_states_vector',
                        sum([
                            s.numel() for s in self._symbols(self.alg_states)
                        ]))
                    self._inputs_vector = ca.MX.sym(
                        'inputs_vector',
                        sum([s.numel() for s in self._symbols(self.inputs)]))

                    states_vector = ca.vertcat(self._states_vector,
                                               self._der_states_vector,
                                               self._alg_states_vector,
                                               self._inputs_vector)
                    equations = [
                        ca.reshape(ca.mtimes(A, states_vector),
                                   equations.shape) + b
                    ]
                    setattr(self, equation_list, equations)

        if options.get('expand_mx', False):
            logger.info("Expanded MX functions will be returned")
            self._expand_mx_func = lambda x: x.expand()

        logger.info("Finished model simplification")
Ejemplo n.º 31
0
 def convert_expr_from_tau_to_time(self, expr, t_k, t_kp1):
     t = self.t
     tau = self.tau
     h = t_kp1 - t_k
     return substitute(expr, tau, (t - t_k) / h)
Ejemplo n.º 32
0
 def construct_upd_xz(self, problem=None):
     # construct optifather & give reference to problem
     self.father_updx = OptiFather(self.group.values())
     self.problem.father = self.father_updx
     # define z_ij variables
     init = self.q_ij_struct(0)
     for nghb, q_ij in self.q_ij.items():
         for child, q_j in q_ij.items():
             for name, ind in q_j.items():
                 var = np.array(child._values[name])
                 v = var.T.flatten()[ind]
                 init[nghb.label, child.label, name, ind] = v
     z_ij = self.define_variable(
         'z_ij', self.q_ij_struct.shape[0], value=np.array(init.cat))
     # define parameters
     l_ij = self.define_parameter('l_ij', self.q_ij_struct.shape[0])
     l_ji = self.define_parameter('l_ji', self.q_ji_struct.shape[0])
     # put them in the struct format
     z_ij = self.q_ij_struct(z_ij)
     l_ij = self.q_ij_struct(l_ij)
     l_ji = self.q_ji_struct(l_ji)
     # get (part of) variables
     x_i = self._get_x_variables(symbolic=True)
     # construct local copies of parameters
     par = {}
     for name, s in self.par_global.items():
         par[name] = self.define_parameter(name, s.shape[0], s.shape[1])
     if problem is None:
         # get time info
         t = self.define_symbol('t')
         T = self.define_symbol('T')
         t0 = t/T
         # transform spline variables: only consider future piece of spline
         tf = lambda cfs, basis: shift_knot1_fwd(cfs, basis, t0)
         self._transform_spline(x_i, tf, self.q_i)
         self._transform_spline([z_ij, l_ij], tf, self.q_ij)
         self._transform_spline(l_ji, tf, self.q_ji)
         # construct objective
         obj = 0.
         for child, q_i in self.q_i.items():
             for name in q_i.keys():
                 x = x_i[child.label][name]
                 for nghb in self.q_ji.keys():
                     l = l_ji[str(nghb), child.label, name]
                     obj += mtimes(l.T, x)
         for nghb, q_j in self.q_ij.items():
             for child in q_j.keys():
                 for name in q_j[child].keys():
                     z = z_ij[str(nghb), child.label, name]
                     l = l_ij[str(nghb), child.label, name]
                     obj -= mtimes(l.T, z)
         self.define_objective(obj)
     # construct constraints
     for con in self.global_constraints:
         c = con[0]
         for sym in symvar(c):
             for label, child in self.group.items():
                 if sym.name() in child.symbol_dict:
                     name = child.symbol_dict[sym.name()][1]
                     v = x_i[label][name]
                     ind = self.q_i[child][name]
                     sym2 = MX.zeros(sym.size())
                     sym2[ind] = v
                     sym2 = reshape(sym2, sym.shape)
                     c = substitute(c, sym, sym2)
                     break
             for nghb in self.q_ij.keys():
                 for label, child in nghb.group.items():
                     if sym.name() in child.symbol_dict:
                         name = child.symbol_dict[sym.name()][1]
                         v = z_ij[nghb.label, label, name]
                         ind = self.q_ij[nghb][child][name]
                         sym2 = MX.zeros(sym.size())
                         sym2[ind] = v
                         sym2 = reshape(sym2, sym.shape)
                         c = substitute(c, sym, sym2)
                         break
             for name, s in self.par_global.items():
                 if s.name() == sym.name():
                     c = substitute(c, sym, par[name])
         lb, ub = con[1], con[2]
         self.define_constraint(c, lb, ub)
     # construct problem
     prob, buildtime = self.father_updx.construct_problem(
         self.options, str(self._index), problem)
     self.problem_upd_xz = prob
     self.father_updx.init_transformations(self.problem.init_primal_transform,
                                      self.problem.init_dual_transform)
     self.init_var_dd()
     return buildtime
Ejemplo n.º 33
0
    def plot(self, p=[['states'], ['constraints']], tikz=False, show=True):
        """Plot states and/or constraints.

        Args:
            p (list of lists): Each inner list defines a plot window. The
              inner lists either contain the strings 'states' and/or
              'constraints', the name of a state defined in ``self.sys`` or a
              CasADi expression in terms of the flat outputs. When giving a
              CasADi expression you can optionally supply a y-label by passing
              it as a tuple.

            tikz (boolean): When true the plot is saved to a .tikz file
              which you can include in you latex documents to produce
              beautiful plots (default False)

            show (boolean): When True the plot is shown by invoking show()
              from matplotlib (default True)
        """
        import matplotlib.pyplot as plt
        for plots in p:
            fig = plt.figure()
            j = 1
            m = (len(plots) + ('states' in plots) * (self.sys._nx - 1) +
                ('constraints' in plots) * (len(self.constraints) - 1))
            for l in plots:
                if l in self.sys.x.keys():
                    ax = fig.add_subplot(m / int(np.sqrt(m)) + np.mod(m, 2),
                                         int(np.sqrt(m)), j)
                    ax.plot(self.sol['t'], self.sol['states'][l])
                    plt.ylabel(l)
                    plt.xlabel('time')
                    j += 1
                elif (l == 'states'):
                    for i, k in enumerate(self.sol[l].keys()):
                        ax = fig.add_subplot(m / int(np.sqrt(m)) + np.mod(m, 2),
                                int(np.sqrt(m)), j)
                        ax.plot(self.sol['t'], self.sol[l][k])
                        plt.ylabel(k)
                        plt.xlabel('time')
                        j += 1
                elif l == 'constraints':
                    for i, f in enumerate(self.constraints):
                        F = cas.substitute(f[0], self.sys.y, self.path)
                        F = cas.SXFunction([self.s], [F])
                        F.init()
                        Flb = cas.SXFunction([self.s], [f[1]])
                        Fub = cas.SXFunction([self.s], [f[2]])
                        c = np.array([evalf(F, s.T).toArray().ravel()
                                               for s in self.sol['s']])
                        cub = np.array([evalf(Fub, s.T).toArray().ravel()
                                               for s in self.sol['s']])
                        clb = np.array([evalf(Flb, s.T).toArray().ravel()
                                               for s in self.sol['s']])
                        ax = fig.add_subplot(m / int(np.sqrt(m)) + np.mod(m, 2),
                                             int(np.sqrt(m)), j)
                        ax.plot(self.sol['t'], c)
                        ax.plot(self.sol['t'], clb, color='black')
                        ax.plot(self.sol['t'], cub, color='black')
                        j += 1
                elif isinstance(l, tuple):
                    if isinstance(l[1], cas.SXMatrix):
                        F = cas.substitute(l[1], self.sys.y, self.path)
                        F = cas.SXFunction([self.s], [F])
                        F.init()
                        c = np.array([evalf(F, s.T).toArray().ravel()
                                               for s in self.sol['s']])
                        ax = fig.add_subplot(m / int(np.sqrt(m)) + np.mod(m, 2),
                                             int(np.sqrt(m)), j)
                        ax.plot(self.sol['t'], c)
                    else:
                        ax = fig.add_subplot(m / int(np.sqrt(m)) + np.mod(m, 2),
                                    int(np.sqrt(m)), j)
                        ax.plot(self.sol['t'], l[1])
                        plt.ylabel(l[0])
                        plt.xlabel('time')
                    j += 1
                else:
                    if isinstance(l, cas.SXMatrix):
                        F = cas.substitute(l, self.sys.y, self.path)
                        F = cas.SXFunction([self.s], [F])
                        F.init()
                        c = np.array([evalf(F, s.T).toArray().ravel()
                                               for s in self.sol['s']])
                        ax = fig.add_subplot(m / int(np.sqrt(m)) + np.mod(m, 2),
                                             int(np.sqrt(m)), j)
                        ax.plot(self.sol['t'], c)
                    else:
                        ax = fig.add_subplot(m / int(np.sqrt(m)) + np.mod(m, 2),
                                    int(np.sqrt(m)), j)
                        ax.plot(self.sol['t'], l)
                        plt.xlabel('time')
                    j += 1
        if tikz and len(p) == 1:
            from matplotlib2tikz import save as tsave
            tsave('_'.join(p[0]) + '.tikz')
        if show:
            plt.show()
Ejemplo n.º 34
0
    def exit_classDefinition(self, tree: etree._Element):  # noqa: too-complex
        dae = HybridDae()
        dae.t = self.scope['time']
        self.model[tree] = dae

        # handle component declarations
        for var_name, v in self.scope['var'].items():
            variability = self.scope['prop'][var_name]['variability']
            if variability == 'continuous':
                if var_name in self.scope['states']:
                    dae.x = ca.vertcat(dae.x, v)
                    dae.dx = ca.vertcat(dae.dx, self.der(v))
                else:
                    dae.y = ca.vertcat(dae.y, v)
            elif variability == 'discrete':
                dae.m = ca.vertcat(dae.m, v)
            elif variability == 'parameter':
                dae.p = ca.vertcat(dae.p, v)
            elif variability == 'constant':
                dae.p = ca.vertcat(dae.p, v)
            else:
                raise ValueError('unknown variability', variability)

        for eq in self.scope['eqs']:
            if isinstance(eq, self.sym):
                dae.f_x = ca.vertcat(dae.f_x, eq)

        # build reinit expression and discrete equations
        dae.f_i = dae.x
        dae.f_m = dae.m
        for eq in self.scope['when_eqs']:
            w = eq['cond']
            for then_eq in eq['then']:
                if isinstance(then_eq, tuple):
                    if then_eq[0] == 'reinit':
                        sub_var = then_eq[1]
                        sub_expr = ca.if_else(self.edge(w), then_eq[2], sub_var)
                        dae.f_i = ca.substitute(dae.f_i, sub_var, sub_expr)
                elif isinstance(then_eq, self.sym):
                    # this is a discrete variable assignment
                    # so it should be a casadi subtraction y = x
                    assert then_eq.is_op(ca.OP_SUB) and then_eq.n_dep() == 2
                    sub_var = then_eq.dep(0)
                    sub_expr = ca.if_else(self.edge(w), then_eq.dep(1), sub_var)
                    dae.f_m = ca.substitute(dae.f_m, sub_var, sub_expr)

        dae.t = self.scope['time']
        dae.prop.update(self.scope['prop'])
        c_dict = self.scope['c']
        for k in c_dict.keys():
            dae.c = ca.vertcat(dae.c, k)
            dae.pre_c = ca.vertcat(dae.pre_c, self.pre_cond(k))
            dae.f_c = ca.vertcat(dae.f_c, c_dict[k])

        for l, r in [('f_c', 'c'), ('c', 'pre_c'), ('dx', 'x'), ('f_m', 'm')]:
            vl = getattr(dae, l)
            vr = getattr(dae, r)
            if vl.shape != vr.shape:
                raise ValueError(
                    '{:s} and {:s} must have the same shape:'
                    '\n{:s}: {:s}\t{:s}: {:s}'.format(
                        l, r, l, str(dae.f_m), r, str(dae.m)))

        dae.ng = ca.vertcat(*self.scope['ng'])
        dae.nu = ca.vertcat(*self.scope['nu'])

        n_eq = dae.f_x.shape[0] + dae.f_m.shape[0]
        n_var = dae.x.shape[0] + dae.m.shape[0] + dae.y.shape[0]
        if n_eq != n_var:
            raise ValueError(
                'must have equal number of equations '
                '{:d} and unknowns {:d}\n:{:s}'.format(
                    n_eq, n_var, str(dae)))
        self.scope_stack.pop()
#!/usr/bin/env python
# Benchmark my casadi-based dynamic equations

import casadi
from carousel_model import DAE_RHS, ODE_RHS, q, dq, ddq, constants, inputs

# Specialize dynamic equations for the constants (modeling parameters)
eqns = casadi.substitute(ODE_RHS,constants.keys(),constants.values())

ins = casadi.vertcat([q,dq,inputs])
f = casadi.SXFunction([ins],[eqns])
f.init()
f.generateCode('carousel_model.c')

#from cse import cse
#from carousel_model import ODE_RHS as f

#cn = casadi.countNodes
#f2 = cse(f)
#print cn(f)
#print cn(f2)

Ejemplo n.º 36
0
 def substitute_nodes(self, f):
     return [
         casadi.substitute([f], sub[0], sub[1])[0]
         for sub in self.node_substitutions
     ]
Ejemplo n.º 37
0
 def subsForModel(self, ll):
     subs = self._getSubs()
     params = casadi.vertcat(*[getattr(self, p) for p, _ in subs])
     paramVals = casadi.vertcat(*[val for _, val in subs])
     return casadi.substitute(ll, params, paramVals)
Ejemplo n.º 38
0
 def _make_constraints(self):
     """Parse the constraints and put them in the correct format"""
     N = self.options['N']
     Nc = self.options['Nc']
     b = self.prob['vars'][0]
     H = self.prob['vars'][1]
     # ODEs
     # ~~~~
     con = self._ode(b)
     lb = np.alen(con) * [0]
     ub = np.alen(con) * [0]
     # Convex combination
     # ~~~~~~~~~~~~~~~~~~
     con.append(cas.sumCols(H))
     lb.extend([1] * Nc)
     ub.extend([1] * Nc)
     # Sample constraints
     # ~~~~~~~~~~~~~~~~~~
     S = self.prob['s']
     path, bs = self._make_path()[0:2]
     basisH = self._make_basis()
     B = [np.matrix(basisH(S))]
     # TODO!!! ============================================================
     for i in range(1, self.h.size2()):
         # B.append(np.matrix(basisH.derivative(S, i)))
         Bi, p = basisH.derivative(i)
         B.append(np.matrix(np.dot(Bi(S), p)))
     # ====================================================================
     for f in self.constraints:
         F = cas.substitute(f[0], self.sys.y, path)
         if f[3] is None:
             F = cas.vertcat([cas.substitute(F,
                              cas.vertcat([
                                 self.s[0],
                                 bs,
                                 cas.vec(self.h)]),
                              cas.vertcat([
                                 S[j],
                                 cas.vec(b[j, :]),
                                 cas.vec(cas.vertcat([cas.mul(B[i][j, :], H) for i in range(self.h.size2())]).trans())
                              ])) for j in range(N + 1)])
             Flb = [evalf(cas.SXFunction([self.s], [cas.SXMatrix(f[1])]), s).toArray().ravel() for s in S]
             Fub = [evalf(cas.SXFunction([self.s], [cas.SXMatrix(f[2])]), s).toArray().ravel() for s in S]
             con.append(F)
             lb.extend(Flb)
             ub.extend(Fub)
         else:
             F = cas.vertcat([cas.substitute(F,
                              cas.vertcat([
                                 self.s[0],
                                 bs,
                                 cas.vec(self.h)]),
                              cas.vertcat([
                                 S[j],
                                 cas.vec(b[j, :]),
                                 cas.vec(cas.vertcat([cas.mul(B[i][j, :], H) for i in range(self.h.size2())]).trans())
                              ])) for j in f[3]])
             con.append(F)
             lb.extend([f[1]])
             ub.extend([f[2]])
     self.prob['con'] = [con, lb, ub]
Ejemplo n.º 39
0
 def fprime(x):
     ret= casadi.substitute(jacob, params, casadi.vertcat(*x))
     ret= numpy.array([float(ret[i]) for i in xrange(ret.shape[1])])
     return ret
Ejemplo n.º 40
0
    def _create_problem(self, model, sampled_parameter):
        # Problem
        problem = OptimalControlProblem(model)
        problem.name = self.socp.name + '_PCE'

        problem.p_opt = substitute(self.socp.p_opt,
                                   self.socp.get_p_without_p_unc(),
                                   problem.model.p_sym)
        problem.theta_opt = substitute(self.socp.theta_opt,
                                       self.socp.model.theta_sym,
                                       problem.model.theta_sym)

        problem.x_max = repmat(vertcat(self.socp.x_max, inf), self.n_samples)
        problem.y_max = repmat(self.socp.y_max, self.n_samples)
        problem.u_max = self.socp.u_max
        problem.delta_u_max = self.socp.delta_u_max
        problem.p_opt_max = self.socp.p_opt_max
        problem.theta_opt_max = self.socp.theta_opt_max

        problem.x_min = repmat(vertcat(self.socp.x_min, -inf), self.n_samples)
        problem.y_min = repmat(self.socp.y_min, self.n_samples)
        problem.u_min = self.socp.u_min
        problem.delta_u_min = self.socp.delta_u_min
        problem.p_opt_min = self.socp.p_opt_min
        problem.theta_opt_min = self.socp.theta_opt_min

        problem.t_f = self.socp.t_f

        if depends_on(self.socp.g_eq, self.socp.model.x_sym) or depends_on(
                self.socp.g_eq, self.socp.model.y_sym):
            raise NotImplementedError(
                'Case where "g_eq" depends on "model.x_sym" or "model.y_sym" is not implemented '
            )

        if depends_on(self.socp.g_ineq, self.socp.model.x_sym) or depends_on(
                self.socp.g_ineq, self.socp.model.y_sym):
            raise NotImplementedError(
                'Case where "g_ineq" depends on "model.x_sym" '
                'or "model.y_sym" is not implemented ')

        original_vars = vertcat(self.socp.model.u_sym,
                                self.socp.get_p_without_p_unc(),
                                self.socp.model.theta_sym,
                                self.socp.model.t_sym, self.socp.model.tau_sym)

        new_vars = vertcat(problem.model.u_sym, problem.model.p_sym,
                           problem.model.theta_sym, problem.model.t_sym,
                           problem.model.tau_sym)

        if not self.socp.n_h_initial == self.socp.model.n_x:
            problem.h_initial = vertcat(
                problem.h_initial,
                substitute(self.socp.h_initial[:self.socp.model.n_x],
                           original_vars, new_vars))
        problem.h_final = substitute(self.socp.h_final, original_vars,
                                     new_vars)

        problem.g_eq = substitute(self.socp.g_eq, original_vars, new_vars)
        problem.g_ineq = substitute(self.socp.g_ineq, original_vars, new_vars)
        problem.time_g_eq = self.socp.time_g_eq
        problem.time_g_ineq = self.socp.time_g_ineq

        for i in range(self.socp.n_uncertain_initial_condition):
            ind = self.socp.get_uncertain_initial_cond_indices()[i]
            x_ind_s = problem.model.x_0_sym[ind::(self.socp.model.n_x + 1)]
            problem.h_initial = substitute(
                problem.h_initial, x_ind_s,
                sampled_parameter[self.socp.n_p_unc + i, :].T)
            problem.h_final = substitute(
                problem.h_final, x_ind_s,
                sampled_parameter[self.socp.n_p_unc + i, :].T)

            problem.g_eq = substitute(
                problem.g_eq, x_ind_s,
                sampled_parameter[self.socp.n_p_unc + i, :].T)
            problem.g_ineq = substitute(
                problem.g_ineq, x_ind_s,
                sampled_parameter[self.socp.n_p_unc + i, :].T)

        problem.last_u = self.socp.last_u

        problem.y_guess = repmat(
            self.socp.y_guess,
            self.n_samples) if self.socp.y_guess is not None else None
        problem.u_guess = self.socp.u_guess
        problem.x_0 = repmat(
            vertcat(self.socp.x_0,
                    0), self.n_samples) if self.socp.x_0 is not None else None

        problem.parametrized_control = self.socp.parametrized_control
        problem.positive_objective = self.socp.parametrized_control
        problem.NULL_OBJ = self.socp.NULL_OBJ

        if not is_equal(self.socp.S, 0) or not is_equal(self.socp.V, 0):
            raise NotImplementedError

        return problem