Skip to content

Add Parameter{T} <: AbstractScalarSet #2094

Closed
@odow

Description

@odow

Today I had a call with @sstroemer and @jd-lara, and we discussed the logistics of adding a Parameter{T} <: AbstractScalarSet to MOI.

Motivation

The Parameter{T} set is similar to the EqualTo{T} set, but with the special case that solvers could support add_constrained_variable(model, ::Parameter) and not add the index as a decision variable to the underlying solver object.

The goal is to support JuMP syntax like:

model = Model()
@variable(model, x)
@variable(model, p in MOI.Parameter(1.0))
# or
@variable(model, p == 1.0, Parameter)
@constraint(model, p * x <= p + 1)
@objective(model, Min, p * x + 3 * p)
optimize!(model)
fix(p, 2.0)
optimize!(model)

The benefits of an explicit parameter are that, compared to EqualTo, they can reduce the problem size and allow solvers to interpret p * x as an affine function instead of quadratic.

Backstory

We've tried quite a few ways to achieve this over the years...

EqualTo

The easiest approach is just to use fixed variables a parameters and let the solvers presolve them out. This works for additive parameters, but fails for p * x if the solver does not support quadratic constraints.

Another downside is that it leads to more decision variables in the problem because p is added as a decision variable.

Nonlinear

JuMP already has nonlinear parameters, which are added via @NLparameter. If we add a NonlinearFunction, #2059, then we'll need some way to add parameters to MOI anyway.

The current parameter implementation is very specialized inside MOI.Nonlinear.Model.

ParameterJuMP

@joaquimg started ParameterJuMP.jl to support right-hand side parameters in JuMP.

The core premise is that it is a JuMP extension which defines a new ParameterRef <: AbstractVariableRef, and then stores parameterized JuMP expressions as a double expression:

struct ParameterizedAffExpr
    lhs::GenericAffExpr{V,VariableRef}
    rhs::GenericAffExpr{V,ParameterRef}
end

There are two main downsides to this approach:

  • Two expressions, coupled with Julia's compiler optimizations which can sometimes put an AffExpr on the stack instead of heap, means that memory overhead can be 2-3X larger than a JuMP model with the EqualTo approach.
  • It doesn't support p * x; only additive terms are supported.

ParametricOptInterface

To work-around the two problems in ParameterJuMP, the PUC-Rio/PSR folks (@guilhermebodin, @rafabench, and @tomasfmg) have been developing https://github.com/jump-dev/ParametricOptInterface.jl.

This is a MOI solver layer that adds a ParametricOptInterface.Parameter set, and then intercepts the model to replace parameters by their fixed constants.

The main downsides are the complexity of the implementation, and how much state is needed to be stored inside the optimizer to track where and when the parameters are stored.

Recent work

@sstroemer has been hard at work exploring this, also motivated by an energy systems model.

One idea is to use the recently added Bridges.final_touch to rewrite quadratic constraints to affine:
#2092

Another is to add support at the JuMP-level to track parameters: jump-dev/JuMP.jl#3215.

Loosely, these correspond to the approaches in ParametricOptInterface and ParameterJuMP.

Proposal

The basic part of the proposal is to add a MOI.Parameter set to MOI. We already know this is useful, because it is identical to what is added by ParametericOptInterface. In addition, we're going to need it for the nonlinear stuff.

The more complicated question is what should happen after that.

ParameterToEqualToBridge

We should add a bridge from Parameter to EqualTo. This would be a pretty trivial fallback that'll mean it should work across all solvers.

FixParametricVariablesBridge

We can update #2092 to use only Parameter instead of EqualTo. This should let p * x be re-written to affine for solvers not supporting quadratic.

But for solvers supporting quadratic, and for additive parameters to be efficient, we'll need...

Solver support

Solvers will need to declare support for MOI.Parameter, and then parse constraints as they're added to extract parameters into the constant terms. They'll also need to maintain a mapping so that updating a parameter updates all of constraints inside the solver.

Why not double down on ParametricOptInterface

This is a good question. At minimum, adding Parameter will also help POI.

I don't have a good answer on the rest. One objection is that the POI codebase is complicated, and there is a lot of internal state to maintain. The bridge in #2092 is very simple in comparison. But perhaps the overhead of adding native parameter support to Gurobi.jl (for example) would be equally challenging.

The argument in favor of solver-specific implementations is that they could make their updates more efficient.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions