How to check solver convergence with GEKKO and APOPT

Viewed 438

I am dealing with small MILP problems for which I have fixed the maximum number of iterations. I would like to determine the instances for which we are sure that we have reached the optimum.

When calling m.solve(disp=True), if the solver stops early, it displays the warning:

Warning: reached maximum MINLP iterations, returning best solution

I would like to check programmatically whether we are in such a situation. I tried

1) looking at the documentation but it says m.options.SOLVESTATUS is always 1 and m.options.APPINFO is always 0 from the moment the solver found a feasible solution.

2)

optimum = m.options.ITERATIONS < m.options.MAX_ITER

but it does not work because in fact m.options.ITERATIONS doesn't do what I thought (it is always much lower than m.options.MAX_ITER).

3) raise and then catch the warning:

import warnings
warnings.filterwarnings("error")

try:
    self.model.solve()
    optimum = True
except:
    optimum = False

But it doesn't work either (no error is raised).


So I have 2 questions:

1) How to check the number of iterations that have been used by the solver ?

2) How to determine whether the solver did check every candidate and thus found the best instanciation ?

2 Answers

Right now, there is no way to report the number of Mixed Integer iterations from APOPT. However, you can ensure that APOPT never stops prematurely by setting minlp_maximum_iterations to a large value such as 100000. The m.options.ITERATIONS reports the NLP iterations of the last branch evaluation.

You can ensure that all candidate solutions are evaluated by setting minlp_gap_tol to zero. The solver will terminate if the gap tolerance condition is met. Setting it to zero means that all options will be evaluated. However, this can take much longer to evaluate all options.

from gekko import GEKKO
m = GEKKO(remote=False) # Initialize gekko
m.options.SOLVER=1  # APOPT is an MINLP solver

# optional solver settings with APOPT
m.solver_options = ['minlp_maximum_iterations 100000', \
                    # minlp iterations with integer solution
                    'minlp_max_iter_with_int_sol 10', \
                    # nlp sub-problem max iterations
                    'nlp_maximum_iterations 50', \
                    # covergence tolerance
                    'minlp_gap_tol 0.00']

# Initialize variables
x1 = m.Var(value=1,lb=1,ub=5)
x2 = m.Var(value=5,lb=1,ub=5)
# Integer constraints for x3 and x4
x3 = m.Var(value=5,lb=1,ub=5,integer=True)
x4 = m.Var(value=1,lb=1,ub=5,integer=True)
# Equations
m.Equation(x1*x2*x3*x4>=25)
m.Equation(x1**2+x2**2+x3**2+x4**2==40)
m.Obj(x1*x4*(x1+x2+x3)+x3) # Objective
m.solve(disp=True) # Solve
print('Results')
print('Iterations: ' + str(m.options.ITERATIONS))
print('x1: ' + str(x1.value))
print('x2: ' + str(x2.value))
print('x3: ' + str(x3.value))
print('x4: ' + str(x4.value))
print('Objective: ' + str(m.options.objfcnval))

There is more information on solver options.

From John Hedengren's answer :

1) How to check the number of iterations that have been used by the solver ?

Right now, there is no way to report the number of Mixed Integer iterations from APOPT

2) How to determine whether the solver did check every candidate and thus found the best instanciation ?

I added this as a feature request on Github: github.com/BYU-PRISM/GEKKO/issues/81

For now I have implemented an ugly solution :

# redirect stdout
sys.stdout = open('tmp_output', 'w')

# solve and output to the tmp file
try:
    self.model.solve(disp=True)
except:
    pass #solution not found

# read the file
with open('tmp_output', 'r') as f:
    line = f.readline()

    # skip the first lines
    while line[:5] != "Iter:":
        line = f.readline()

    # skip the next lines (one line per iteration)
    while line[:5] in ["Iter:", "--Int"]:
        line = f.readline()

    # check the warning
    optimal = line != " Warning: reached maximum MINLP iterations, returning best solution\n"

return optimal
Related