How to add condition to gurobi - python

I have variables whose values change every hour during the day (24 values):
plants = ['Plant1', 'Plant2']
users = ['user1', 'user2']
time_steps = range(0,24)
p_gen = model.addVars(plants, time_steps, name="pow_gen")
tot_consume = model.addVars(users, time_steps, name="total_demand")
p_grid = model.addVars(time_steps, lb = -GRB.INFINITY, name="exch_pow")
I want to implement something like this:
If ((quicksum(p_gen[t] for pp in plants) - quicksum(tot_d[u,t] for u in users) )>= p_grid[t] for t in time_steps)
model.addConstrs(A)
model.addConstrs(B)
else:
model.addConstrs(C)
My problem is that Gurobi does not understand the variables which depend on the time. I want to implement if the condition, so it depends on the condition the program, will you different addConstr.
How to implement this condition in Gurobi?

Linear Programming doesn't work like this.
You have constraints and your model must fulfill them, otherwise your model is infeasible.
You can't put constraints based on constraints conditions, if anything you can put constraints based on boolean conditions (like a setting, a value...) or you can put boolean constraints.
You can, however, have two models at the same time, with the same vars and constraints before the if / else branches.
You can resolve the first model, get the value you need with the x attribute (just call variable.x to get its value), and with that value you can select which constraints add to second model, and then resolve it.

Related

Python Optimization Problem - Minimize number of groups needed to satisfy requirements of another variable

I am trying to correctly understand an optimization problem using PuLP, however, I'm having a hard time understanding the formulation of this problem. Most examples I've found have clear expectations from the objective function and the target of the optimization typically does not have many possible matches that are defined by another variable.
Some info on the problem:
Objective is to minimize the number of creates needed to satisfy modeltype required number
Each crate contains any number (from 0-1000s) of modeltypes
Each modeltype has a required number to meet from any number of crates
...... for example, if 10 modeltpyes are required, the optimization may get all 10 from one crate or 2 from one crate and 8 from another crate.
There are many crates, but we do not need every create
I think what's throwing me off on this one is I can't approach it like the traditional optimization problems I've done in the past, where a modeltype would be assigned to 1 crate.
is this even a linear problem?
how can I think about the objective function?
are there even really any constraints? I suppose an artificial constraint would be to limit the number of creates that a modeltype could be searched in.
is this actually a very easy problem with an easy solution and I'm overcomplicating this?
Here's an example of modeltype and the number required.
ModelTpye
Required #
model1
10
model2
25
...
...
model150
100
Here's the example of the crates and relationship to modeltype.
Crate
ModelType
# of ModelType
a
model1
10
b
model1
5
a
model2
17
c
model2
25
...
...
...
z
model50
5
I could list some of my PuLP code, but I feel like there may be a simple solution here that I simply have not thought of.
I tried to fit this in the bounds of a normal optimization problem, but to no avail. I keep getting stuck on the fact that the Modeltypes can be assigned to any number of bins subject to their requirements.
Any assistance would be much appreciated!
Edit****
Here's what I have so far for the logic. This returns an answer, but not the most efficient possible...
modeltypes= list(df['modeltype_id'])
crates= list(df['crate_id'])
problem = LpProblem("Minimize_Crates_Problem", LpMinimize)
modeltype_units_required = dict(zip(modeltypes, df['num_units_required']))
# gets the possible crates for each modeltype
modeltype_crates= {}
for m in modeltypes:
modeltype_crates[m] = list(df[df["modeltype_id"] == m].crate_id)
#dictiomary within dictionary, modeltpye - crate- number of modeltype units in crate
modeltype_crate_units = {}
crate_units = {}
temp_df = pd.DataFrame()
for m in modeltype_crates:
crate_units = {}
for c in modeltype_crates[m]:
temp_df = df[(df["modeltype_id"]==m)&(df["crate_id"]==c)]
new_value = list(temp_df.units)[0]
if new_value !=0:
crate_units[c] = new_value
modeltype_crate_units[m] = crate_units
# each modeltype can change crate to assign to
crate_pick = LpVariable.dicts('crate_pick', indices=[(m,c) for m in modeltypes for c in crates], cat='Binary')
# assigns modetype to 1 crate, since I wasn't sure how to allow it to assign to 1 or more crates
for m in modeltypes:
problem += lpSum(crate_pick[m, c] for c in modeltype_crates[m]) ==1
# I think this means that a modeltype can only be assigned to a crate that has the number of units needed or higher
for m in modeltypes:
problem += lpSum(crate_pick[m,c]*modeltype_crates_total[m][c] for c in modeltype_crates[m]) >= modeltype_units_required[m]
The step from an English description to Python/Pulp code is often a bit too large. The best thing for me is to formulate a precise and compact mathematical model. Once we have that, transcribing this into Pulp is usually trivial. So here is my model:
Data
Demand[m] "demand for model types"
Content[c,m] "number of items of type m in crate c"
Binary Variables
Use[c] "usage of crates"
Optimization Model
min sum(c,Use[c])
sum(c,Use[c]*Content[c,m]) >= Demand[m] for all m
If there are multiple crates of type a,b,.. then Use[c] should be an integer variable (with an appropriate upper bound).

Pyomo: a Set of a Set

I'm new to Pyomo and Python. I was wondering if someone could help me with the case of a Set Q that is based on a Set P.
model.P = pyo.Set(initialize=list(df.iloc[:,0]))
model.Q = pyo.Set(model.P)
Model.P stands for the products I read in out of a file. Model.Q is the number of possible quantities for every product.
So, for example: product "AAA" has 3 different quantities, than I would like to set model.Q["AAA"] equal to the range(1,3). Product "BBB" has 7 different quantities, than I would like to set model.Q["BBB"] equal to the range(1,7).
The number of quantities are read into a list [3, 7, ...]
When I want to initialize a parameter like the following, it gives an error like below the example.
# Setup time
model.st = pyo.Param(model.P,model.Q,initialize=setupTimes,domain=pyo.NonNegativeReals)
TypeError: Cannot apply a Set operator to an indexed Set component (Q)
How can I implement this? Thanks for the help.
I really do not know what you intend to do with that set, but I feel there may be other ways to efficiently model your problem with just a set and a parameter of quantities for each set.
However, to do what you want, one way I suggest is to create a second set "model.PP" which has the range(max(list)) as elements.
Then you create a list of tuples mapping each of model.P to the elements of model.PP appropriately which you then use to initilise model.Q ie.
model.Q = pyo.Set(within=model.P*model.PP, initialise=[("AAA",1), ("AAA",2). . .])
You can create that new list with a for loop.

Pyomo Constraint iteration for Optimal Power Flow

I am solving a DC Optimal Power Flow Problem and I am trying to brainstorm the most efficient way to iterate over a constraint in Pyomo.
The following is the data structure, where i and k are the nodes connected through a branch, and X is the reactance, a property of the branch.
Sample Branch Data
The constraint I am having trouble with is the following:
constraint
Where the symbol "delta" and "p" is a variable in the constraint, each node has a single value of delta and p. What this constraint basically does, is it insures that all the powers flowing into the node i, from all the connected nodes k, are equal to the existing power value in the same node.
Here is an example for i=1 and i=2, iterations of the constraint.
Sample constraint
So I am trying to find the most efficient way to state this constraint in pyomo, where instead of having several constraint iterations written like this:
def P1_rule(modelo):
return modelo.p[0]-L[0]== ((modelo.d[0]-modelo.d[1])/0.1)+((modelo.d[0]-modelo.d[2])/0.1)
model.P1 = Constraint(rule=P1_rule)
def P2_rule(modelo):
return modelo.p[1]-L[1]==((modelo.d[1]-modelo.d[0])/0.1)+((modelo.d[1]-modelo.d[2])/0.1)
model.P2 = Constraint(rule=P2_rule)
def P3_rule(modelo):
return modelo.p[2]-L[2] ==((modelo.d[2]-modelo.d[0])/0.1)+((modelo.d[2]-modelo.d[1])/0.1)
model.P3 = Constraint(rule=P3_rule)
I want a single line like this, so that it easily generalizes over a huge network:
def P3_rule(modelo):
return modelo.p[i] ==((modelo.d[i]-modelo.d[k])/X[k])
model.P3 = Constraint(rule=P3_rule)
I have came up with a way that includes restructuring the data and creating new arrays of indices etc... I would like to see if i can apply the constraint using the data keeping the same structure and more directly.
Ok so I figured out how to do it. The best way, which I didn't know was possible, is through making an if statement within the summation, so basically make a full conditional iteration inside of the summation. In the code bellow, G is the list of nodes, while "From" and "To" are the branch number columns in the FullBranch data table.
def Try_rule(mod,g):
return mod.p[g] - L[g] == sum((mod.d[i-1]-mod.d[k-1])/FullBranch.loc[x,"X"] for x,(i,k) in enumerate(zip(FullBranch["From"], FullBranch["To"])) if i == g+1)
model.Try = Constraint(G,rule=Try_rule)

Pyomo creating a variable time index

I'm trying to bring this constraint in my pyomo model
[1
I define a set for indexing over time and I want to optimize the corresponding energy variable below
model.grid_time = Set(initialize=range(0, 23)))
model.charging_energy = Var(model.grid_time, initialize=0)
My constraint definition looks like as follows:
model.limits = ConstraintList()
for t in model.grid_time:
model.limits.add(sum(model.charging_energy[t] for t in model.grid >= energy_demand.at[t,"total_energy_demand"])
The problem with these codelines is that I'm summing over the whole indexing set model.grid_time and not just up to t. I think I need a second variable indexing set (replacing for t in model.grid), but I'm searching unsuccessfully after how creating a variable index set..
I would appreciate any help or comment!
Would something like this work?
def Sum_rule(model, v, t):
return sum(model.Ech[t2] for t2 in model.grid_time if t2 <= t) <= model.Edem[v,t]
model.Sum_constraint = Constraint(model.grid_time, model.V, rule=Sum_rule)
Essentially, what happens is that the t in the Sum_rule(model, v, t) makes sure that the constraint is called for each t in model.grid_times. The t2 in the sum is also part of model.grid_times, but it will only take values that are smaller than the t at which the constraint is called.
I am not sure if my constraint matches exactly your notation, as you have not provided all the information required (e.g. regarding the subscript v of the E^dem variable, but it will basically do what you want with the sum.

How to implement a cost minimization objective function correctly in Gurobi?

Given transport costs, per single unit of delivery, for a supermarket from three distribution centers to ten separate stores.
Note: Please look in the #data section of my code to see the data that I'm not allowed to post in photo form. ALSO note while my costs are a vector with 30 entries. Each distribution centre can only access 10 costs each. So DC1 costs = entries 1-10, DC2 costs = entries 11-20 etc..
I want to minimize the transport cost subject to each of the ten stores demand (in units of delivery).
This can be done by inspection. The the minimum cost being $150313. The problem being implementing the solution with Python and Gurobi and producing the same result.
What I've tried is a somewhat sloppy model of the problem in Gurobi so far. I'm not sure how to correctly index and iterate through my sets that are required to produce a result.
This is my main problem: The objective function I define to minimize transport costs is not correct as I produce a non-answer.
The code "runs" though. If I change to maximization I just get an unbounded problem. So I feel like I am definitely not calling the correct data/iterations through sets into play.
My solution so far is quite small, so I feel like I can format it into the question and comment along the way.
from gurobipy import *
#Sets
Distro = ["DC0","DC1","DC2"]
Stores = ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9"]
D = range(len(Distro))
S = range(len(Stores))
Here I define my sets of distribution centres and set of stores. I am not sure where or how to exactly define the D and S iteration variables to get a correct answer.
#Data
Demand = [10,16,11,8,8,18,11,20,13,12]
Costs = [1992,2666,977,1761,2933,1387,2307,1814,706,1162,
2471,2023,3096,2103,712,2304,1440,2180,2925,2432,
1642,2058,1533,1102,1970,908,1372,1317,1341,776]
Just a block of my relevant data. I am not sure if my cost data should be 3 separate sets considering each distribution centre only has access to 10 costs and not 30. Or if there is a way to keep my costs as one set but make sure each centre can only access the costs relevant to itself I would not know.
m = Model("WonderMarket")
#Variables
X = {}
for d in D:
for s in S:
X[d,s] = m.addVar()
Declaring my objective variable. Again, I'm blindly iterating at this point to produce something that works. I've never programmed before. But I'm learning and putting as much thought into this question as possible.
#set objective
m.setObjective(quicksum(Costs[s] * X[d, s] * Demand[s] for d in D for s in S), GRB.MINIMIZE)
My objective function is attempting to multiply the cost of each delivery from a centre to a store, subject to a stores demand, then make that the smallest value possible. I do not have a non zero constraint yet. I will need one eventually?! But right now I have bigger fish to fry.
m.optimize()
I produce a 0 row, 30 column with 0 nonzero entries model that gives me a solution of 0. I need to set up my program so that I get the value that can be calculated easily by hand. I believe the issue is my general declaring of variables and low knowledge of iteration and general "what goes where" issues. A lot of thinking for just a study exercise!
Appreciate anyone who has read all the way through. Thank you for any tips or help in advance.
Your objective is 0 because you do not have defined any constraints. By default all variables have a lower bound of 0 and hence minizing an unconstrained problem puts all variables to this lower bound.
A few comments:
Unless you need the names for the distribution centers and stores, you could define them as follows:
D = 3
S = 10
Distro = range(D)
Stores = range(S)
You could define the costs as a 2-dimensional array, e.g.
Costs = [[1992,2666,977,1761,2933,1387,2307,1814,706,1162],
[2471,2023,3096,2103,712,2304,1440,2180,2925,2432],
[1642,2058,1533,1102,1970,908,1372,1317,1341,776]]
Then the cost of transportation from distribution center d to store s are stored in Costs[d][s].
You can add all variables at once and I assume you want them to be binary:
X = m.addVars(D, S, vtype=GRB.BINARY)
(or use Distro and Stores instead of D and S if you need to use the names).
Your definition of the objective function then becomes:
m.setObjective(quicksum(Costs[d][s] * X[d, s] * Demand[s] for d in Distro for s in Stores), GRB.MINIMIZE)
(This is all assuming that each store can only be delivered from one distribution center, but since your distribution centers do not have a maximal capacity this seems to be a fair assumption.)
You need constraints ensuring that the stores' demands are actually satisfied. For this it suffices to ensure that each store is being delivered from one distribution center, i.e., that for each s one X[d, s] is 1.
m.addConstrs(quicksum(X[d, s] for d in Distro) == 1 for s in Stores)
When I optimize this, I indeed get an optimal solution with value 150313.

Categories

Resources