Andrew T
Andrew T

Reputation: 582

returning function calls and other info when optimization solves

I am benchmarking multiple problems for multiple systems using Gekko, and I would like to get my code to return the function calls, iterations, and time it takes to solve. I know that the solver automatically prints all of this data but is there an object or attribute that can be returned to allow my function to return the numeric values?

Here is an example of how the code is set up.

def model(plot=False):
    t = np.linspace(0, 1, 101)
    m = GEKKO(remote=False); m.time=t

    fe = m.Param(np.cos(2*np.pi*t)+3)
    de = m.Var(fe[0])

    e = m.CV(0); e.STATUS=1; e.SPHI=e.SPLO=0; e.WSPHI=1000; e.WSPLO=1
    der = m.MV(0, lb=-1, ub=1); der.STATUS=1

    m.Equations([de.dt() == der,  e == fe-de])

    m.options.IMODE=6; m.solve()

    if plot:
        import matplotlib.pyplot as plt
        plt.plot(t, fe)
        plt.plot(t, de)
        plt.plot(t, der)
        plt.show()
    return m.fcalls


if __name__ == "__main__":
    model(plot=True)

Upvotes: 4

Views: 65

Answers (1)

John Hedengren
John Hedengren

Reputation: 14346

The objective function, iterations, solve time, and solution status are available in Gekko with:

  • m.options.OBJFCNVAL
  • m.options.ITERATIONS
  • m.options.SOLVETIME
  • m.options.APPSTATUS

You could return these as a list as I've done with summary.

from gekko import GEKKO
import numpy as np

def model(plot=False):
    t = np.linspace(0, 1, 101)
    m = GEKKO(remote=False); m.time=t

    fe = m.Param(np.cos(2*np.pi*t)+3)
    de = m.Var(fe[0])

    e = m.CV(0); e.STATUS=1; e.SPHI=e.SPLO=0; e.WSPHI=1000; e.WSPLO=1
    der = m.MV(0, lb=-1, ub=1); der.STATUS=1

    m.Equations([de.dt() == der,  e == fe-de])

    m.options.DIAGLEVEL=1
    m.options.SOLVER=1
    m.options.IMODE=6; m.solve()

    if plot:
        import matplotlib.pyplot as plt
        plt.plot(t, fe)
        plt.plot(t, de)
        plt.plot(t, der)
        plt.savefig('result.png')
    return [m.options.OBJFCNVAL,\
            m.options.ITERATIONS,\
            m.options.SOLVETIME,\
            m.options.APPSTATUS]


if __name__ == "__main__":
    summary = model(plot=True)
    print(summary)

If you want function calls, it is a little more complicated because there are different types of function calls. There are function calls for the objective function and constraints, function calls for 1st derivatives, and function calls for 2nd derivatives. You can get a complete report of all the subroutine calls and the individuals and cumulative times for each by setting m.options.DIAGLEVEL=1 or higher. Here is the solver output for this problem:

 Number of state variables:    1900
 Number of total equations: -  1800
 Number of slack variables: -  0
 ---------------------------------------
 Degrees of freedom       :    100

 ----------------------------------------------
 Dynamic Control with APOPT Solver
 ----------------------------------------------

 Iter    Objective  Convergence
    0  9.81590E+01  1.00000E+00
    1  7.62224E+01  4.00000E-10
    2  7.62078E+01  1.10674E-02
    3  7.62078E+01  1.00000E-10
    4  7.62078E+01  8.32667E-17
    5  7.62078E+01  8.32667E-17
 Successful solution

 ---------------------------------------------------
 Solver         :  APOPT (v1.0)
 Solution time  :  0.5382 sec
 Objective      :  76.20778997271815
 Successful solution
 ---------------------------------------------------

Some solvers, like IPOPT, don't have the iterations readily available from the API so they are always reported as zero. With APOPT, the summary list is [76.207789973, 5, 0.5253, 1]. The timing and function call report is after the solver summary.

Timer #     1       0.70/       1 =       0.70 Total system time
Timer #     2       0.54/       1 =       0.54 Total solve time
Timer #     3       0.05/       9 =       0.01 Objective Calc: apm_p
Timer #     4       0.00/       5 =       0.00 Objective Grad: apm_g
Timer #     5       0.02/       9 =       0.00 Constraint Calc: apm_c
Timer #     6       0.00/       0 =       0.00 Sparsity: apm_s
Timer #     7       0.00/       0 =       0.00 1st Deriv #1: apm_a1
Timer #     8       0.00/       5 =       0.00 1st Deriv #2: apm_a2
Timer #     9       0.02/     200 =       0.00 Custom Init: apm_custom_init
Timer #    10       0.00/     200 =       0.00 Mode: apm_node_res::case 0
Timer #    11       0.00/     600 =       0.00 Mode: apm_node_res::case 1
Timer #    12       0.00/     200 =       0.00 Mode: apm_node_res::case 2
Timer #    13       0.00/     400 =       0.00 Mode: apm_node_res::case 3
Timer #    14       0.00/    4800 =       0.00 Mode: apm_node_res::case 4
Timer #    15       0.00/    2000 =       0.00 Mode: apm_node_res::case 5
Timer #    16       0.00/       0 =       0.00 Mode: apm_node_res::case 6
Timer #    17       0.00/       5 =       0.00 Base 1st Deriv: apm_jacobian
Timer #    18       0.02/       5 =       0.00 Base 1st Deriv: apm_condensed_jacobian
Timer #    19       0.00/       1 =       0.00 Non-zeros: apm_nnz
Timer #    20       0.00/       0 =       0.00 Count: Division by zero
Timer #    21       0.00/       0 =       0.00 Count: Argument of LOG10 negative
Timer #    22       0.00/       0 =       0.00 Count: Argument of LOG negative
Timer #    23       0.00/       0 =       0.00 Count: Argument of SQRT negative
Timer #    24       0.00/       0 =       0.00 Count: Argument of ASIN illegal
Timer #    25       0.00/       0 =       0.00 Count: Argument of ACOS illegal
Timer #    26       0.00/       1 =       0.00 Extract sparsity: apm_sparsity
Timer #    27       0.00/      17 =       0.00 Variable ordering: apm_var_order
Timer #    28       0.00/       1 =       0.00 Condensed sparsity
Timer #    29       0.00/       0 =       0.00 Hessian Non-zeros
Timer #    30       0.00/       3 =       0.00 Differentials
Timer #    31       0.00/       0 =       0.00 Hessian Calculation
Timer #    32       0.00/       0 =       0.00 Extract Hessian
Timer #    33       0.00/       1 =       0.00 Base 1st Deriv: apm_jac_order
Timer #    34       0.06/       1 =       0.06 Solver Setup
Timer #    35       0.40/       1 =       0.40 Solver Solution
Timer #    36       0.00/      23 =       0.00 Number of Variables
Timer #    37       0.00/      12 =       0.00 Number of Equations
Timer #    38       0.05/      17 =       0.00 File Read/Write
Timer #    39       0.00/       1 =       0.00 Dynamic Init A
Timer #    40       0.02/       1 =       0.02 Dynamic Init B
Timer #    41       0.02/       1 =       0.02 Dynamic Init C
Timer #    42       0.00/       1 =       0.00 Init: Read APM File
Timer #    43       0.00/       1 =       0.00 Init: Parse Constants
Timer #    44       0.00/       1 =       0.00 Init: Model Sizing
Timer #    45       0.00/       1 =       0.00 Init: Allocate Memory
Timer #    46       0.00/       1 =       0.00 Init: Parse Model
Timer #    47       0.00/       1 =       0.00 Init: Check for Duplicates
Timer #    48       0.00/       1 =       0.00 Init: Compile Equations
Timer #    49       0.00/       1 =       0.00 Init: Check Uninitialized
Timer #    50       0.00/     205 =       0.00 Evaluate Expression Once
Timer #    51       0.00/       0 =       0.00 Sensitivity Analysis: LU Factorization
Timer #    52       0.00/       0 =       0.00 Sensitivity Analysis: Gauss Elimination
Timer #    53       0.00/       0 =       0.00 Sensitivity Analysis: Total Time

Timers 3, 4, and 5 are probably most relevant to your question. They are objective function requests, 1st derivative requests, and constraint evaluation requests.

Upvotes: 2

Related Questions