Is it possible to extend the syntax of set_upper_bound to an expression, despite (perhaps) there might not be a corresponding solver API?
Consider
julia> using JuMP
julia> model = Model();
julia> @variable(model, x[1:2]);
julia> @expression(model, e, x[1] + 2x[2]);
Suppose we could write
julia> set_upper_bound(e, 1)
ERROR: Cannot call set_upper_bound with x[1] + 2 x[2] because it is not an affine expression of one variable.
julia> set_upper_bound(e, 2)
ERROR: Cannot call set_upper_bound with x[1] + 2 x[2] because it is not an affine expression of one variable.
If the user hadn’t written set_upper_bound(e, some_Float64) ever before, then it doesn’t exist.
(@constraint are used to build “complicating” constraints, while set_upper_bound is reserved to build relatively simple constraints.) In this manner the code reads more clear.
e.g., In a Lagrangian relaxation, only those constraints built with JuMP.@constraint are dualized.
And if it does exist? How do we associate the expression with a constraint? Is the constraint reference returned by set_upper_bound? What happens if the expression does not have any variable terms?
In this manner the code reads more clear
I think this is where we disagree. A bound and a constraint are different concepts. We’re going to keep them separate.
You can do something like this in your code (although I don’t think you should):
my_set_upper_bound(x::VariableRef, u) = set_upper_bound(x, u)
function my_set_upper_bound(x::AffExpr, u)
model = owner_model(x)
@constraint(model, x <= u)
return
end
It is straightforward to piggyback on the existing API
julia> using JuMP; model = Model(); @variable(model, x[1:2]);
julia> @variable(model, e <= 1); # the RHS number is the upper bound
julia> @constraint(model, e == x[1] + 2x[2]) # the RHS is the expression
-x[1] - 2 x[2] + e == 0
julia> set_upper_bound(e, 2)
julia> set_upper_bound(e, 3)
The expense is: we need one additional decision variable.
If this variable == the desired expression x[1] + 2x[2] is an “objective” one, it’s value may be large, e.g., the value of e might be 10000, whereas value of the rest “physical” decisions could be e.g. 0 or 1.
So, in this case, the numeric scale of the decisions vary greatly, (I don’t know if it will affect the solver somehow, maybe not).
If we use expression and “upper_bound” constraints, the above numeric inconsistence could be avoided.