Julia: How to introduce binary integers for mixed integers optimization problems with JuMP? - julia

Problem description
I am trying to do a mixed-integer optimization for a "Unit Commitment" problem in Julia with Jump. But JuMP expects my introduction of the unit activation variable, x[1:N], to be a number and not a variable. However, the unit activation is a binary integer decision variable for the optimization problem so I have trouble including the variable into the optimization problem.
What am I doing wrong?
My approaches have been:
Approach 1: Include x[1:N] as part of #variable macro for P_G.
m = Model(Cbc.Optimizer) # Model
#variable(m, x[1:N], Bin) # Unit activation
#variable(m, P_C[i,1]*x[i] <= P_G[i=1:N,1:T] <= P_C[i,2]*x[i]) # Unit generation limit
for i in 1:T # Load balance
#constraint(m, sum(P_G[:,i]) == P_D[i])
end
#objective(m,Min,sum(P_G[:,1:T].*F[1:N]*x[1:N])) # Objective function
optimize!(m) # Solve
This leads to the following error:
LoadError: InexactError: convert(Float64, 50 x[1]).
Approach 2: Define the feasible region for P_G as a constraint including x[1:N]:
m = Model(Cbc.Optimizer) # Model
#variable(m, x[1:N]) # Unit activation
#variable(m, P_G[i=1:N,1:T] ) # Unit generation limit
for i in 1:T # Load balance
#constraint(m, sum(P_G[:,i]) == P_D[i])
end
for i in 1:N # Unit generation limit
for j in 1:T
#constraint(m, P_C[i,1]*x[i] <= P_G[i,j] <= P_C[i,2]*x[i])
end
end
#objective(m,Min,sum(P_G[:,1:T].*F[1:N]*x[1:N])) # Objective function
optimize!(m) # Solve
This leads to: LoadError: [..] '#constraint(m, $(Expr(:escape, :(P_C[i, 1]))) * $(Expr(:escape, :(x[i]))) <= P_G[i, j] <= $(Expr(:escape, :(P_C[i, 2]))) * $(Expr(:escape, :(x[i]))))': Expected 50 x[1] to be a number.
NB: there might be more proper iteration methods but this should be idiot proof to Julia and JuMP newbies like me.
Working code without mixed-integer optimization
using JuMP, Cbc # Optimization and modelling
using Plots, LaTeXStrings # Plotting
# DATA
P_C = [50 200; # Power capacity [:, (min, max)]
25 200;
100 200;
120 500;
10 500;
20 500;
200 800;
200 800;
100 800;
200 1000;]
P_D = LinRange(sum(P_C[:,1]), sum(P_C[:,2]), 100) # Power demand
F = rand(100:500,10) # Random prod. prices
T = length(P_D) # Number of time steps
N = length(P_C[:,1]) # Number of generators
# MODEL
m = Model(Cbc.Optimizer) # Model
#variable(m, x[1:N], Bin) # Unit activation
#variable(m, P_C[i,1] <= P_G[i=1:N,1:T] <= P_C[i,2]) # Unit generation limit
for i in 1:T # Load balance
#constraint(m, sum(P_G[:,i]) == P_D[i])
end
#objective(m,Min,sum(P_G[:,1:T].*F[1:N])) # Objective function
optimize!(m) # Solve
# PLOT
plt = plot(P_D[:],value.(P_G[:,1:T])', xlab = L"P_{load} [MW]", ylab = L"P_{unit} [MW]")
#show plt
Which should produce something similar:
The expected outcome of introducing the unit activation variable would be that each unit is not required to generate power in the the lower region of the P_load.
Preliminary
I have introduced the basics of the problem with success:
Objective function: Minimize the cost of power generation
Variable, P_G: for power generation (feasible region defined by min and max capacity, P_C)
Production cost, F (as constant only!)
Power demand, P_D, is set to be a linear space from the min power cap. to the max cap.
Mathematically expressed:

Variable bounds cannot include other variables. Do:
m = Model(Cbc.Optimizer)
#variable(m, x[1:N], Bin)
#variable(m, P_G[i=1:N,1:T])
#constraint(m, [i=1:N, t=1:T], P_C[i, 1] * x[i] <= P_G[i, t])
#constraint(m, [i=1:N, t=1:T], P_G[i, t] <= P_C[I, 2] * x[i])

Related

Solve a system of N equations with N unknowns using Julia

I have :
a set of N locations which can be workplace or residence
a vector of observed workers L_i, with i in N
a vector of observed residents R_n, with n in N
a matrix of distance observed between all pair residence n and workplace i
a shape parameter epsilon
Setting N=3, epsilon=5, and
d = [1 1.5 3 ; 1.5 1 1.5 ; 3 1.5 1] #distance matrix
L_i = [13 69 18] #vector of workers in each workplace
R_n = [27; 63; 10]
I want to find the vector of wages (size N) that solve this system of N equations,
with l all the workplaces.
Do I need to implement an iterative algorithm on the vectors of workers and wages? Or is it possible to directly solve this system ?
I tried this,
w_i = [1 ; 1 ; 1]
er=1
n =1
while er>1e-3
L_i = ( (w_i ./ d).^ϵ ) ./ sum( ( (w_i ./ d).^ϵ), dims=1) * R
er = maximum(abs.(L .- L_i))
w_i = 0.7.*w_i + 0.3.*w_i.*((L .- L_i) ./ L_i)
n = n+1
end
If L and R are given (i.e., do not depend on w_i), you should set up a non-linear search to get (a vector of) wages from that gravity equation (subject to normalising one w_i, of course).
Here's a minimal example. I hope it helps.
# Call Packages
using Random, NLsolve, LinearAlgebra
# Set seeds
Random.seed!(1704)
# Variables and parameters
N = 10
R = rand(N)
L = rand(N) * 0.5
d = ones(N, N) .+ Symmetric(rand(N, N)) / 10.0
d[diagind(d)] .= 1.0
ε = -3.0
# Define objective function
function obj_fun(x, d, R, L, ε)
# Find shares
S_mat = (x ./ d).^ε
den = sum(S_mat, dims = 1)
s = S_mat ./ den
# Normalize last wage
x[end] = 1.0
# Define loss function
loss = L .- s * R
# Return
return loss
end
# Run optimization
x₀ = ones(N)
res = nlsolve(x -> obj_fun(x, d, R, L, ε), x₀, show_trace = true)
# Equilibrium vector of wages
w = res.zero

Julia JuMP feasibility slack of constraints

In Julia, by using JuMP am setting up a simple optimization problem (MWE, the real problem is much bigger).
model = Model()
set_optimizer(model, MosekTools.Optimizer)
#variable(model, 0 <= x[1:2])
#constraint(model, sum(x) <= 2)
#constraint(model, 1 <= sum(x))
#objective(model, Min, sum(x))
print(model)
Which gives this model:
Min x[1] + x[2]
Subject to
x[1] + x[2] ≤ 2.0
-x[1] - x[2] ≤ -1.0
x[1] ≥ 0.0
x[2] ≥ 0.0
I optimize this model via optimize!(model).
Now, obviously, the constraint x[1] + x[2] <= 2 is redundant and it has a feasibility slack of "3". My goal is to determine all the constraints that have slacks larger than 0 and display the slacks. Then I will delete those from the model.
To this end, I iterate over the constraints which are not variable bounds and print their values.
for (F, S) in list_of_constraint_types(model)
# Iterate over constraint types
if F!= JuMP.VariableRef #for constraints that
for ci in all_constraints(model, F, S)
println(value(ci))
end
end
end
However, because I print the value of the constraints, I get the left-hand sides:
1.0
-1.0
I want to instead see the slacks as
0
3
How may I do this? Note that I am not necessarily interested in linear programs, so things like shadow_value is not useful for me.
Based on the accepted answer, I am adding a MWE that solves this problem.
model = Model()
set_optimizer(model, MosekTools.Optimizer)
#variable(model, 0 <= x[1:2])
#constraint(model, sum(x) <= 2)
#constraint(model, 1 <= sum(x))
#constraint(model, 0.9 <= sum(x))
#objective(model, Min, sum(x))
print(model)
optimize!(model)
constraints_to_delete = vec([])
for (F, S) in list_of_constraint_types(model)
if F!= JuMP.VariableRef
for ci in all_constraints(model, F, S)
slack = normalized_rhs(ci) - value(ci)
if slack > 10^-5
push!(constraints_to_delete, ci)
println(slack)
#delete(model, ci)
end
end
end
end
for c in constraints_to_delete
delete(model, c)
end
print(model)
Read this (hot off the press) tutorial: https://jump.dev/JuMP.jl/dev/tutorials/linear/lp_sensitivity/.
Although focused on LPs, it shows how to compute slacks etc using normalized_rhs(ci) - value(ci).

MINLP in Julia using Juniper

I'm trying to solve a MINLP with binary variables in Julia.
I have a user-specific objective function which is non-linear and I have nonlinear and linear constraints. I tried to solve the problem using Juniper, but I always get the error KeyError: key :myfunc not found which I don't understand.
Here is an example where the error occurs.
using Ipopt, JuMP, Juniper
N = 5
optimizer = Juniper.Optimizer
params = Dict{Symbol,Any}()
params[:nl_solver] = with_optimizer(Ipopt.Optimizer, print_level=0)
m = Model(with_optimizer(optimizer, params))
#variable(m, z[1:N],binary=true)
#NLconstraint(m, sum(z[i]*z[i+1] for i=1:N-1) <= 20)
#constraint(m, sum(z[i] for i=1:N) <= 10)
myfunc(z...) = sum(sin(i)*z[i]^i for i in 1:length(z))
register(m, :myfunc, N, myfunc, autodiff=true)
#NLobjective(m, Max, myfunc(z...))
m
optimize!(m)
println(JuMP.value.(z))
println(JuMP.objective_value(m))
println(JuMP.termination_status(m))
Do you have any idea why the error KeyError: key :myfunc not found occurs and how I can fix it?
Thanks!
It seems that the objective needs to be registered both with JuMP and Juniper. Here is a working example of an MINLP where i) the objective is non-linear, ii) the constraints are both linear and non-linear, and iii) the variables are binary.
using Juniper, Ipopt, JuMP, Cbc # <- last package is optional
N = 4
function myfunction(x...)
return sum(x[i].^4 for i = 1:length(x))
end
m = Model(
with_optimizer(
Juniper.Optimizer;
nl_solver = with_optimizer(Ipopt.Optimizer, print_level = 0),
mip_solver = with_optimizer(Cbc.Optimizer, logLevel=0), # <- optional
registered_functions = [
Juniper.register(:myfunction, N, myfunction; autodiff = true)
]
)
)
register(m, :myfunction, N, myfunction; autodiff = true)
#variable(m, x[1:N], Bin)
#NLconstraint(m, sum(sin(x[i]^2) for i=1:N) <= 4)
#constraint(m, x[1]+x[2]+x[3] <= 2)
#NLobjective(m, Max, myfunction(x...))
optimize!(m)
println(JuMP.value.(x))
println(JuMP.objective_value(m))
println(JuMP.termination_status(m))
Here's the resulting output:
nl_solver : OptimizerFactory(Ipopt.Optimizer, (), Base.Iterators.Pairs(:print_level => 0))
mip_solver : OptimizerFactory(Cbc.Optimizer, (), Base.Iterators.Pairs(:logLevel => 0))
log_levels : Symbol[:Options, :Table, :Info]
registered_functions : Juniper.RegisteredFunction[Juniper.RegisteredFunction(:myfunction, 4, myfunction, nothing, nothing, true)]
#Variables: 4
#IntBinVar: 4
#Constraints: 2
#Linear Constraints: 1
#Quadratic Constraints: 0
#NonLinear Constraints: 1
Obj Sense: Max
Incumbent using start values: 0.0
Status of relaxation: LOCALLY_SOLVED
Time for relaxation: 0.19966506958007812
Relaxation Obj: 1.5925926562762966
MIPobj NLPobj Time
=============================================
1.3333 0.0 0.0
FP: 0.00896906852722168 s
FP: 1 round
FP: Obj: 3.0
ONodes CLevel Incumbent BestBound Gap Time Restarts GainGap
============================================================================================================
0 2 3.0 1.59 46.91% 0.1 0 -
#branches: 1
Obj: 3.000000119960002
[1.0, 0.0, 1.0, 1.0]
3.000000119960002
LOCALLY_SOLVED

Nonlinearly constrained maximization problem using MathProgBase in Julia

The objective function is f(x,y)=sqrt(x^2+2*y^2-xy), subject to 10 > x > 0, 10 > y > 0, x > y. I am going to find the x and y which maximize objective function. I am required to use Nonlinear models in MathProgBase.jl packages. The tutorial from https://mathprogbasejl.readthedocs.io/en/latest/nlp.html is difficult for me to follow since I am a beginner. I really appreciate your help!
It seems that JuMP doesn't support strictly greater/lower constraints (as result that most solver engines doesn't either).
A modellisation in Julia of your problem would be:
using JuMP, Ipopt
m = Model(with_optimizer(Ipopt.Optimizer, print_level=0))
#variable(m, 0 <= x <= 10, start=1)
#variable(m, 0 <= y <= 10, start=1)
#constraint(m, c1, y <= x )
#NLobjective(m, Max, sqrt(x^2+2*y^2-x*y))
optimize!(m)
status = termination_status(m)
if (status == MOI.OPTIMAL || status == MOI.LOCALLY_SOLVED || status == MOI.TIME_LIMIT) && has_values(m)
if (status == MOI.OPTIMAL)
println("** Problem solved correctly **")
else
println("** Problem returned a (possibly suboptimal) solution **")
end
println("- Objective value : ", objective_value(m))
println("- Optimal solutions:")
println("x: $(value.(x))")
println("y: $(value.(y))")
else
println("The model was not solved correctly.")
println(status)
end
(see https://lobianco.org/antonello/personal/blog/2017/0203_jump_for_gams_users for an explanation of the various steps)
That results of the script is:
** Problem returned a (possibly suboptimal) solution **
- Objective value : 14.14213575988668
- Optimal solutions:
x: 10.0
y: 10.0

Minimize the maximum variable

I have a Mixed Integer Programming problem. The objective function is a minimization of the maximum variable value in the a vector. The variable is has an upper bound of 5. The problem is like this:
m = Model(solver = GLPKSolverMIP())
#objective(m, Min, max(x[i] for i=1:12))
#variable(m, 0 <= x[i] <= 5, Int)
#constraint(m, sum(x[i] for i=1:12) == 12)
status = solve(m)
The max variable is not part of the julia JuMP syntax. So I modified the problem to
t=1
while t<=5 && (status == :NotSolved || status == :Infeasible)
m = Model(solver = GLPKSolverMIP())
i = 1:12
#objective(m, Min, max(x[i] for i=1:12))
#variable(m, 0 <= x[i] <= t, Int)
#constraint(m, sum(x[i] for i=1:12) == 12)
status = solve(m)
t += 1
end
This solution does the job by solving the problem iterative for starting with a upper bound for the variable at 1 and then increase by one until the solutoin is feasible. Is this really the best way to do this?
The question wants to minimize a maximum, this maximum can be held in an auxiliary variable and then we will minimize it. To do so, add constraints to force the new variable to actually be an upper bound on x. In code it is:
using GLPKMathProgInterface
using JuMP
m = Model(solver = GLPKSolverMIP())
#variable(m, 0 <= x[i=1:3] <= 5, Int) # define variables
#variable(m, 0 <= t <= 12) # define auxiliary variable
#constraint(m, t .>= x) # constrain t to be the max
#constraint(m, sum(x[i] for i=1:3) == 12) # the meat of the constraints
#objective(m, Min, t) # we wish to minimize the max
status = solve(m)
Now we can inspect the solution:
julia> getValue(t)
4.0
julia> getValue(x)
3-element Array{Float64,1}:
4.0
4.0
4.0
The actual problem the poster wanted to solve is probably more complex that this, but it can be solved by a variation on this framework.

Resources