First off, thanks for taking the time to contribute!
If you think you found a bug, feel free to open an issue. Focused suggestions and requests can also be opened as issues. Before opening a pull request, please start an issue or a discussion.
If you want to ask a question not suited for a bug report, feel free to start a discussion here, a forum for general discussion about this repository and the JuliaSmoothOptimizers organization. Discussions about any of our packages are welcome.
We welcome pull requests proposing new problems to the problem set. As a general guideline, a pull request should concern one problem only. We recommend checking existing problems as a template for your new problems.
Here is a to-do list, to help you add new problems:
- Before implementing a new problem, make sure it does not already exist in this repository.
- This package contains implementations using
JuMPandADNLPModels. A pull request should include both implementations of a new problem. Additionally, a "meta" provides general information regarding the problem. Therefore, a PR adding a new problem should contain 3 files:src/ADNLPProblems/problem_name.jlsrc/PureJuMP/problem_name.jlsrc/Meta/problem_name.jlIn both cases, the function must have the same nameproblem_nameas the file.
- When submitting a problem, please pay particular attention to the documentation. We would like to gather as much information as possible on the provenance of problems, other problem sets where the problems are present, and general information on the problem.
The documentation should be added to the file in the
PureJuMPfolder. - New problems can be scalable, see ADNLPProblems/arglina.jl and PureJuMP/arglina.jl for examples. In that case, the first keyword parameter should be the number of variables
n::Intand have the default valuedefault_nvar(constant predefined in the module). If your problem has restrictions on the number of variables, e.g.,nshould be odd, ornshould have the form4k + 3, then, instead of throwing errors when the restrictions are not satisfied, you should instead use the number of variables to be as close tonas possible. For example, if you wantnodd andn = 100is passed, you can internally convert ton = 99. If you wantn = 4k + 3, andn = 100is passed, then computek = round(Int, (n - 3) / 4)and updaten. - A first version of the
metacan be generated usinggenerate_meta. AStringis returned that can be copy-pasted into theMetafolder, and then edited.
using ADNLPModels, Distributed, NLPModels, NLPModelsJuMP, OptimizationProblems, Test
include("test/utils.jl")
# there must exists a function `problem_name` which loads the model in the environment,
# it must be exported.
create_meta_files(String["catmix", "gasoil", "glider", "methanol", "pinene", "rocket", "steering"])- Problems modeled with
ADNLPModelsshould be type-stable, i.e. they should all have keyword argumenttype::Type{T} = Float64whereTis the type of the initial guess and the type used by theNLPModelAPI. - In particular, the initial point
x0should be aVector{T}, the objective evaluation should return values of typeT, and thenamekeyword should be passed toADNLPModel/ADNLSModelwith a meaningful problem name.
In order to standardize the new functions, we offer here a template for both AD and JuMP models.
First, we describe the PureJuMP file function_name.jl. This file contains the documentation on the problem.
# Full name of the problem (while function_name could be an abbreviation)
#
# Source of the problem
# Don't hesitate to put more than one source if it is mentioned elsewhere
#
# CUTEst classification (if available)
#
# other information related to the problem
#
export function_name
"A short docstring on the problem"
function function_name(; n::Int = default_nvar, kwargs...)
nlp = Model()
# define the model: TODO
return nlp
end
Next, we describe the ADNLPProblems file function_name.jl.
export function_name
function function_name(; n::Int = default_nvar, type::Type{T} = Float64, kwargs...) where {T}
# define f
# define x0
# ensure x0 isa Vector{T} and f(x) returns T
# nlp = ADNLPModels.ADNLPModel(f, x0, name = "function_name"; kwargs...)
return nlp
end
- Ensure all meta fields are accurate and complete.
- For implementations in both
ADNLPProblemsandPureJuMP, use the same initial point, variable bounds, constraint bounds and explicitly compare the two models to ensure objective and constraint values match within a relative tolerance. - The objective of implementations must be callable at the starting point.
- For
ADNLPModelsproblems, the objective should return values of typeTfromtype::Type{T}and the initial point should be typed consistently (x0::Vector{T}). - Pass a meaningful
namekeyword toADNLPModel/ADNLSModelconstructors (typically matching the problem/function name). - For least-squares problems, support the
use_nls=true/falsekeyword to allow bothADNLPModelandADNLSModelinstantiation from the same problem. - For least-squares problems, instantiate both
ADNLPModelandADNLSModeland ensureresidual!(nls, x, Fx)is allocation-free and that the objectives agree (or differ by a factor of 2 for LS). - For constrained problems, ensure in-place constraint evaluations (e.g.,
cons_nln!) are allocation-free. - Objective evaluations should have minimal allocations (preferably zero allocations in hot paths).
- For variable-size problems, validate at multiple sizes (for example
n = 5,n = default_nvar, and a largern) and check all of the following:- model instantiation succeeds for each tested
n; - effective
nvarmatches the intended rule (including any internal adjustment such as oddnor4k + 3constraints); - metadata formulas (
nvar,nnzh,nnzj, etc.) match the instantiated model values.
- model instantiation succeeds for each tested
Optional (recommended): provide a local solver sanity check showing that a standard solver can solve the model from the provided starting point. This is not a hard requirement for CI or review.
using OptimizationProblems, OptimizationProblems.ADNLPProblems
using NLPModelsIpopt
nlp = problem_name()
stats = ipopt(nlp)
stats.statusFor least-squares problems, you may also run the same check with problem_name(use_nls=true).
If your problem is a nonlinear least squares (NLS), please follow these guidelines:
- Use
ADNLSModelsfor the ADNLPProblems implementation (see ADNLPModels.jl). - Set the
:objtypeentry in the meta file to:least_squares. - Add a getter for the number of NLS equations, named
get_problemname_nls_nequ. - In the
PureJuMPfile, clearly document that the problem is a nonlinear least squares (NLS) problem and explain how users can construct both the standard and NLS variants. - Explicitly state that the NLS variant can be accessed by passing the keyword argument
use_nls=truewhen constructing the problem, e.g.:# Standard model (ADNLPModel) myprob_nlp = myprob() # Least-squares model (ADNLSModel) myprob_nls = myprob(use_nls=true)
- Make sure this information is also reflected in the meta file, so users and tools can easily discover the NLS capability.
See existing NLS problems (e.g.,
lanczos1,lanczos2,brownal) for templates.
- First check: the problem is added in exactly these three files with the same base name:
src/ADNLPProblems/problem_name.jl,src/PureJuMP/problem_name.jl, andsrc/Meta/problem_name.jl. Example:arglinain ADNLPProblems,arglinain PureJuMP, andarglinain Meta.
Meta
- The corresponding meta file exists (
src/Meta/problem_name.jl), the problem name matches the AD and JuMP files, andOptimizationProblems.metacontains the problem entry. - All meta fields (origin, objtype, contype, bounds, best-known, etc.) are filled correctly.
- Meta formulas for variable sizes match actual model behavior.
Definition
- No extra or spurious exports are introduced.
- Model name matches the file and function name.
- The implemented objective, constraints, and bounds match the mathematical problem definition from the cited reference/documentation.
Implementation
- Objective and constraint values agree (ADNLPProblems vs PureJuMP) within tolerance at test points.
- Number of variables and constraints match.
- For
type::Type{T},x0 isa Vector{T}and objective values are of typeT. -
ADNLPModel/ADNLSModelconstructors receive a meaningfulnamekeyword.
Sanity
- Objective is callable at the starting point and does not return NaN (unless documented).
- Model instantiates without error for different types, e.g. Float32 and Float64.
- For scalable problems, changing
nupdatesnvarand all related meta fields, and the effective number of variables remains as close as possible to the requestednwhen internal adjustments are required.
Zero-Allocation
- All in-place APIs (constraints, residuals) are allocation-free.
- No unnecessary allocations in tight loops or callbacks.
- Objective evaluation has minimal allocations (ideally allocation-free in performance-critical paths).
Least-Squares & In-Place APIs
- If least-squares, ADNLP constructor supports
nls=true/falsefor both ADNLPModel and ADNLSModel. - In-place nonlinear constraint evaluation (
cons_nln!(nlp, x, cx)) and least-squares residuals (residual!) are allocation-free. - For least-squares, objectives for NLP and NLS agree (or differ by a factor of 2, as appropriate).