Monitor and stop a solve

snopt accepts three optional functions that SNOPT.jl calls during the solve. Each function receives one argument that describes the current state. If snstop or callback returns false, SNOPT stops the solve; any other return value lets the solve continue. SNOPT ignores the return value of snlog.

KeywordCalledArgumentCan stop the solve
snlogOnce per major iterationSnoptMajorLogNo
snstopOnce per major iterationSnoptStopEventYes
callbackAfter each evaluation of the objective or the constraintsNamedTupleYes

Use snlog to print or record progress. It takes the place of SNOPT's own major-iteration log, so the print and summary files have no iteration lines when snlog is given. Use snstop to stop the solve, or to read the gradients or the multipliers; it does not change the files. Use callback only when you need every function evaluation: SNOPT evaluates the functions several times per major iteration, so this function is called much more often.

result = snopt(objective, gradient!, x0;
               snlog = function (event)
                   println("iteration ", event.major_iter,
                           ": f = ", event.objective,
                           ", infeasibility = ", event.primal_infeasibility)
               end)

The most useful fields of SnoptMajorLog are:

FieldContent
major_iterNumber of the current major iteration.
objectiveCurrent objective value.
primal_infeasibilityConstraint violation, scaled.
dual_infeasibilityError in the optimality conditions, scaled.
xCurrent variables, followed by one slack variable per constraint.

The docstring of SnoptMajorLog lists all fields. SNOPT can scale the variables and the constraints to improve the numerical behaviour (option "Scale option"), and it computes the two infeasibility values for the scaled problem. They decrease to the tolerances in Set solver options as the solve converges.

Stop on a custom condition

This example stops the solve after 30 seconds of wall-clock time:

deadline = time() + 30.0
result = snopt(objective, gradient!, x0;
               snstop = event -> time() < deadline)
result.status   # SnoptStatus.StopInMonitor if the deadline was reached

When a function returns false, the result has a status from SnoptStatus.StopInFunction to SnoptStatus.StopInMonitor (codes 71–74), and result.x is the point at which SNOPT stopped. To limit only the number of iterations or the CPU time, use the options "Major iterations limit" or "Time limit" instead. The option "Time limit" measures CPU time, so use snstop to stop on wall-clock time, as in this example.

SnoptStopEvent has all the fields of SnoptMajorLog, and also the objective gradient gobj, the Jacobian values gcon, the multipliers pi and the scaled bounds bl and bu.

Observe each function evaluation

For snopt, the callback function receives a NamedTuple with these fields. The callback builders of the Low-level interface use other kind values and fields; their docstrings list them.

FieldContent
kind:objective or :constraint.
mode0 if SNOPT asked for the value only, 2 if it also asked for derivatives.
major_iter, minor_iterAlways 0 in this version. Use snlog for iteration counts.
xCopy of the point.
fObjective value. Only when kind == :objective.
cCopy of the constraint values. Only when kind == :constraint.

Before SNOPT starts, SNOPT.jl evaluates each function once at x0, with each entry clamped to its bounds [lb, ub]. This finds errors in the user functions early. callback is also called for these first evaluations.

If callback returns false during these first evaluations, SNOPT does not start. The result then has the status SnoptStatus.StopInFunction, and result.basis contains no information: a warm start from it is the same as a cold start.