# Standard-Use Example 2

This example is also provided here as a Jupyter Notebook.

### Using EAGO's Basic Optimizer With User-Defined Subroutines

In this section, we construct an optimizer that uses EAGO's basic NLP solution routine with user-defined lower and upper-bounding problems. The `Optimizer`

structure supplies a number of parameters and stored structures that advanced users may find useful for constructing specialized solution routines.

In this example, we'll forgo extensive integration into the `Optimizer`

and simply replace the lower and upper-bounding problems to construct a B&B routine that solves the following problem to global optimality using bounds obtained from interval arithmetic:

\[\begin{aligned} & \min_{\mathbf x \in X} \; \; \sin(x_{1}) x_{2}^{2} - \cos(x_{3}) / x_{4} \\ & X = [-10, 10] \times [-1, 1] \times [-10, 10] \times [2, 20]. \end{aligned}\]

We begin by importing EAGO, IntervalArithmetic [1], and JuMP [2].

`using EAGO, IntervalArithmetic, JuMP`

We now define the `IntervalExt`

struct as a subtype of the `ExtensionType`

.

`struct IntervalExt <: EAGO.ExtensionType end`

## Define a Custom Lower-Bounding Problem

A valid lower bound is obtained from the lower bound of the natural interval extension using the IntervalArithmetic.jl [1] package. The `LowerProblem`

is dispatched using the new `IntervalExt`

structure and the `GlobalOptimizer`

structure, computes the bound using interval arithmetic, and stores the results to the appropriate field of the `GlobalOptimizer`

. Note that the problem is unconstrained on the domain so we can assume it is always feasible. Further, since the interval bound is constrained along the entire domain associated with a node, no additional cuts will be beneficial and thus we've disabled them using the `_cut_add_flag`

field.

```
import EAGO: lower_problem!
function lower_problem!(t::IntervalExt, x::EAGO.GlobalOptimizer)
# Retrieve bounds at current node
n = x._current_node
lower = n.lower_variable_bounds
upper = n.upper_variable_bounds
# Define X for the node and compute the interval extension
x_value = Interval.(lower, upper)
F = sin(x_value[1])*x_value[2]^2 - cos(x_value[3])/x_value[4]
x._lower_objective_value = F.lo
x._lower_solution = IntervalArithmetic.mid.(x_value)
x._lower_feasibility = true
x._cut_add_flag = false
return
end
```

## Define a Custom Upper-Bounding Problem

Since the problem is unconstrained, any feasible point represents a valid upper bound. Thus, if we arbitrarily evaluate the function at the midpoint, we obtain a valid upper bound. This function constructs an upper bound in this manner then stores the results to the appropriate field of the `GlobalOptimizer`

.

```
import EAGO.upper_problem!
function EAGO.upper_problem!(t::IntervalExt, x::EAGO.GlobalOptimizer)
# Retrieve bounds at current node
n = x._current_node
lower = n.lower_variable_bounds
upper = n.upper_variable_bounds
# Compute midpoint value and evaluate at that point
x_value = 0.5*(upper + lower)
f_val = sin(x_value[1])*x_value[2]^2-cos(x_value[3])/x_value[4]
x._upper_objective_value = f_val
x._upper_solution = x_value
x._upper_feasibility = true
return
end
```

## Disable Unnecessary Routines

It is entirely possible to disable domain reduction by manipulating keyword arguments supplied to the optimizer. However, for simplicity's sake we'll simply overload the default preprocessing and postprocessing methods and indicate that there are no conditions under which EAGO should cut the node.

```
import EAGO: preprocess!, postprocess!, cut_condition
function EAGO.preprocess!(t::IntervalExt, x::EAGO.GlobalOptimizer)
x._preprocess_feasibility = true
return
end
function EAGO.postprocess!(t::IntervalExt, x::EAGO.GlobalOptimizer)
x._postprocess_feasibility = true
return
end
EAGO.cut_condition(t::IntervalExt, x::EAGO.GlobalOptimizer) = false
```

## Construct the JuMP Model and Optimize

We now add our optimizer to a JuMP [2] model, provide variable bounds, and optimize.

```
# Create a factory that specifies the interval extension in EAGO's SubSolver
factory = () -> EAGO.Optimizer(SubSolvers(; t = IntervalExt()))
# Create a JuMP model using the factory, and with the absolute tolerance set by keyword argument
m = Model(optimizer_with_attributes(factory,
"absolute_tolerance" => 0.001
))
# Add variables, bounds, and the objective function
x_L = [-10.0, -1.0, -10.0, 2.0]
x_U = [10.0, 1.0, 10.0, 20.0]
@variable(m, x_L[i] <= x[i=1:4] <= x_U[i])
@NLobjective(m, Min, sin(x[1])*x[2]^2 - cos(x[3])/x[4])
# Perform the optimization
optimize!(m)
```

## Retrieve Results

The objective value, solution, termination status, and primal status can then be accessed via the standard JuMP interface.

```
fval = JuMP.objective_value(m)
xsol = JuMP.value.(x)
status_term = JuMP.termination_status(m)
status_prim = JuMP.primal_status(m)
println("EAGO terminated with a status of $status_term and a result code of $status_prim")
println("The optimal value is: $(round(fval, digits=3)), the solution found is $(round.(xsol, digits=4)).")
```

## Advice for More Advanced Constructions

The default `lower_problem!`

and `upper_problem!`

should be used as templates for error handling and retrieving information from MOI models.

Essentially all of EAGO's subroutines stored to a field in the `Optimizer`

structure can be reset as user-defined functions.

## References

- IntervalArithmetic.jl [Computer software] (2019). Retrieved from https://github.com/JuliaIntervals/IntervalArithmetic.jl
- Iain Dunning and Joey Huchette and Miles Lubin. JuMP: A Modeling Language for Mathematical Optimization, SIAM Review, SIAM 59 (2017), pp. 295-320.