Optimization.jl puts two dozen Julia solver packages behind one interface
Mathematical Optimization in Julia. Local, global, gradient-based and derivative-free. Linear, Quadratic, Convex, Mixed-Integer, and Nonlinear Optimization in one simple, fast, and differentiable interface.
At a glance
- What is it?
- Optimization.jl is the SciML package that gathers local and global optimization packages behind a single Julia interface, so a strategy can be written once and run under Nelder-Mead, a black-box evolutionary method, BFGS with automatic differentiation, or a solver reached through MathOptInterface. The interface is deliberately thin, which means the interesting decisions are still yours.
- Who is it for?
- Adopt Optimization.jl when your Julia project needs to change solver without rewriting its problem definition, or when you want to compare a gradient-based method against a derivative-free one on the same objective. Do not adopt it as a way to reach solver specific options, because the unified interface deliberately exposes less than each underlying package does, and use the solver package directly when you need its full configuration.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Julia, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One problem definition, many solver packages
Optimization.jl describes itself as a package with a scope beyond a normal global optimization package. It seeks to bring together all of the optimization packages it can find, local and global, into one unified Julia interface, and the pitch in the README is that you learn one package and you learn them all.
The audience is a Julia developer who does not want to rewrite a problem every time the algorithm changes. A strategy written against this interface can be run under a derivative-free simplex method, a black-box evolutionary algorithm, a quasi-Newton method with gradients supplied by automatic differentiation, or a mixed integer solver reached through MathOptInterface, without the problem definition changing.
What sits on top of the solvers is a set of high-level features, and the README names automatic differentiation integration as the main one, with the aim of making usage fairly simple for most cases while still allowing all of the options in a single interface.
The package lives in the SciML organisation, the same group that maintains the scientific machine learning stack in Julia, and it is MIT licensed. The documentation is published at docs.sciml.ai, with a stable version and a separate in-development version that contains the unreleased features.
The repository layout shows the monorepo shape: lib for the subpackages, src and test, a benchmark directory, and docs, with a Project.toml at the root and a NEWS.md for release notes.
OptimizationBase is the interface, the solvers are separate
The installation section is where the architecture becomes visible, and it has a detail that surprises people.
Assuming Julia is installed, the README says it suffices to import OptimizationBase in the standard way:
using Pkg
Pkg.add("OptimizationBase")Then it says the packages relevant to the core functionality are imported accordingly and that in most cases you do not have to worry about the manual installation of dependencies. But then it lists the packages that need to be installed explicitly if you intend to use the specific optimization algorithms offered by them, and that list runs to about two dozen entries.
The names follow a pattern that makes the family obvious. OptimizationOptimJL for Optim.jl, OptimizationIpopt for Ipopt.jl, OptimizationLBFGSB, OptimizationMadNLP, OptimizationNLopt, OptimizationPRIMA, OptimizationNOMAD, OptimizationBBO for BlackBoxOptim.jl, OptimizationEvolutionary for Evolutionary.jl, OptimizationCMAEvolutionStrategy, OptimizationGCMAES, OptimizationQuadDIRECT, OptimizationMetaheuristics, OptimizationManopt for optimization on manifolds, OptimizationAuglag for augmented Lagrangian methods, OptimizationPolyalgorithms, OptimizationSophia, OptimizationSpeedMapping, OptimizationSciPy and OptimizationPyCMA via PythonCall, and OptimizationMOI for MathOptInterface usage.
So the model is a set of adapter packages, one per solver, all speaking the same interface. The core package is small, and the weight of the dependency tree lands on the solver packages you actually choose.
The same objective under three algorithms
The README's example makes the payoff concrete. The objective is the Rosenbrock function, defined with a parameter vector, and it is solved three ways in the documentation.
The first run uses a derivative-free simplex method from Optim.jl:
using OptimizationBase
rosenbrock(x, p) = (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2
x0 = zeros(2)
p = [1.0, 100.0]
prob = OptimizationProblem(rosenbrock, x0, p)
using OptimizationOptimJL
sol = solve(prob, NelderMead())The reported output shows a status of success, a final objective value of 3.525527e-09, 60 iterations and 118 calls to f(x), with the convergence measure written out as the root mean square deviation of the iterates.
The second run swaps in a black-box evolutionary method from BlackBoxOptim and, note this, adds bounds to the problem:
using OptimizationBBO
prob = OptimizationProblem(rosenbrock, x0, p, lb = [-1.0, -1.0], ub = [1.0, 1.0])
sol = solve(prob, BBO_adaptive_de_rand_1_bin_radiuslimited())The third run keeps the same problem object shape but changes the objective into a differentiable function, and it converges in fewer iterations with gradient calls recorded separately:
using ForwardDiff, ADTypes
f = OptimizationFunction(rosenbrock, ADTypes.AutoForwardDiff())
prob = OptimizationProblem(f, x0, p)
sol = solve(prob, BFGS())That output lists a final objective value of 7.645684e-21, 16 iterations, 53 f(x) calls and 53 gradient calls, plus a table of convergence measures for the iterate difference, the objective difference and the gradient norm.
The pattern to take away is that the problem object is the stable part and the algorithm, the bounds and the differentiation backend are the swappable parts.
Automatic differentiation is a first-class argument
The third example above is the one that matters most for a scientific computing audience, because it shows how the interface handles gradients.
The objective is wrapped in an OptimizationFunction that carries an ADTypes backend, in this case ADTypes.AutoForwardDiff(). ForwardDiff is the package doing the work, and the backend type is what tells the solver how to obtain a gradient when the solver asks for one.
The reason this is a library decision rather than a user detail is that the same problem can be handed to a solver that wants no derivatives, such as Nelder-Mead, or to one that wants them, such as BFGS, and the problem definition stays the same. The output for the gradient run records f(x) calls and gradient calls in separate counters, 53 of each, which is the kind of accounting you need when you care about the cost of a solve.
The ADTypes indirection also means other differentiation backends can be substituted without changing the solver call. The README does not enumerate them, so the current set of supported backends is a question for the ADTypes package rather than for this one.
For a team with a differentiable model, this is the feature that removes the usual friction. Without it, every gradient-based solver needs its own wrapper, and the wrappers diverge.
MathOptInterface and NLopt are the two bridges out
Two of the adapter packages exist to reach solvers that already have a well established API, and they are worth understanding separately.
OptimizationMOI wraps MathOptInterface, and the README describes it as the usage of the algorithm via the MathOptInterface API. The comment in the explicit installation list links to the MathOptInterface documentation. This is the path for solvers that ship as MathOptInterface models, and it is the reason a JuMP-style modelling layer can sit in front of a solver chosen through this interface.
OptimizationNLopt does the same for the NLopt API, and the README links to the list of NLopt algorithms.
The rest of the list follows a third pattern, where the adapter gives a uniform handle on a package that would otherwise be used directly: OptimizationOptimJL for Optim.jl, OptimizationIpopt for Ipopt.jl, OptimizationLBFGSB, OptimizationMadNLP, OptimizationPRIMA, OptimizationNOMAD, OptimizationEvolutionary for Evolutionary.jl, OptimizationBBO for BlackBoxOptim.jl.
Two entries are for problem classes rather than solvers. OptimizationManopt is for optimization on manifolds, and OptimizationODE is for optimization of steady state and time dependent ODE problems, which is the case where your objective is not a function of a vector but of a trajectory.
Two entries reach outside Julia. OptimizationSciPy and OptimizationPyCMA call Python implementations through PythonCall, which means a Julia user can use a solver written in Python without leaving the unified interface, at the cost of a Python dependency in the environment.
What the unified interface does not decide for you
There is a real trade-off in a facade like this, and the README states it in a sentence worth reading twice: it adds a few high-level features, such as integrating with automatic differentiation, to make its usage fairly simple for most cases, while allowing all of the options in a single unified interface.
The phrase fairly simple for most cases is the honest part. A common interface is a common subset. When a solver has an option that no other solver has, the interface either has no place for it, has a solver-specific extension, or exposes it in a way that only makes sense for one backend. The adapter packages are where that decision is made per algorithm, and a user who needs a specific solver's full option set ends up reading the adapter's source or dropping to the solver package directly.
The other cost is the dependency surface. A project that wants to compare three algorithms ends up installing three adapter packages and their solver dependencies, and those solver libraries bring their own dependency trees and their own version constraints. The README is candid that you must install the algorithm packages explicitly, so this is visible rather than hidden, but it is still a decision with a price.
There is also a documentation boundary. The stable documentation is what matches a released version, and the dev documentation contains unreleased features, so a feature you read about in the dev docs may not be in the version your Project.toml resolves. For a package that releases frequently, that is normal, but it is a reason to read the stable docs when you are pinning.
None of this is an argument against the package. It is an argument for reading the adapter documentation for the specific solver you intend to use, and for trying the vanilla interface first.
Releases, subpackages and the cost of an upgrade
The release history shows how the monorepo publishes. The most recent tags are OptimizationMOI-v1.6.0 on 2026-10-19, OptimizationBase-v5.10.0 on 2026-10-19 and OptimizationOptimisers-v0.3.26 on 2026-10-17, and the last push to the master branch was on 2026-10-19.
Each tag names a subpackage rather than the umbrella. OptimizationBase is at 5.x, the MathOptInterface bridge at 1.6, and the machine learning optimisers package at 0.3, so the versions move independently. In practice that means your Project.toml pins the base package and, if you use one of the adapters, that adapter's version too, and they do not all move in step.
The licence is MIT, which is the permissive choice a research and education focused project usually makes, and the package carries a Zenodo DOI badge, which is the convention for a package that wants to be citable in academic work. There is also a ColPrac contributor's guide badge and a SciML code style badge, so the project follows the community's shared practices rather than inventing its own.
The repository has a NEWS.md for release notes and a benchmark directory, which suggests the maintainers care about how solvers compare, and a docs directory for the manual sources.
The practical upgrade advice is short. Pin the base package and the adapter you use, read the NEWS.md before bumping the adapter, and keep the solver packages themselves in the same range, since a major bump in a solver library is where an interface break is most likely to show up.
Editorial conclusion
Adopt Optimization.jl when your Julia project needs to change solver without rewriting its problem definition, or when you want to compare a gradient-based method against a derivative-free one on the same objective. Do not adopt it as a way to reach solver specific options, because the unified interface deliberately exposes less than each underlying package does, and use the solver package directly when you need its full configuration. Before you commit, check the explicit installation list, since the algorithm packages are not pulled in automatically, and read the stable documentation rather than the dev documentation when you want behaviour that matches a released version.
Frequently asked questions
How do I install Optimization.jl in Julia?
Add the core interface package with Pkg.add("OptimizationBase"). The algorithm packages are not installed automatically, so you also add the specific ones you want, such as OptimizationOptimJL, OptimizationBBO, OptimizationIpopt or OptimizationMOI.
How do I use automatic differentiation with an Optimization.jl solver?
Wrap the objective in an OptimizationFunction that carries an ADTypes backend, for example OptimizationFunction(rosenbrock, ADTypes.AutoForwardDiff()), and pass that to the OptimizationProblem. The README's BFGS example then reports separate counters for f(x) and gradient calls.
What is the difference between OptimizationBase and the Optimization algorithm packages?
OptimizationBase provides the unified problem and solve interface, while each adapter package, named Optimization followed by the solver, adapts one specific solver to it. The README says the packages relevant to the core functionality are imported accordingly, but the algorithm packages have to be installed explicitly if you want to use them.
Where can I read the Optimization.jl documentation?
The stable documentation is at docs.sciml.ai/Optimization/stable/, and a separate in-development version at docs.sciml.ai/Optimization/dev/ contains the unreleased features. The project also points to a Zulip chat and a Discord channel through its README badges.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sciml-optimization-jl)Was this useful?