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.
Related
I have three different sets
Number of Store - 100
Number of Products - 10
Number of Size in each product - 10
I want to create Parameter in pyomo which is combination of above three sets. Basically i want to skip cross product which have code snippet below.
Reason to skip below approach is each product can have 10 different sizes and no need to create combination of product of A and sizes coming from product B, which doesn't make sense.
Code snippet with cross product:
model = pyo.AbstractModel()
model.stores = pyo.Set()
model.sizes = pyo.Set()
model.packs = pyo.Set()
model.products = pyo.Set()
model.demand = pyo.Param(model.clusters,
model.products,
model.sizes, default = 0)
So, if I understand your dilemma, the sizes are different for different products and a universal cross-set of products and sizes doesn't work because of that.
I think you have 2 options. Either works.
The easies thing to do would be just to make tuples of product-size pairs and use that as a set...basically merging the products with their sizes.
products = {(shoes, 12), (shoes, 13), (shoes, 5), (pants, XL), (pants, L),...}
It is perfectly legitimate to use a flat set like that and use that to initialize your pyomo.Set. It might get a little tricky if you need to sum over individual products because that info is merged with the sizes. Not sure if that is needed.
Option 2 is to use an indexed set, so you would have sets of sizes that are indexed by product. Here is an example using EV's and times. You would set it up similarly for products & sizes.
So I am trying to think of how I can list all possible combinations of 5 variables (that have a set value AND a second property, like a charge for example, so for example I would have x = 100 and has a charge state of +1) with different factors to each variable (so x can vary between 1 and 4, y only between 1 and 2).
At the end I would like to have a list with the combination of variables and the summed value of all like this:
4x+2y+3*z = 1400 (3+)
Is this possible? I am trying to wrap my head around this or if there are too many variables in this.
I am a beginner, so explain it like I am dumb.
Thx
I have a Python script which ends up creating a 2D array based on user input. Therefore, the length of the 2D array is unknown and the length of the individual arrays within the 2D array are also unknown until the user has input the information. I would like to sort the individual array pieces based on a value associated with them. An example of a possible output that needs to be sorted is below:
Basically, each individual array is a failure symptom followed by the a list of possible components, each having a "score" associated with them that is the likelihood that this component is causing the failure. My goal is to reorder the array with the components along with their scores in descending order based on the score, i.e., the component and score need to be moved together. The problem I have is like I said, I do not know the length of anything until user input is given. There could be only 1 failure symptom input, or there could be 9. The failure symptom could contain only 1 component, or maybe 12. I know it will take nested for loops and if statements, but I haven't been able to figure it out based on all the possible scenarios. Some possible scenarios I have thought of:
The array is already in order (move to the next failure symptom)
The first component is correct, but the ones after may not be. Or the first two are correct, but the ones after may not be, etc...
The array is completely backwards in order
The array only contains 1 component, therefore there is no need to sort
The array is in some random order, so some positions for some components may already be in the correct spot while some others aren't
Every time I feel like I am making headway, I think of another scenario which wouldn't hold up. Any help is greatly appreciated!
Your problem is a bit special. You don't only want to sort a multidimensional array, which would be rather simple using the default sorting algorithms, you also want to keep the order between the key/value pairs.
The second problem is that the keys are strings with numbers in it. So simple string comparison wouldn't work, because it is compared letter by letter, so "test9" > "test11" would be true (the second 1 wouldn't be even recognized, because 9>1).
The simpliest solution i figured out would be the following:
#get the failure id of one list
def failureId(value):
return int(value[0].replace("failure",""))
#get the id of one component
def componentId(value):
return int(value.replace("component",""))
#sort one failure list using bubble sort
def sortFailure(failure):
#iteraring through the array twice (only the keys, ignoring the values)
for i in range(1,len(failure), 2):
for j in range(1,i, 2):
#comparing the component ids
if (componentId(failure[j])>componentId(failure[j+2])):
#swaping keys and values
failure[j],failure[j+2] = failure[j+2],failure[j]
failure[j+1],failure[j+3] = failure[j+3],failure[j+1]
#sorting the full list
def sortData(data):
#sorting the failures using default sort algorithm
data.sort(key=failureId)
#sorting the single list of failure datas itself
for failure in data:
sortFailure(failure)
data = [['failure2', 'component2', 0.15, 'component1', 0.85], ['failure3', 'component1', 0.95], ['failure1','component1',0.05,'component3', 0.8, 'component2', 0.1, 'component4', 0.05]]
print(data)
sortData(data)
print(data)
The first two functions are required to get the numbers(=id) from the strings as mentioned above. The second function uses "bubble sort" to sort the array. It uses steps 2 for the range function, because we want to skipt the values for each component. If the data are in wrong order we are swapping the key & value. In the sortData function we are using the built in sort function for lists to sort the whole list (by failure ids). Then we take each "sublist" and sort them using the other function.
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.
I would like to create an array of Zipf Distributed values withing range of [0, 1000].
I am using numpy.random.zipf to create the values but I cannot create them within the range I want.
How can I do that?
normalize and multiply by 1000 ?
a=2
s = np.random.zipf(a, 1000)
result = (s/float(max(s)))*1000
print min(s), max(s)
print min(result), max(result)
althought isn't the whole point of zipf that the range of values is a function of the number of values generated ?
I agree with the original answer (Felix) that forcing Zipf values to a specific range is a very unusual thing, and it likely means that you're doing something wrong.
Having said that, I actually had a similar problem, where I really did need to generate Zipf values conforming to a certain criteria. In my case, I wanted to generate a brand new set of data that was similar to an existing data set. I wanted the sum to be the same as the existing distribution, but the values to be different.
My insight is that it's possible to re-generate the values a few times until you get ones you like.
#Generate a quantity of Zipf-distributed values close to a desired sum
def gen_zipf_values(alpha, sum, quantity):
best = []
best_sum = 0
for _ in range(10):
s = np.random.zipf(alpha,quantity)
this_sum = s.sum()
if (this_sum > best_sum) and (this_sum <= sum):
best = s
best_sum=this_sum
return best
Again, this solution is tailored to my problem, where I wanted to generate values close to a sum, without going over. I also had a pretty good idea of what I wanted alpha to be in each time. I omitted some of the conditions checking, sorting, etc. for clarity.
If you had to do it more than a few times though (i.e. you had to run the for loop 1 million times to get your distribution), you probably have something wrong (like alpha, or unrealistic expectations on the values). I feel it's valid to 'let the computer do the work', or to hand-pick the best option from a few reasonable ones.