Why are names not allowed for upper/lower bound constraints?

I saw Implement names for constrained variables · Issue #193 · jump-dev/Dualization.jl · GitHub was recently opened based on another post Can't get the example dualization to work.

What is the reasoning behind not allowing names for these constraints in the first place? I tried just removing the methods mentioning VariableIndexConstraintNameError and setting names in JuMP upon creation (diff) and things seem to be working fine.

I don’t think a julia name is needed for, you can directly call

julia> LowerBoundRef(x)
x >= 0

Just the same reason as you don’t need a julia name for the objective expression, as you can call

julia> objective_function(model)
2 x

PS bound constraints are simple constraints which are more basic than general constraints. One should never write a simple constraint as a general constraint (i.e. call @constraint). In practice, typically we won’t do sensitivity analysis about simple bound constraints. (Instead, we are only interested in the dual variables of complicating constraints, where a name for referencing is entailed.)


Here is a related example that you don’t need to introduce 2 dual variables for one lower_upper_bound constraint

using JuMP
import Gurobi

# the original formulation
model = Model(Gurobi.Optimizer); set_silent(model)
@variable(model, 0 ≤ x ≤ 1)
@objective(model, Min, .7x)
optimize!(model); dual.([LowerBoundRef(x), UpperBoundRef(x)]) # [.7, 0]
set_objective_function(model, -objective_function(model))
optimize!(model); dual.([LowerBoundRef(x), UpperBoundRef(x)]) # [0, -.7]

# to distill the above nonzero duals (.7 and -.7), you only need one 
# copy constr, consider the reformulation below
model = Model(Gurobi.Optimizer); set_silent(model)
@variable(model, x)
@variable(model, 0 ≤ z ≤ 1) # create a copy variable
@constraint(model, c_copy, x == z)
@objective(model, Min, .7x)
optimize!(model); dual(c_copy) # .7
set_objective_function(model, -objective_function(model))
optimize!(model); dual(c_copy) # -.7

Sorry, I should have been more specific. By name I meant assigning a MOI.ConstraintName() attribute to the constraint, not a julia name. That is what Dualization.jl uses to name the variables associated with those constraints. For example, consider the model

using JuMP, Dualization
m = Model()
@variable m x
@constraint m x_lb x ≥ 1
set_upper_bound(x, 2)
@objective m Max 3x

Which looks like this:

julia> print(m)
 Max 3 x
Subject to
 x_lb : x ≥ 1
 x ≤ 2

Notice that there is no name for the last constraint – since it is a variable bound. Since Dualization.jl uses that attribute to name the dual variables, we end up with the _[2] variable in the dual:

julia> print(dualize(m, dual_names=DualNames()))
Min -2 _[2] - x_lb
Subject to
 x : x_lb + _[2] = -3
 x_lb ≥ 0
 _[2] ≤ 0

But with the diff applied, that variable has an interpretable name:

julia> print(dualize(m, dual_names=DualNames()))
Min -2 x_ub - x_lb
Subject to
 x : x_lb + x_ub = -3
 x_lb ≥ 0
 x_ub ≤ 0

Obviously the diff is very crude, always adding names to every bound constraint is probably not a good idea. But I am wondering why it is not allowed to even explicitly set those names.

1 Like

Hi @klamike, welcome to the forum :smile:

What is the reasoning behind not allowing names for these constraints in the first place?

You’ve hit upon a part of MathOptInterface that caused us a number of issues over the years. For the backstory, start with:

https://github.com/jump-dev/GLPK.jl/pull/101#discussion_r295583456

Most (but not all) solvers have support for variable names. A large subset have support for names of the affine/row constraints. To the best of our knowledge, no solver has native support for naming a variable bound constraint.

When we originally supported variable bound names in MOI, this meant that every solver wrapper had to add customized support for storing and reasoning about the names of variable bounds. This added a lot of complexity, but even if we added it, it didn’t really add much for the user. (I think you might be the first person who has asked for this feature after we removed it!)

More discussion about removing it is in:

https://github.com/jump-dev/MathOptInterface.jl/issues/832

And we added the error in:

https://github.com/jump-dev/MathOptInterface.jl/pull/1312

To summarize: there’s no technical reason why we should disable variable bound constraints, which is why your diff works. It’s more of a cultural choice that we made to simplify the requirements for solver wrappers.

The only place this really comes up is in Dualization, where we need some way of identifying the dual variables. But the solution is probably to have a standard convention in Dualization, not to support bound constraint names throughout all of MOI.

2 Likes