From 7bd5b62aab5dee2dbd6da2c40f3e0c752b5136ce Mon Sep 17 00:00:00 2001 From: tmigot Date: Wed, 20 Dec 2023 13:32:01 +0100 Subject: [PATCH] Add JSO-compliant solver documentation [draft] --- docs/make.jl | 6 +++++- docs/src/jso-compliant.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 docs/src/jso-compliant.md diff --git a/docs/make.jl b/docs/make.jl index 8a24349..52dd77e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -9,7 +9,11 @@ makedocs( prettyurls = get(ENV, "CI", nothing) == "true", ), sitename = "SolverCore.jl", - pages = ["Home" => "index.md", "Reference" => "reference.md"], + pages = [ + "Home" => "index.md", + "JSO-compliant solvers" => "jso-compliant.md", + "Reference" => "reference.md", + ], ) deploydocs( diff --git a/docs/src/jso-compliant.md b/docs/src/jso-compliant.md new file mode 100644 index 0000000..df37b48 --- /dev/null +++ b/docs/src/jso-compliant.md @@ -0,0 +1,32 @@ +# JSO-compliant solvers + +The following are JSO-compliance expectations and recommendations that are implemented in JuliaSmoothOptimizers. + +## Mandatory + +- Create a function `solver_name(nlp::AbstractNLPModel; kwargs...)` that returns a `GenericExecutionStats`. + +## Recommended + +- Create a solver object `SolverName <: SolverCore.AbstractOptimizationSolver`. +- Store all memory-allocating things inside this object. This includes vectors, matrices, and possibly other objects. +- One of these objects should be `solver.x`, that stores the current iterate. +- Implement a constructor `SolverName(nlp; kwargs...)`. +- Implement `SolverCore.solve!(solver, nlp::AbstractNLPModel, stats::GenericExecutionStats)` and change `solver_name` to create a `SolverName` object and call `solve!`. +- Make sure that `solve!` is not allocating. +- Accept the following keyword arguments (`T` is float type and `V` is the container type): + - `x::V = nlp.meta.x0`: The starting point. + - `atol::T = sqrt(eps(T))`: Absolute tolerance for the gradient. Use in conjunction with the relative tolerance below to check $\Vert \nabla f(x_k)\Vert \leq \epsilon_a + \epsilon_r\Vert \nabla f(x_0)\Vert$. + - `rtol::T = sqrt(eps(T))`: Relative tolerance for the gradient. See `atol` above. + - `max_eval::Int = -1`: Maximum number of objective function evaluation plus constraint function evaluations. Negative number means unlimited. + - `max_iter::Int = typemax(Int)`: Maximum number of iterations. + - `max_time::Float64 = 30.0`: Maximum elapsed time. + - `verbose::Int = 0`: Verbosity level. `0` means nothing and `1` means something. There are no rules on the level of verbosity yet. + - `callback = (nlp, solver, stats) -> nothing`: A callback function to be called at the end of an iteration, before exit status are defined. +- Use `set_status!` and `get_status` to update `stats` before starting the method loop, and at the end of every iteration. +- Call the `callback` after running `set_status!` in both places. +- Define `done = stats.status != :unknown` and loop with `while !done`. +- To check for logic errors and stop the method use `set_status!(stats, ...)`, `done = true`, and `continue`, where the second argument of `set_status!` is one of the statuses available in SolverCore.STATUSES. You can call `SolverCore.show_statuses()` to see them. If you need more specific statuses, create an issue. +- Use the `set_...!(stats, ...)` functions from `SolverCore` to update the `stats`. For instance, `set_objective!(stats, f)`, `set_time!(stats, time() - start_time)`, and `set_dual_residual!(stats, gnorm)`. +- Don't log when `verbose == 0`. When logging, use `@info log_header(...)` and `@info log_row(...)`. +- Write docstrings for `SolverName`. The format is still a bit loose.