Pyomo bin sizes - pyomo

I am new to pyomo. I would like to ask if there is a way to achieved this requirement.
I want my asset to be assigned to 5 different bins. Each bin will have max capacity. for example, y1 has max 50, y2 has max 20,..
some of my assets can only go to certain bin. For example, A can only go to y1, y2. B can go to y4 and y5.
minimise the number of the bin used
Currently my code shown below and the year will all be filled with at least 1 asset . But I would like if only 2 or 3 of the year are used(minimize the number of bins) and would like if assets can be placed from smallest year to highest year
from pyomo.opt import SolverFactory
value_asset = {'J': 2, 'B': 4, 'D': 18, 'C': 34, 'A': 20, 'E': 31}
bins = {'y1': 50, 'y2': 20, 'y3': 30, 'y4': 70, 'y5': 40}
Assets = {'A': ['y1', 'y2'], 'J': ['y1', 'y2'], 'E': ["y4", "y5"], 'B': ["y4", "y5"],
'D': ['y5', "y4", "y3"],
'C': ["y1", "y2", 'y3', 'y4', 'y5']}
model = pyo.ConcreteModel()
model.Assets = pyo.Set(initialize=Assets.keys())
model.budget = pyo.Set(initialize=bins.keys())
model.x = pyo.Var(model.Assets, model.budget, within=pyo.Integers, bounds=(0, None))
model.less_budget = pyo.ConstraintList()
# make sure that all the total are always less than or equal to the budget
for b in model.budget:
model.less_budget.add(expr=sum(model.x[asset, b]*value_asset[asset] for asset in model.Assets) <= bins[b])
# we want to exclude certain year that some assets cannot do
model.excluded = pyo.ConstraintList()
for asset in model.Assets:
inc = Assets[asset]
exc = list(bins.keys() - inc)
for t in exc:
model.excluded.add(expr=model.x[asset, t] == 0)
# each item can only go to 1 bin
model.one_bins = pyo.ConstraintList()
for asset in model.Assets:
model.one_bins.add(expr=sum(model.x[asset, b] for b in (model.budget )) <= 1)
model.obj = pyo.Objective(expr=sum(model.x[asset, b] for asset in model.Assets for b in model.budget),sense=pyo.maximize)
solver = pyo.SolverFactory('cbc', executable=r'C:\Users\cc\Downloads\Cbc-2.10-win64-msvc15-md\bin\cbc.exe')
solver.solve(model)
model.x.display()

Related

In the PuLP scheduling problem, how to bring/group the consecutive Zeros together and still get an optimal solution? For meal breaks in the schedule

Solving an agent scheduling problem using PuLP.
8 hours of shift, 4 agents.
I have to generate an output where the 8 hrs of shift is divided into 15-minute intervals. Hence, 32 total periods. (the 15 minute periods of an 8 hour shift is a fixed input, which is not allowed to be tweaked.)
At any given period, there need to be a minimum of 3 agents working (i.e.not on break)
Now, there needs to be a 1 hour meal break, and a 30 min short break.
So, for 1 hour meal break, I will have to combine 4 periods of 15 mins, and for the 30 min short break, I'll have to combine 2 periods of 15 mins.
I tried getting 26 counts of 1... and, 6 counts of 0.
The idea was to then combine 4 zeros together (meal break), and the remaining 2 zeros together (short break).
The current LpStatus is 'infeasible'... if i remove the constraint where i am trying to club the zeros, then the solution is optimal, or else it shows infeasible.
Have also pasted my final dataframe output screenshots.
import pandas as pd
import numpy as np
import scipy as sp
import seaborn as sns
import matplotlib.pyplot as plt
from pandas.plotting import table
import os, sys, json
from pulp import *
%matplotlib inline
# agent works either 1, 2, 3, 4, 5, 6, 7, 8 hours per week.
working_periods = [26]
# maximum periods that agent will work (7 hrs in each shift)
max_periods = 26
# planning_length
planning_length = 1 # TODO: Total number of shifts i.e. 21 in our case
# Number of periods per shift:
daily_periods = [0, 1, 2, 3, 4, 5, 6, 7, 8,9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
# Label the days from Monday to Sunday.
s = ['Period']
# Create the required_agents dataframe
col_2 = range(0, 1*planning_length)
required_agents_per_period = pd.DataFrame(data = None, columns=s, index = daily_periods)
for j in col_2:
# Small number is better for visualization.
required_agents_per_period.iloc[0][j] = 3
required_agents_per_period.iloc[1][j] = 3
required_agents_per_period.iloc[2][j] = 3
required_agents_per_period.iloc[3][j] = 3
required_agents_per_period.iloc[4][j] = 3
required_agents_per_period.iloc[5][j] = 3
required_agents_per_period.iloc[6][j] = 3
required_agents_per_period.iloc[7][j] = 3
required_agents_per_period.iloc[8][j] = 3
required_agents_per_period.iloc[9][j] = 3
required_agents_per_period.iloc[10][j] = 3
required_agents_per_period.iloc[11][j] = 3
required_agents_per_period.iloc[12][j] = 3
required_agents_per_period.iloc[13][j] = 3
required_agents_per_period.iloc[14][j] = 3
required_agents_per_period.iloc[15][j] = 3
required_agents_per_period.iloc[16][j] = 3
required_agents_per_period.iloc[17][j] = 3
required_agents_per_period.iloc[18][j] = 3
required_agents_per_period.iloc[19][j] = 3
required_agents_per_period.iloc[20][j] = 3
required_agents_per_period.iloc[21][j] = 3
required_agents_per_period.iloc[22][j] = 3
required_agents_per_period.iloc[23][j] = 3
required_agents_per_period.iloc[24][j] = 3
required_agents_per_period.iloc[25][j] = 3
required_agents_per_period.iloc[26][j] = 3
required_agents_per_period.iloc[27][j] = 3
required_agents_per_period.iloc[28][j] = 3
required_agents_per_period.iloc[29][j] = 3
required_agents_per_period.iloc[30][j] = 3
required_agents_per_period.iloc[31][j] = 3
# List of number of agents required in specific periods
r_p = required_agents_per_period.values.swapaxes(0,1).ravel()
print("The number of agents required for each period is: ")
print (r_p)
print("Total no. of periods is: ", len(r_p))
print ("\nIn matrix form:")
print (required_agents_per_period)
# Total number of the agents
total = 4
print ("\nTotal number of agents are: {}".format(total))
# Create agents_id tag
agent_id_working_in_shift = ['agent7', 'agent10', 'agent13', 'agent18'] # TODO: Important: Here agent_id will be array of agents that will be extracted from dataframe.
print ("\nThe agents are: ")
print (agent_id_working_in_shift)
# Total periods
periods = range(1*32)
agents_per_shift = range(total)
## Create shift names based on index:
period_name = []
for p in periods:
period_name.append(s[0] + '_' + 'P' + str(p))
print("The periods are: ")
print(periods)
print("\nThe names of corresponding periods are: ")
print(period_name)
print("\nThe agents are: ")
print(agents_per_shift)
def LpProb():
# The prob variable is created to contain the problem data
prob = LpProblem("Agents Meal Scheduling Per Shift",LpMinimize)
# Creating the variables.
var = {
(n, p): pulp.LpVariable(
"schdule_{0}_{1}".format(n, p), cat = "Binary")
for n in agents_per_shift for p in periods
}
# add constraints:
for n in agents_per_shift:
for p in periods:
prob.addConstraint(var[(n,p)] <= 1)
# add constraints:
# Exactly 32 working periods per shift
for n in agents_per_shift:
prob.addConstraint(
sum(var[(n,p)] for p in periods) == 26
)
for n in agents_per_shift:
for p in periods:
if(p == periods[-1] or p == periods[-2] or p == periods[-3]):
continue
prob.addConstraint(var[(n,p)] + var[(n,p+1)] + var[(n,p+2)] + var[(n,p+3)] == 0)
# add constraints
# for each shift, the numbers of working agents should be greater than or equal to
# the required numbers of agents
for p in periods:
try:
prob.addConstraint(
sum(var[(n,p)] for n in agents_per_shift) >= 3
)
except:
print("len(periods) should be equal to len(agents_per_shift)")
sys.exit(-1)
prob.objective = sum(var[(n,p)] for n in agents_per_shift for p in periods)
return var, prob
# Run the solver
var, prob = LpProb()
prob.solve()
print(LpStatus[prob.status])
def agent_scheduling(var = var):
schedule = pd.DataFrame(data=None, index = agent_id_working_in_shift, columns = period_name)
for k, v in var.items():
n, p = k[0], k[1]
schedule.iloc[n][p] = int(value(v)) # pulp.value()
return schedule
schedule = agent_scheduling()
schedule.T
I was expecting an output of only zeros and one.
Want to combine four 0's as meal break, that need to be consecutive, and then a two 0's consecutive for short break (remaining 26 cells should be 1's)
Also, only 1 agent can be on a break (0), the other 3 agents needs to be working in that period (1)
OUTPUTS:-
enter image description here
enter image description here
enter image description here

Newbye in pyomo : translate a Concrete problem into Abstract

I'm encoutering some issues trying to translate a Contrete Model into Abstract one.
Months = RangeSet(6)
RequiredHours = {1: 8000, 2: 9000, 3: 9800, 4: 9900, 5: 10050, 6: 10500}
Notifications = {1: 2, 2: 0, 3: 2, 4: 0, 5: 1, 6: 0}
Costs = {'Attendants': 5100, 'Trainees': 3600}
Hours = {'Attendants': 150, 'Trainees': 25}
TraineeMax = 5
model = ConcreteModel()
model.TraineesNb = Var(Months,domain=NonNegativeIntegers,bounds=(0,TraineeMax))
AvailableAttendants = {}
AvailableTrainees = {}
for m in Months:
if m == 1:
AvailableAttendants[m] = 62
AvailableTrainees[m] = model.TraineesNb[m]
if m == 2:
AvailableAttendants[m] = AvailableAttendants[m-1] - Notifications[m-1]
AvailableTrainees[m] = model.TraineesNb[m] + model.TraineesNb[m-1]
if m > 2:
AvailableAttendants[m] = AvailableAttendants[m-1] - Notifications[m-1] + model.TraineesNb[m-2]
AvailableTrainees[m] = model.TraineesNb[m] + model.TraineesNb[m-1]
# Objective
model.Costs = Objective(
expr = sum(AvailableAttendants[m] for m in Months)*Costs["Attendants"] +
sum(AvailableTrainees[m] for m in Months)*Costs["Trainees"] ,
sense = minimize)
# declare constraints
model.NeededHours = ConstraintList()
for m in Months:
model.NeededHours.add(expr = AvailableAttendants[m]*Hours["Attendants"] +
AvailableTrainees[m]*Hours["Trainees"] >= RequiredHours[m])
The concrete model is forwarding the adequate resulys (perhaps not the most elegant code, but it works). In Discrete version of this model, an error occurs at the loop [for m in model.Months]
Thanks for your help and comments
Naji

How to convert 35 classes of cityscapes dataset to 19 classes?

The following is a small snippet of my code. Using this, I can train my model called 'lolnet' on cityscapes dataset. But the dataset contains 35 classes/labels [0-34].
imports ***
trainloader = torch.utils.data.DataLoader(
datasets.Cityscapes('/media/farshid/DataStore/temp/cityscapes/', split='train', mode='fine',
target_type='semantic', target_transform =trans,
transform=input_transform ), batch_size = batch_size, num_workers = 2)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
net = lolNet()
criterion = CrossEntropyLoss2d()
net.to(device)
num_of_classes = 34
for epoch in range(int(0), 200000):
lr = 0.0001
for batch, data in enumerate(trainloader, 0):
inputs, labels = data
labels = labels.long()
inputs, labels = inputs.to(device), labels.to(device)
labels = labels.view([-1, ])
optimizer = optim.Adam(net.parameters(), lr=lr)
optimizer.zero_grad()
outputs = net(inputs)
outputs = outputs.view(-1, num_of_class)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
outputs = outputs.to('cpu')
outputs = outputs.data.numpy()
outputs = outputs.reshape([-1, num_of_class])
mask = np.zeros([outputs.shape[0]])
#
for i in range(len(outputs)):
mask[i] = np.argmax(outputs[i])
mask = mask.reshape([-1, 1])
IoU = jaccard_score(labels.to('cpu').data, mask, average='micro')
But I want to train my model only on the 19 classes. These 19 classes are found here . The labels to train for are stored as "ignoreInEval" = True. This pytorch Dataloader helper for this dataset doesnt provide any clue.
So my question is how can I train my model on the desired 19 classes of this dataset using pytorch's "datasets.Cityscapes" api.
It's been a time, but leaving an answer as can be useful for others:
Firstly create a mapping to 19 classes + background. Background is related to not so important classes with ignore flag as said here.
# Mapping of ignore categories and valid ones (numbered from 1-19)
mapping_20 = {
0: 0,
1: 0,
2: 0,
3: 0,
4: 0,
5: 0,
6: 0,
7: 1,
8: 2,
9: 0,
10: 0,
11: 3,
12: 4,
13: 5,
14: 0,
15: 0,
16: 0,
17: 6,
18: 0,
19: 7,
20: 8,
21: 9,
22: 10,
23: 11,
24: 12,
25: 13,
26: 14,
27: 15,
28: 16,
29: 0,
30: 0,
31: 17,
32: 18,
33: 19,
-1: 0
}
Then for each label image (the gray images where each pixel contains a class, which has pattern "{city}__{number}_{number}_gtFine_labelIds.png") that you load for training, run function below.
It will convert each pixel according to mapping above and your label images (masks) will have now only 20 (19 classes + 1 background) different values, instead of 35.
def encode_labels(mask):
label_mask = np.zeros_like(mask)
for k in mapping_20:
label_mask[mask == k] = mapping_20[k]
return label_mask
Then you can train your model normally with these new number of classes.
You download the model and the weights.
import torch
import torch.nn as nn
import torchvision.models as models
r = models.resnet50(pretrained=True)
Note that original resent has 1000 categories/classes. So when you download pretrained model that last fc will be for 1000 classes.
Here is the forward() method you have, and above that code is your model.
You can remove the last fc fully connected layer from the original resnet50 model and add your new fc with exactly 19 classes (19 outputs) and you can train the classifier only for that last layer. The other layers, except that last should be frozen.
So you will learn just the 19 classes you need.
Note the resent __init__ method may also take the number of classes so you may try that, but in this case you cannot load the pretrained weights so you need to use pretrained=False and you need to train from scratch.
import torch
import torch.nn as nn
import torchvision.models as models
r = models.resnet50(num_classes=19, pretrained=False)

python networkX: Making graph from tuples and assigning different colour for nodes

new = (('AXIN', 37, REPORTED),
('LGR', 34, REPORTED),
('NKD', 29, REPORTED),
('TNFRSF', 23, REPORTED),
('APCDD', 18, REPORTED),
('TOX', 15, UNREPORTED),
('LEF', 14, REPORTED),
('PLCB', 13, REPORTED),
('MME', 13, UNREPORTED),
('NOTUM', 13,UN REPORTED),
('GNG', 11, , REPORTED),
('LOXL', 10, UNREPORTED))
import matplotlib.pyplot as plt
import networkx as nx
children = sorted(new, key=lambda x: x[1])
parent = children.pop()[0]
G = nx.Graph()
for child, weight in children: G.add_edge(parent, child, weight=weight)
width = list(nx.get_edge_attributes(G, 'weight').values())
plt.savefig("plt.gene-expression.pdf")
plt.figure(figsize = (20, 10))
nx.draw_networkx(G, font_size=10, node_size=2000, alpha=0.6) #width=width is very fat lines
plt.savefig("gene-expression-graph.pdf")
In this nx graph, how can I make the UNREPORTED - green color, REPORTED-yellow color?
Parent node is the node with the largest number i.e., AXIN, 37
colors = []
for i in new:
if i[2] == 'UNREPORTED':
colors.append('green')
elif i[2] == 'REPORTED':
colors.append('yellow')
nx.draw_networkx(G, font_size=10, node_size=2000, alpha=0.6, node_color=colors)
The mismatch in ordering comes from the dictionaries that underlie networkx's graph representation. If you ensure that the list of colors is ordered the same way you will have the right color for the right node.
I've written two different approaches here that achieve what I think you want.
Note: I declared values for reported and unreported, rather than turning the third piece of every tuple into a string. But this part isn't essential
# Delcare the graph:
REPORTED = 1
UNREPORTED = 2
new = (('AXIN', 37, REPORTED),
('LGR', 34, REPORTED),
<...>
('LOXL', 10, UNREPORTED))
# 2 axes to show different approaches
plt.figure(1); plt.clf()
fig, ax = plt.subplots(1, 2, num=1, sharex=True, sharey=True)
### option 1: draw components step-by-step
# positions for drawing of all components in right place
pos = nx.spring_layout(G)
# identify which nodes are reported/unreported
nl_r = [name for (name, w, state) in new if state == REPORTED]
nl_u = [name for (name, w, state) in new if state == UNREPORTED]
# draw each subset of nodes in relevant color
nx.draw_networkx_nodes(G, pos=pos, nodelist=nl_r, node_color='g', nodesize=2000, ax=ax[0])
nx.draw_networkx_nodes(G, pos=pos, nodelist=nl_u, node_color='y', nodesize=2000, ax=ax[0])
# also need to draw the egdes
nx.draw_networkx_edges(G, pos=pos, ax=ax[0])
nx.draw_networkx_labels(G, pos=pos, ax=ax[0], font_size=10)
### option 2: more complex color list construction (but simpler plot command)
nl, cl = zip(*[(name, 'g') if state == REPORTED else (name, 'y') for (name, w, state) in new])
nx.draw_networkx(G, pos=pos, nodelist=nl, node_color=cl, nodesize=2000, ax=ax[1], font_size=10)
plt.show()

defined function not found

I have a script as follows:
import numpy as np
import pandas as pd
import pdb
# conventions: W = fitness, A = affinity ; sex: 1=M, 0=F; alien: 1=alien,
# 0=native
# pop array order: W, A, sex, alien
def mkpop(n):
W = np.repeat(a=1, repeats=n)
A = np.random.normal(1, 0.1, size=n)
A[A < 0] = 0
alien = np.repeat(a=False, repeats=n)
sex = np.random.randint(0, 2, n)
pop = np.array([W, A, sex, alien])
pop = np.transpose(pop)
return pop
def migrate(pop, n=10, gParams=[1, 0.1]):
W = np.random.gamma(shape=gParams[0], scale=gParams[1], size=n)
A = np.repeat(1, n)
# 0 is native; 1 is alien
alien = np.repeat(True, n)
# 0 is female
sex = np.random.randint(0, 2, n)
popAlien = np.array([W, A, sex, alien])
popAlien = np.transpose(popAlien)
pop = np.vstack((pop, popAlien))
return pop
def mate(pop):
# split into male and female
f = pop[pop[:, 2] == 0]
m = pop[pop[:, 2] == 1]
# create transition matricies for native and alien mates
# m with native = m.!alien.transpose * f.alien
# negate alien
naLog = list(np.asarray(m[:, 3]) == False)
naPdMat = np.outer(naLog, f[:, 1])
# mate with alien = m.alien.transpose * affinity
alPdMat = np.outer(m[:, 3], f[:, 1])
# add transition matrices for probability density matrix
pdMat = alPdMat + naPdMat
# transition matrix is equal to the pd matrix / column sumso
colSums = np.sum(pdMat, axis=0)
pMat = pdMat / colSums
# select mates
def choice(x):
ch = np.random.choice(a=range(0, len(x)), p=x)
return ch
mCh = np.apply_along_axis(choice, 0, pMat)
mCh = m[mCh, :]
WMid = (f[:, 0] + mCh[:, 0]) / 2
AMid = (f[:, 1] + mCh[:, 1]) / 2
# assign fitness based on group affiliation; only native/alien matings have
# modified fitness
# reassign fitness and affinity based on group id and midparent vals
W1 = np.where(
(f[:, 3] == mCh[:, 3]) |
((f[:, 3] == 1) & (mCh[:, 3] == 0))
)
WMid[W1] = 1
# number of offspring is a poisson-distributed variable with lambda=2W
nOff = map(lambda x: np.random.poisson(lam=x), 2 * WMid)
# generate offspring
# expand list of nOff to numbers of offspring per pair
# realized offspring is index posisions of W and A vals to be replicated
# for offspring
# this can be rewritten to return a matrix of the appropriate length. This
# should work
midVals = np.array([WMid, AMid]).T
realOff = np.array([0, 0])
for i in range(0, len(nOff)):
sibs = np.repeat([np.array(midVals[i])], [nOff[i]], axis=0)
realOff = np.vstack((realOff, sibs))
offspring = np.delete(realOff, 0, 0)
sex = np.random.randint(0, 2, len(offspring))
alien = np.repeat(0, len(offspring))
otherStats = np.array([sex, alien]).T
offspring = np.hstack([offspring, otherStats])
return offspring # should return offspring
def sim(nInit, nGen=100, nAlien=10, gParams=[1, 0.1]):
gen = 0
pop = mkpop
stats = pd.DataFrame(columns=('gen', 'W', 'WMean', 'AMean', 'WVar', 'AVar'))
while gen < nGen:
pop = migrate(pop, nAlien, gParams)
offspring = mate(pop)
var = np.var(offspring, axis=0)
mean = np.mean(offspring, axis=0)
N = len(offspring)
W = N / nInit
genStats = N.append(W, gen, mean, var)
stats = stats.append(genStats)
print(N, gen)
gen = gen + 1
return stats
print mkpop(100)
print mate(mkpop(100))
#
sim(100, 100, 10, [1, 0.1])
Running this script, outputs NameError: name 'sim' is not defined. It is apparent from the commands before the final one that all the other functions defined within this script work without a hitch. I'm not sure what is going on here, and there is probably some very easy fix that I'm overlooking. Ctags recognizes this function just fine. It's entirely possibe that sim() doesn't actually work yet, as I haven't been able to debug it.
Your sim function defined in mate function scope so it's invisible to global scope. You need to fix your indentation for sim function