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).
Related
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])
I saw an outdated answer in the following thread (How to do "for all" in sum notation in Julia/JuMP)
which is 3 years old unfortunately, but it's exactly what I want. However the code fails due to a number of syntax errors as the sum() function has changed these past few years.
For my code, I found that the sum() function only works for one indexing variable i, but if I include another variable j, the function stops working. I'm also using jupyter notebook if that makes any difference. Any ideas?
Using JuMP
ZS = Model(with_optimizer(Gurobi.Optimizer))
P = [[10 -20];
[30 -40]]
#variable(ZS, x[1,1:2])
#variable(ZS, y[1:2,1])
#objective(ZS, Max, sum(x[i]*P[i,j]*y[j] for i=1:2 for j=1:2))
#constraint(ZS, con1, x[1] + x[2] <= 1)
#constraint(ZS, con2, y[1] + y[2] <= 1)
optimize!(ZS)
For this example of code, I received a "key not found" error
Seems like you need an update of the for loop syntax and to set your solver to be non-convex.
I also recommend using anonymous labeling for vars, exp etc so that you can change them as required.
using JuMP
using Gurobi
ZS = Model(Gurobi.Optimizer)
set_optimizer_attribute(ZS, "NonConvex", 2)
P = [[10 -20];
[30 -40]]
xs = #variable(ZS, x[1:2])
ys = #variable(ZS, y[1:2])
my_obj = #objective(ZS, Max, sum(x[i]*P[i,j]*y[j] for i in 1:2, j in 1:2))
con1 = #constraint(ZS, x[1] + x[2] <= 1)
con2 = #constraint(ZS, , y[1] + y[2] <= 1)
optimize!(ZS)
Runtime is pretty dang long though...
Change definitions of variables to be one-dimensional like this:
#variable(ZS, x[1:2])
#variable(ZS, y[1:2])
and all should work as expected.
Alternatively leave x and y two dimensional and redefine your objective and constraints like this:
#objective(ZS, Max, sum(x[1,i]*P[i,j]*y[j,1] for i=1:2 for j=1:2))
#constraint(ZS, con1, x[1,1] + x[1,2] <= 1)
#constraint(ZS, con2, y[1,1] + y[2,1] <= 1)
As a side note you can define P more simply like this:
julia> P = [10 -20
30 -40]
2×2 Array{Int64,2}:
10 -20
30 -40
I am working in an optimization problem (A*v = b) where I would like to rank a set of alternatives X = {x1,x2,x3,x4}. However, I have the following normalization constraint: |v[i] - v[j]| <= 1, which can be in the form -1 <= v[i] - v[j] <= 1.
My code is as follows:
import cvxpy as cp
n = len(X) #set of alternatives
v = cp.Variable(n)
objective = cp.Minimize(cp.sum_squares(A*v - b))
constraints = [0 <= v]
#Normalization condition -1 <= v[i] - v[j] <= 1
for i in range(n):
for j in range(n):
constraints = [-1 <= v[i]-v[j], 1 >= v[i]-v[j]]
prob = cp.Problem(objective, constraints)
# The optimal objective value is returned by `prob.solve()`.
result = prob.solve()
# The optimal value for v is stored in `v.value`.
va2 = v.value
Which outputs:
[-0.15 0.45 -0.35 0.05]
Result, which is not close to what should be and even have negative values. I think, my code for the normalization contraint most probably is wrong.
You are not appending your constraints, instead you are overwriting them each time. Instead of this line
constraints = [-1 <= v[i]-v[j], 1 >= v[i]-v[j]]
You should have
constraints += [-1 <= v[i]-v[j], 1 >= v[i]-v[j]]
For cleanliness you may want to change this
for i in range(n):
for j in range(n):
To only consider each pair once:
for i in range(n):
for j in range(i+1, n):
I'm trying to implement the following constraint in a JuMP environment:
#constraint(m, ((c*x) + (p*o)) + (r.*z) - d .== g')
Unfortunately, I get the following error ERROR: MethodError: no method matching append
But trying the element-wise multiplication alone does not return any error and implements it correctly into the model.
Here you have the minimal example I'm working with.
m = Model(solver = GLPKSolverLP());
np = 3; #number of products
c = [3 7 5;
6 5 7;
3 6 5;
-28 -40 -32];
g = [200 200 200 -1500];
n = length(g);
o = [1 1 1]';
#variable(m, x[1:np] >= 0);
#variable(m, d[1:n] >= 0);
#variable(m, z[1:n] >= 0);
#variable(m, r[1:n] >= 0);
#variable(m, p[1:n,1:np] >= 0);
#objective(m, Min, sum(d));
#constraint(m, ((c*x) + (p*o)) + (r.*z) - d .== g')
It seems that there is a problem when you add quadratic term to linear term and quadratic term is on right hand side of the addition inside #constraint macro.
There are two solutions:
A. write the quadratic term as first like this:
#constraint(m, (r.*z) + ((c*x) + (p*o)) - d .== g')
B. define LHS of the equation outside (and now the order of terms does not matter)
constr = ((c*x) + (p*o)) + (r.*z) - d
#constraint(m, constr .== g')
As a side note: your problem is quadratic so GLPKSolverLP will not solve it as it does not allow such constraints.
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.