I have a constraint that can be either a range or an equality depending upon whether a user passes a range (i.e. two arguments) or a single value as a constraint. I convert the range constraint to an equality using the standard technique, by defining a slack variable u. I define an abstract type SPECIAL_CONS with two subtypes Range and Equality.
What’s the correct way of generating this constraint and adding it to the model (we don’t know in advance whether a a single value or two values will be passed so that correct constraint is generated)?
using JuMP, Cbc
abstract type SPECIAL_CONS end
struct Range <: SPECIAL_CONS end
struct Equality <: SPECIAL_CONS end
function constraint_gen(lb,ub,::Range)
@variable(model,0<=u<=ub-lb)
return @constraint(model,u+sum(C[i]*x[i] for i in 1:10) == ub)
end
function constraint_gen(ub,::Equality)
return @constraint(model,sum(C[i]*x[i] for i in 1:10) == ub)
end
function solve_model(constraint_gen::Function)
c = rand(10)
d = rand(10)
model = Model(Cbc.Optimizer)
@variable(model, x[1:10] >= 0)
@constraint(model,sum(x[i]*c[i] for i=1:10) >=sum(d))
constraint_gen(constraint_gen::Function)
optimize!(model)
end
If I run the code using the following, it errors as model is not defined, but adding model as argument to the constraint_gen function also errors.
abstract type SPECIAL_CONS end
struct Range <: SPECIAL_CONS end
struct Equality <: SPECIAL_CONS end
function constraint_gen(model,lb,ub,::Range)
@variable(model,0<=u<=ub-lb)
return @constraint(model,u+sum(C[i]*x[i] for i in 1:10) == ub)
end
function constraint_gen(model,ub,::Equality)
return @constraint(model,sum(C[i]*x[i] for i in 1:10) == ub)
end
function solve_model(constraint_gen::Function)
c = rand(10)
d = rand(10)
model = Model(Cbc.Optimizer)
@variable(model, x[1:10] >= 0)
@constraint(model,sum(x[i]*c[i] for i=1:10) >=sum(d))
constraint_gen(constraint_gen::Function)
optimize!(model)
end
solve_model(constraint_gen(model,1,Equality()))
This gives error “UndefVarError: model not defined”. I understand why I get this error, but how to correct it as model is generated by the function solve_model()?
gives the same error. Calling solve_model(constraint_gen(model,1,Equality())) expects model to be an argument, and I am generating the model inside the function solve_model.
It is not obvious how the code will work to use the correct function definition depending upon if one value is passed solve_model(ub) or a range is passed solve_model(lb,ub)
Do I need to use something like below?
if one_input
solve_model(constraint_gen(model,1,Equality()))
elseif range
solve_model(constraint_gen(model,0.5,1,Range()))
end
Looking closer, it seems you are also not passing the variables C and x, and are passing constraint_gen to itself! You need to put all the variables you need to use in function signature and then pass those variables when you call the function. So if you define the function constraint_gen(lb,ub,::Range) then you only have access to those variables inside the function (except if they are global variables, which it’s best to avoid), and you need to pass them all when you call the function.
Yes, I realised that I need to pass C and x as well so the function is defined like constraint_gen(model,x,C,lb,ub,::Range). This will still not address what my question is about. When I call the solve_model() it will then expect all these arguments. And how can I code so that it calls the right definition as shown in this pseudocode below?
if one_input
solve_model(constraint_gen(model,1,Equality()))
elseif range
solve_model(constraint_gen(model,0.5,1,Range()))
end
You can use splatting, e.g. if args = (1,) (a one-element tuple containing the integer 1), then f(args...) calls the function f with one argument, the integer 1, whereas if args = (0.5, 1) then f(args...) calls f with two arguments, 0.5 and 1. So you use f(model, args..., Range()) or something like that. But I think I still don’t really understand the broader context of your code structure.
using JuMP, Cbc
function constraint_gen(model, c, x, bound::Tuple{Float64,Float64})
lb, ub = bound
@variable(model, 0 <= u <= ub - lb)
@constraint(model, u + sum(c[i] * x[i] for i in 1:10) == ub)
return
end
function constraint_gen(model, c, x, bound::Float64)
@constraint(model, sum(c[i] * x[i] for i in 1:10) == bound)
return
end
function solve_model(bound)
c = rand(10)
d = rand(10)
model = Model(Cbc.Optimizer)
@variable(model, x[1:10] >= 0)
@constraint(model,sum(x[i] * c[i] for i in 1:10) >= sum(d))
constraint_gen(model, c, x, bound)
optimize!(model)
end
solve_model(1.0)
solve_model((1.0, 2.0))