JuMP model optimisation

PortfolioOptimisers.jl is based on JuMP, as such it tries to be as flexible as possible.

These types and functions let us define solver and solution interfaces.

PortfolioOptimisers.SolverType
struct Solver{__T_name, __T_solver, __T_settings, __T_check_sol, __T_add_bridges} <: AbstractEstimator

Configures one solver backend, its attributes, and the statuses its solutions must reach.

Every optimiser takes one Solver or a vector of them. optimise_JuMP_model! tries them in order and stops at the first that returns a solution check_sol accepts, so a vector is a fallback chain.

Fields

  • name: Symbol or string identifier. It is also the key under which optimise_JuMP_model! files this solver's failure in JuMPResult's trials, so two solvers that share a name share one entry and the later failure overwrites the earlier one. Give each solver of a vector its own name, because the default "" is shared by all of them.
  • settings: Optional solver-specific settings used in set_attribute.
  • check_sol: Named tuple of keyword arguments splatted into assert_is_solved_and_feasible after each solve. It decides which solver statuses count as a solved model. The default (;) accepts JuMP's own defaults, allow_local = true and allow_almost = false: the termination status must be OPTIMAL or LOCALLY_SOLVED, and the primal status must be FEASIBLE_POINT. The strictness is deliberate — a solution the solver itself flags as approximate is rejected rather than silently accepted, so a solver stage that fails this check falls through to the next solver in the vector. The common relaxation is check_sol = (; allow_local = true, allow_almost = true), which also accepts ALMOST_OPTIMAL, ALMOST_LOCALLY_SOLVED and NEARLY_FEASIBLE_POINT; it is what the examples, the user guide and the test suite pass, because a first-order solver reaching its tolerance on a well-posed portfolio problem is usually good enough. Go the other way with allow_local = false to reject LOCALLY_SOLVED and demand a certified global optimum, and add dual = true to also require a feasible dual point.

Constructors

Solver(;    name::Sym_Str = "",    solver::Any,    settings::Option{<:SlvSettings} = nothing,    check_sol::NamedTuple = (;),    add_bridges::Bool = true) -> Solver

Keywords correspond to the struct's fields.

Validation

  • If not nothing, !isempty(settings).

Examples

The default check_sol is strict — it accepts only a solution the solver reports as OPTIMAL or LOCALLY_SOLVED at a FEASIBLE_POINT.

julia> Solver(; solver = nothing)Solver         name ┼ String: ""       solver ┼ nothing     settings ┼ nothing    check_sol ┼ @NamedTuple{}: NamedTuple()  add_bridges ┴ Bool: true

To also accept an approximate solution, which is the common case and what the examples, user guide and tests use:

julia> Solver(; solver = nothing, check_sol = (; allow_local = true, allow_almost = true))Solver         name ┼ String: ""       solver ┼ nothing     settings ┼ nothing    check_sol ┼ @NamedTuple{allow_local::Bool, allow_almost::Bool}: (allow_local = true, allow_almost = true)  add_bridges ┴ Bool: true

Related

source
PortfolioOptimisers.JuMPResultType
struct JuMPResult{__T_trials, __T_success} <: AbstractJuMPResult

Records which solvers failed, at which stage, and whether any of them succeeded.

When success is false the constructor emits a warning built by failed_solve_msg: one bounded line per failed solver stage (name, stage, first line of the error). The full per-solver exceptions and settings stay available on trials and are never dumped into the log.

trials records failures only. A solver that succeeds leaves no entry, so an empty trials with success = true means the first solver answered, and it is not a record of the solve. Each entry is keyed by the solver's name and holds a dictionary from the failed stage — :set_optimizer, :optimize! or :assert_is_solved_and_feasible — to the exception. Two solvers that share a name share one entry, and the default name is "" for all of them, so a vector of unnamed solvers keeps only its last failure.

Fields

  • trials: Dictionary of solver trials and errors.
  • success: Boolean indicating whether optimisation succeeded.

Constructors

JuMPResult(;    trials::AbstractDict,    success::Bool) -> JuMPResult

Keywords correspond to the struct's fields.

Examples

julia> JuMPResult(; trials = Dict(:HiGHS => Dict(:optimize! => "error")), success = true)JuMPResult   trials ┼ Dict{Symbol, Dict{Symbol, String}}: Dict(:HiGHS => Dict(:optimize! => "error"))  success ┴ Bool: true

Related

source