ParametricOptInterface.jl
ParametricOptInterface.jl is a package for managing parameters in JuMP and MathOptInterface.
License
ParametricOptInterface.jl is licensed under the MIT License.
Installation
Install ParametricOptInterface using Pkg.add:
import Pkg
Pkg.add("ParametricOptInterface")Use with JuMP
Use ParametricOptInterface with JuMP by following this brief example:
julia> using JuMP, HiGHSjulia> import ParametricOptInterface as POIjulia> model = Model(() -> POI.Optimizer(HiGHS.Optimizer))A JuMP Model ├ solver: Parametric Optimizer with HiGHS attached ├ objective_sense: FEASIBILITY_SENSE ├ num_variables: 0 ├ num_constraints: 0 └ Names registered in the model: nonejulia> @variable(model, x)xjulia> @variable(model, p in Parameter(1))pjulia> @constraint(model, p * x + p >= 3)p*x + p ≥ 3julia> @objective(model, Min, 2x + p)2 x + pjulia> optimize!(model)Running HiGHS 1.13.1 (git hash: 1d267d97c): Copyright (c) 2026 under Apache 2.0 license terms Using BLAS: blastrampoline LP has 1 row; 1 col; 1 nonzero Coefficient ranges: Matrix [1e+00, 1e+00] Cost [2e+00, 2e+00] Bound [0e+00, 0e+00] RHS [2e+00, 2e+00] Presolving model 0 rows, 0 cols, 0 nonzeros 0s 0 rows, 0 cols, 0 nonzeros 0s Presolve reductions: rows 0(-1); columns 0(-1); nonzeros 0(-1) - Reduced to empty Performed postsolve Solving the original LP from the solution after postsolve Model status : Optimal Objective value : 5.0000000000e+00 P-D objective error : 0.0000000000e+00 HiGHS run time : 0.00julia> value(x)2.0julia> set_parameter_value(p, 2.0)julia> optimize!(model)LP has 1 row; 1 col; 1 nonzero Coefficient ranges: Matrix [2e+00, 2e+00] Cost [2e+00, 2e+00] Bound [0e+00, 0e+00] RHS [1e+00, 1e+00] Solving LP with useful basis so presolve not used Model status : Optimal Objective value : 3.0000000000e+00 P-D objective error : 0.0000000000e+00 HiGHS run time : 0.00julia> value(x)0.5
The dual of a parameter
In some applications you may need the dual of a parameter. The dual can be computed only if the parameter appears additively in the problem. Query the dual associated with the parameter as follows:
julia> using JuMP, HiGHSjulia> import ParametricOptInterface as POIjulia> model = Model(() -> POI.Optimizer(HiGHS.Optimizer()));julia> set_silent(model)julia> @variable(model, x);julia> @variable(model, p in Parameter(1));julia> @constraint(model, x + p >= 3);julia> @objective(model, Min, 2x);julia> optimize!(model)julia> dual(VariableInSetRef(p))-2.0
Note how the dual is the same as the reduced_cost of an equivalent fixed variable:
julia> using JuMP, HiGHSjulia> model = Model(HiGHS.Optimizer);julia> set_silent(model)julia> @variable(model, x);julia> @variable(model, p == 1);julia> @constraint(model, x + p >= 3);julia> @objective(model, Min, 2x);julia> optimize!(model)julia> reduced_cost(p)-2.0
Note that, even though ParametricOptInterface supports querying the dual of a parameter, this is not true in general. For example, Ipopt does not support querying the dual of a parameter. Moreover, the dual is available only if all of the parameters appear additively in the model; the dual is not available if there are multiplicative parameters.
Variable bounds
An ambiguity arises when the user writes a model like:
julia> using JuMPjulia> model = Model();julia> @variable(model, x)xjulia> @variable(model, p in Parameter(2))pjulia> @constraint(model, x >= p)x - p ≥ 0
Did they mean an affine constraint like 1.0 * x + 0.0 in GreaterThan(2.0), or did they mean a variable bound like x in GreaterThan(2.0)?
By default, ParametricOptInterface does not attempt to simplify affine constraints involving parameters to variable bounds, but this behavior can be controlled using the ConstraintsInterpretation attribute.
For example, the default is:
julia> using JuMP, HiGHSjulia> import ParametricOptInterface as POIjulia> model = Model(() -> POI.Optimizer(HiGHS.Optimizer()));julia> set_attribute(model, POI.ConstraintsInterpretation(), POI.ONLY_CONSTRAINTS)julia> @variable(model, x);julia> @variable(model, p in Parameter(1));julia> @constraint(model, x >= p)x - p ≥ 0julia> optimize!(model)Running HiGHS 1.13.1 (git hash: 1d267d97c): Copyright (c) 2026 under Apache 2.0 license terms Using BLAS: blastrampoline LP has 1 row; 1 col; 1 nonzero Coefficient ranges: Matrix [1e+00, 1e+00] Cost [0e+00, 0e+00] Bound [0e+00, 0e+00] RHS [1e+00, 1e+00] Presolving model 0 rows, 0 cols, 0 nonzeros 0s 0 rows, 0 cols, 0 nonzeros 0s Presolve reductions: rows 0(-1); columns 0(-1); nonzeros 0(-1) - Reduced to empty Performed postsolve Solving the original LP from the solution after postsolve Model status : Optimal Objective value : 0.0000000000e+00 P-D objective error : 0.0000000000e+00 HiGHS run time : 0.00
but by setting the ConstraintsInterpretation attribute to POI.BOUNDS_AND_CONSTRAINTS, we can solve a problem with one decision variable and zero constraint rows:
julia> using JuMP, HiGHSjulia> import ParametricOptInterface as POIjulia> model = Model(() -> POI.Optimizer(HiGHS.Optimizer()));julia> set_attribute(model, POI.ConstraintsInterpretation(), POI.BOUNDS_AND_CONSTRAINTS)julia> @variable(model, x);julia> @variable(model, p in Parameter(1));julia> @constraint(model, x >= p)x - p ≥ 0julia> optimize!(model)Running HiGHS 1.13.1 (git hash: 1d267d97c): Copyright (c) 2026 under Apache 2.0 license terms Using BLAS: blastrampoline LP has 0 rows; 1 col; 0 nonzeros Solving unconstrained LP Solving an unconstrained LP with 1 columns Model status : Optimal Objective value : 0.0000000000e+00 P-D objective error : 0.0000000000e+00 HiGHS run time : 0.00
Parameters multiplying quadratic terms
POI supports parameters that multiply quadratic variable terms in objectives only. This creates cubic polynomial expressions of the form c * p * x * y where c is a number, p is a parameter, and x and y are variables. After parameter substitution, the objective is quadratic instead of cubic.
Note that the maximum degree is 3 (cubic), at least one factor in each cubic term must be a parameter, and pure cubic variable terms (for example, x * y * z with no parameters) are not supported.
julia> using JuMP, HiGHSjulia> import ParametricOptInterface as POIjulia> model = Model(() -> POI.Optimizer(HiGHS.Optimizer()))A JuMP Model ├ solver: Parametric Optimizer with HiGHS attached ├ objective_sense: FEASIBILITY_SENSE ├ num_variables: 0 ├ num_constraints: 0 └ Names registered in the model: nonejulia> set_silent(model)julia> @variable(model, 0 <= x <= 10)xjulia> @variable(model, p in Parameter(2))pjulia> @objective(model, Min, p * x^2 - 3x)(p * (x²)) - (3 x)julia> optimize!(model)julia> value(x) # x = 3 / 2p = 0.750.7499999812500006julia> set_parameter_value(p, 3)julia> optimize!(model)julia> value(x) # x = 3 / 2p = 0.50.4999999916666667