2

I'm trying to minimize a custom cost function (see below) using the python gekko library but seem to be encountering issues with the dtype being set by the library. Attempts to cast the variables to np.int_ result in a ValueError as well. Is there a way to make the MINLP solver of the GEKKO library allow for using its variables as indexers? Or is there potentially a way to minimize this cost function with another library?

def find_optimal_path(
        self,
        p0: npt.NDArray[np.int_],
        correlation_matrix: npt.NDArray[np.float64],
        euclidean_distance_matrix: npt.NDArray[np.float64],
        lags: npt.NDArray[np.int_],
    ) -> np.float64:
        r"""
        Finds the optimal path for the provided correlation matrix of size
        M x N, euclidean distance matrix of size M and N and lags vector of size Mx1
        where M is the shift distance and N is the number of
        rolling windows. The cost function is defined as:

        .. math::

            cost(\textbf{p}) = 1 - \frac{1}{N}\sum_{i=0}^{N}{C[p_i,i]} +
                \frac{\alpha}{N}\sum_{i=0}^{N}{E[p_i,i]} +
                \frac{\beta}{N}|\sum_{i=0}^{N}{\textbf{l}[p_i]}| +
                \frac{\gamma}{N}\sum_{i=0}^{N}{|\textbf{l}[p_i]-\textbf{l}[p_{i-1}]|+|\textbf{l}[p_i]-\textbf{l}[p_{i+1}]|}
        """

        def previous_and_next(some_iterable):
            prevs, items, nexts = tee(some_iterable, 3)
            prevs = chain([None], prevs)
            nexts = chain(islice(nexts, 1, None), [None])
            return zip(prevs, items, nexts)

        # TODO: Tune these
        alpha = beta = gamma = 0.1
        N = len(p0)

        def obj(path):
            return (
                1
                - 1 / N * np.sum(correlation_matrix[path, np.arange(len(path))])
                + alpha
                / N
                * np.sum(euclidean_distance_matrix[path, np.arange(len(path))])
                + beta / N * abs(np.sum([lags[y] for y in path]))
                + gamma
                / N
                * np.sum(
                    [
                        abs(lags[y] - lags[p]) + abs(lags[y] - lags[n])
                        for p, y, n in previous_and_next(path)
                    ]
                )
            )

        # Instantiate local MINLP solver
        m = GEKKO(remote=False)

        # Add path variable
        path = m.Array(m.Var, N, lb=0, ub=correlation_matrix.shape[0] - 1, integer=True)
        for i in range(N):
            path[i].value = p0[i]

        # Add objective
        m.Minimize(obj(path))

        # Solve
        m.solve(disp=True)

        return path

If I run the above function I get an IndexError from numpy which I think comes from the fact that the path variable has dtype=object:

IndexError: arrays used as indices must be of integer (or boolean) type

If I attempt to cast the path variable inside my obj function like so path = np.asarray(path, dtype=np.int_) I get the following python ValueError:

ValueError: setting an array element with a sequence.
vcromy
  • 75
  • 1
  • 8

1 Answers1

0

The equations in Gekko are written to a model file that is compiled to byte-code to provide derivatives for the gradient-based optimizer. Any function call to build the model is only called when the model is built. There is currently no callback to revisit the function when the values change. An alternative to using a variable as an index is to use binary values that can then be solved as a mixed integer optimization problem such as shown in the Logical Condition examples.

Here is an example of path optimization with Gekko that may help as you build your application.

path optimization

It is for a High Altitude Long Endurance (HALE) aircraft.

HALE UAV

John Hedengren
  • 12,068
  • 1
  • 21
  • 25