How to State for NLPs

The data used through the algorithmic process in the Stopping framework are stored in a State. We illustrate here the NLPAtX which is a specialization of the State for non-linear programming.

The Julia file corresponding to this tutorial can be found here.

using Test, NLPModels, Stopping

Formulate the problem with NLPModels:

include("../test-stopping/rosenbrock.jl")
x0   = ones(6)
y0   = ones(1)
c(x) = [x[1] - x[2]]
lcon = [0.0]
ucon = [0.0]

We can create a NLPAtX for constrained optimization. Here we provide y0 = [1.0]. Note that the default value is [0.0].

nlp = ADNLPModel(x -> rosenbrock(x), x0, y0 = y0,
                 c = c, lcon = lcon, ucon = ucon,
                 lvar = zeros(6), uvar = Inf * ones(6))

We can create a NLPAtX for bounds-constrained optimization:

nlp2 = ADNLPModel(x -> rosenbrock(x), x0,
                 lvar = zeros(6), uvar = Inf * ones(6))

We can create a NLPAtX for unconstrained optimization:

nlp3 = ADNLPModel(x -> rosenbrock(x), x0)

I. Initialize a NLPAtX:

There are two main constructor for the States. The unconstrained:

state_unc = NLPAtX(x0)

The constrained:

state_con = NLPAtX(x0, y0)

By default, all the values in the State are set to nothing except x and lambda In the unconstrained case lambda is a vector of length 0.

@test !(state_unc.lambda == nothing)

From the default initialization, all the other entries are void:

@test state_unc.mu == nothing && state_con.mu == nothing
@test state_unc.fx == nothing && state_con.fx == nothing

An exception is the counters which is initialized as a default Counters:

@test (sum_counters(state_unc.evals) + sum_counters(state_con.evals)) == 0

Note that the constructor proceeds to a size checking on gx, Hx, mu, cx, Jx. It returns an error if this test fails.

try
  NLPAtX(x0, Jx = ones(1,1))
  @test false
catch
  #printstyled("NLPAtX(x0, Jx = ones(1,1)) is invalid as length(lambda)=0\n")
  @test true
end

II. Update the entries

At the creation of a NLPAtX, keyword arguments populate the state:

state_bnd = NLPAtX(x0, mu = zeros(6))
@test state_bnd.mu == zeros(6) #initialize multipliers with bounds constraints

The NLPAtX has two functions: update! and reinit! The update! has the same behavior as in the GenericState:

update!(state_bnd, fx = 1.0, blah = 1) #update! ignores unnecessary keywords
@test state_bnd.mu == zeros(6) && state_bnd.fx == 1.0 && state_bnd.x == x0

reinit! by default reuse x and lambda and reset all the entries at their default values (void or empty Counters):

reinit!(state_bnd, mu = ones(6))
@test state_bnd.mu == ones(6) && state_bnd.fx == nothing
@test state_bnd.x == x0 && state_bnd.lambda == zeros(0)

Trying to inherit reinit!(AbstractState, Vector) would not work here as lambda is a mandatory entry.

try
 reinit!(state_bnd, 2 * ones(6))
 @test false
catch
 @test true
end

However, we can specify both entries

reinit!(state_bnd, 2 * ones(6), zeros(0))
@test state_bnd.x == 2*ones(6) && state_bnd.lambda == zeros(0)
@test state_bnd.mu == nothing && sum_counters(state_bnd.evals) == 0

Giving a new Counters update as well:

test = Counters(); setfield!(test, :neval_obj, 102)
reinit!(state_bnd, evals = test)
@test getfield(state_bnd.evals, :neval_obj) == 102
@test sum_counters(state_bnd.evals) - 102 == 0

III. Domain Error

Similar to the GenericState we can use domain_check to verify there are no NaN

@test Stopping._domain_check(state_bnd) == false
update!(state_bnd, fx = NaN)
@test Stopping._domain_check(state_bnd) == true

IV. Use the NLPAtX

For algorithmic use, it might be conveninent to fill in all the entries of then State. In this case, we can use the Stopping:

stop = NLPStopping(nlp, (x,y) -> unconstrained_check(x,y), state_unc)

Note that the fillin! can receive known informations via keywords. If we don't want to store the hessian matrix, we turn the keyword matrixinfo as false.

fill_in!(stop, x0, matrix_info = false)
@test stop.current_state.Hx == nothing

We can now use the updated step in the algorithmic procedure

@test start!(stop) #return true