I have the following table, from which a have to create a recipe with a specific value for protein and carbohydrates.
And using or-tools to solve this problem, so far I have:
The formated data
data = [
['f1', 10, 15, 17, 10],
['f2', 2, 11, 12, 14],
['f3', 6.5, 17, 16, 13],
['f4', 8, 12, 8, 16]
]
The constraines for the nutriends:
nutrients = [
["protein",15.5],
["carbohydrates",12.3]]
The objective function, where the upper bound "datai" is the stock of that particular element.
food = [[]] * len(data)
# Objective: minimize the sum of (price-normalized) foods.
objective = solver.Objective()
for i in range(0, len(data)):
food[i] = solver.NumVar(0.0, data[i][1], data[i][0])
objective.SetCoefficient(food[i], 4)
objective.SetMinimization()
I also have the as constrain the required value of each nutrient:
constraints = [0] * (len(nutrients))
for i in range(0, len(nutrients)):
constraints[i] = solver.Constraint(nutrients[i][1], solver.infinity())
for j in range(0, len(data)):
constraints[i].SetCoefficient(food[j], data[j][i+3])
And finally the solver:
status = solver.Solve()
if status == solver.OPTIMAL:
# Display the amounts (in dollars) to purchase of each food.
price = 0
num_nutrients = len(data[i]) - 3
nutrients = [0] * (len(data[i]) - 3)
for i in range(0, len(data)):
price += food[i].solution_value()
for nutrient in range(0, num_nutrients):
nutrients[nutrient] += data[i][nutrient+3] * food[i].solution_value()
if food[i].solution_value() > 0:
print ("%s = %f" % (data[i][0], food[i].solution_value()))
print ('Optimal price: $%.2f' % (price))
else: # No optimal solution was found.
if status == solver.FEASIBLE:
print ('A potentially suboptimal solution was found.')
else:
print ('The solver could not solve the problem.')
Which up to this part is working fine, the result I get is the following:
f1 = 0.077049
f3 = 0.886885
Optimal price: $0.96
Know I need to add as well the constraints of how many kg I will make, which have to satisfy the previous constraints as well.
My first guess was to add a multiplier to the nutrients requirement
factor = 10
nutrients = [
["protein",15.5*factor],
["carbohydrates",12.3*factor]]
Tjis way I will have 10 times more food, but then I realized that this is not correct, since what I need is a concentración E.G.
I need 10kg with 15.5 protein/kg and 12.3 carbohydrates/kg
the constrain I need is something like this:
(f1*W + f2*X + f3*Y + f4*Z)/(W+X+Y+Z) = 10kg with 15.5 protein/kg and 12.3 carbohydrates/kg
Where W, X, Y and Z are the kg of each food
How can I add this constrain to solver?
(f1*W + f2*X + f3*Y + f4*Z)/(W+X+Y+Z) = 10
is the same as
f1*W + f2*X + f3*Y + f4*Z = 10*(W+X+Y+Z)
This is now linear.
And, in case we missed some math classes, we can write this as a standard LP constraint:
(f1-10)*W + (f2-10)*X + (f3-10)*Y + (f4-10)*Z = 0
Related
So my current snippet of code is running properly, but I want to create a list where I could store all the payOut (variable)
so if:
wage = [10 , 5, 4] and
addedHours = [2 , 3, 2]
the new variable I want (lets call it totalWage):
totalWage = [20, 15, 9]
I want this totalWage variable to be part of the for loop in the code below. How would I do that?
def printPayroll(self):
totalPayroll = 0
i = 0
product = ""
for y in names:
payOut = float(wage[i]) * float(addedHours[i])
totalPayroll += payOut
product += ('%-10s%-10s%-0s%-0s') % (str(names[i]), str(addedHours[i]), str(payOut), "\n")
i += 1
finalPayroll = "Total Payroll = $" + str(totalPayroll)
def printPayroll(self):
totalPayroll = 0
i = 0
product = ""
totalWage = []
for y in names:
payOut = float(wage[i]) * float(addedHours[i])
totalPayroll += payOut
product += ('%-10s%-10s%-0s%-0s') % (str(names[i]), str(addedHours[i]), str(payOut), "\n")
i += 1
totalWage.append(payOut)
finalPayroll = "Total Payroll = $" + str(totalPayroll)
print(totalWage)
ps. Computation of payroll and print of it can be separated for better code. Also for loop can be simplified with zip function. I am not including because it is not part of the question.
You just need to define a totalWage list, and append your wage*addedHours for each item in that list. In addition, you can use enumerate to both get the indexes and the item in a list.
wage = [10 , 5, 4]
addedHours = [2 , 3, 2]
names = ['Jack', 'John', 'Joe']
def printPayroll():
totalWage = []
totalPayroll = 0
i = 0
product = ""
for i, name in enumerate(names):
payOut = float(wage[i]) * float(addedHours[i])
totalWage.append(payOut)
totalPayroll+= payOut
product += ('%-10s%-10s%-0s%-0s') % (name, str(addedHours[i]), str(payOut), "\n")
finalPayroll = "Total Payroll = $" + str(totalPayroll)
print(totalWage)
print(product)
print(finalPayroll)
printPayroll()
#[20.0, 15.0, 8.0]
#Jack 2 20.0
#John 3 15.0
#Joe 2 8.0
#Total Payroll = $43.0
You need to declare an empty list within function and keep appending every value of payout inside the loop. You can also use this faster if you have many rows for computation using zip & use pandas dataframe to store result in case you want to later write the output to csv and store for future references.
Here is the code:
wage = [10 , 5, 4]
addedHours = [2 , 3, 2]
import pandas as pd
def printPayroll():
totalPayroll = 0
totalWage = []
for w, h in zip(wage, addedHours):
payout = w*h
totalPayroll = totalPayroll + payout
totalWage.append(payout)
print("Total Payroll = $" + str(totalPayroll))
print("Total wage:" , totalWage)
# if names are A, B, C
names = ["A", "B", "C"]
print(pd.DataFrame({'name':names, 'added_hours': addedHours, 'total_wage': totalWage}))
printPayroll()
Hope this helps!
I have a code which return the number of trucks required to pack the given items based on the Weight and Volume of the items. This objective of this function is to minimize the cost of transportation
Code:
from pulp import *
import numpy as np
# Item masses, volumes
item_mass = data["Weight"].tolist()
item_vol = data["Volume"].tolist()
n_items = len(item_vol)
set_items = range(n_items)
# Mass & volume capacities of trucks
truck_mass = truck["Weight"].tolist()
truck_vol = truck["Volume"].tolist()
# Cost of using each truck
truck_cost = truck["Price"].tolist()
n_trucks = len(truck_cost)
set_trucks = range(n_trucks)
y = pulp.LpVariable.dicts('truckUsed', set_trucks,
lowBound=0, upBound=1, cat=LpInteger)
x = pulp.LpVariable.dicts('itemInTruck', (set_items, set_trucks),
lowBound=0, upBound=1, cat=LpInteger)
# Model formulation
prob = LpProblem("Truck allocatoin problem", LpMinimize)
# Objective
prob += lpSum([truck_cost[i] * y[i] for i in set_trucks])
# Constraints
for j in set_items:
# Every item must be taken in one truck
prob += lpSum([x[j][i] for i in set_trucks]) == 1
for i in set_trucks:
# Respect the mass constraint of trucks
prob += lpSum([item_mass[j] * x[j][i] for j in set_items]) <= truck_mass[i]*y[i]
# Respect the volume constraint of trucks
prob += lpSum([item_vol[j] * x[j][i] for j in set_items]) <= truck_vol[i]*y[i]
# Ensure y variables have to be set to make use of x variables:
for j in set_items:
for i in set_trucks:
x[j][i] <= y[i]
prob.solve()
x_soln = np.array([[x[i][j].varValue for i in set_items] for j in set_trucks])
y_soln = np.array([y[i].varValue for i in set_trucks])
print (("Status:"), LpStatus[prob.status])
print ("Total Cost is: ", value(prob.objective))
print("Trucks used: " + str(sum(([y_soln[i] for i in set_trucks]))))
a = []
b = []
for i in set_items:
for j in set_trucks:
if x[i][j].value() == 1:
print("Item " + str(i) + " is packed in vehicle "+ str(j))
a.append(str(j))
b.append(str(i))
totalitemvol = sum(item_vol)
totaltruckvol = sum([y[i].value() * truck_vol[i] for i in set_trucks])
print("Volume of used trucks is " + str(totaltruckvol))
if(totaltruckvol >= totalitemvol):
print("Trucks are sufficient")
else:
print("Items cannot fit")
This code return the output as follows:
Status: Optimal
Total Cost is: 400000.0
Trucks used: 3.0
Item 0 is packed in vehicle 7
Item 1 is packed in vehicle 7
Item 2 is packed in vehicle 6
Item 3 is packed in vehicle 7
Item 4 is packed in vehicle 16
Item 5 is packed in vehicle 7
Item 6 is packed in vehicle 16
Item 7 is packed in vehicle 7
Item 8 is packed in vehicle 16
Item 9 is packed in vehicle 6
Item 10 is packed in vehicle 16
Volume of used trucks is 3436.0
Trucks are sufficient
Instead of getting the index of items Can I replace "Item 0" with "Item (productId)" where ProductID is a series in the "data" Dataframe.
I am happy to give the data and trucks csv files or the colab link.
Instead of "Item " + str(i) + " is packed in vehicle " + str(j) and assuming that the order of the ProductID is the same as the order of set_trucks, you could do
s = pd.Series(['item0', 'item1', 'item2'])
for i in set_items:
for j in set_trucks:
print("Item " + str(s[i]) + " is packed in vehicle " + str(j))
Since you're using Python 3, you can do it faster by using string formatting such as
print(f"Item {s[i]} is packed in vehicle {j}")
This is just a fun project I thought would be cool to do, but I'm struggling to figure it out.
plates = [100, 45, 35, 25, 10, 5, 2.5]
goal_weight = 425
starting_weight = 45
while goal_weight > starting_weight:
My idea was to iterate through plates with a while loop. I need each number to max out into the goal weight (100 goes into 450 4 times) then moves onto the next number and attempts there, to show the ideal way to load the bar. But I may be on the wrong track here.
Example: 250 = 45lb bar(starting_weight), two 100lb plate, two 2.5lb plate
425 = 45lb bar, two 100lb, four 45lb
Wanting it to print something like: Two 100's, two 45's, two 10's
Here is a little program to find the right combinations of weight plates. Note the function zip that combines the list of number of weight plates with the list of weights. list(zip(nweights, weights)) forming a list of tuples, for example [(4, 100), (0, 45) ... (0, 2), (0, 2.5)]
weights=[100, 45, 35, 25, 10, 5, 2, 2.5]
targetweight = int(input('What is the target weight: '))
nweights = []
remaining = targetweight
for i, weight in enumerate(weights):
nweights.append(int(remaining/ weight))
remaining = remaining - nweights[i]*weights[i]
if remaining == 0:
break
listweights=zip(nweights, weights)
for weight in listweights:
print(f'you need {weight[0]} of weight {weight[1]} pound')
if remaining !=0:
print(f'the correct weight combination cannot be found,'
f'the remaining weight is: {remaining} pound')
This is what I ended up with. Appreciate the help, guys!
weights=[100, 45, 35, 25, 10, 5, 2.5]
target_weight = int(input('How much weight do you need? '))
bar_weight = int(input('Enter bar weight: '))
nweights = []
remaining = target_weight - bar_weight
for i, weight in enumerate(weights):
if int(remaining / weight) % 2 == 0:
nweights.append(int(remaining/ weight))
else:
nweights.append(int(remaining/ weight) - 1)
remaining = remaining - nweights[i]*weights[i]
if remaining == 0:
break
listweights=zip(nweights, weights)
print(f'{bar_weight}lb bar')
for weight in listweights:
if weight[0] >= 2:
print(f"{weight[0]} | {weight[1]}'s")
if remaining !=0:
print(f'the correct weight combination cannot be found,'
f'the remaining weight is: {remaining} pound')
Yes I think your solution works, though perhaps the below snippet is more logical...
(using a bit of numpy array methods)
import numpy as np
weights=np.array([100, 45, 35, 25, 10, 5, 2.5])
weights=weights*2
target_weight = int(input('How much weight do you need? '))
nweights=[]
remaining = target_weight
for i, weight in enumerate(weights):
nweights=np.append(nweights, int(remaining/ weight))
remaining = remaining - nweights[i]*weights[i]
if remaining == 0:
break
nweights = nweights*2
weights=weights*0.5
weightlist=zip(nweights, weights)
barweight=0
for weight in weightlist:
print(f"{weight[0]} | {weight[1]}'s")
barweight=barweight+weight[0]*weight[1]
print(f'total weight: {barweight} pound')
if remaining !=0:
print(f'the correct weight combination cannot be found,'
f'the remaining weight is: {remaining} pound')
Linear program:
Decision Variables:
x[]
Maximize: Sum(i to n) (fare[i] * x[i])
subject to:
x[i] <= demand[i]
I am trying to add a linear constraint in cplex using Python, but I am not able to get the syntax correct.
fare = [400.0, 700.0, 600.0]
dmd= [2, 4, 3]
itins = []
for count in range(3):
i = Itinerary(count, 1, dmd[count], fare[count])
itins.append(i)
# problem variables
X=[] * len(itins)
def setupproblem(c):
c.objective.set_sense(c.objective.sense.maximize)
num_x = range(len(itins))
print (num_x)
varnames = ["X"+str(j) for j in range(len(itins))]
c.variables.add(names = varnames, lb=[0.0, 0, 0], ub=[10.0, 10, 10])
x = [c.variables.add(obj=fare) for i in num_x]
i_iten = range(len(itins))
c.linear_constraints.add(lin_expr = [cplex.SparsePair(ind = i_iten,
val = X[i])
for i in range(len(itins) -1 )],
senses = ["L"],
rhs = capacity,
names = ["capacity_"+str(i)
for i in i_iten])
I am getting this error:
raise CplexError(" %d: Invalid name -- '%s'\n" % tuple(self._mat))
cplex.exceptions.errors.CplexError: 1210: Invalid name -- 'X'
In cplex.SparcePair you need to specify nonzero elements under val and the corresponding variables under ind. Plus, from your linear program your right-hand side should be dmd.
c.linear_constraints.add(lin_expr = [cplex.SparsePair(ind = xname,
val = [1.0])
for xname in varnames],
senses = ["L"],
rhs = dmd,
names = ["capacity_"+str(i)
for i in i_iten])
Also I would suggest to indicate the objective function when you add variables:
c.variables.add(obj = fare, names = varnames, lb=[0.0, 0, 0], ub=[10.0, 10, 10])
c.linear_constraints.add(lin_expr=[cplex.SparsePair(ind=[xname], val=[1.0]) for xname in varn], senses=["L"] * len(varnames), rhs=dmd)
But before you add the constraints on the variables, please keep in mind that the variable names should be added to the function. I spent 4 hours going round and round to figure out what was going wrong.
This line should come first.
c.variables.add(varnames)
Thanks serge_k for your initial pointers !!
I am trying to figure out what is wrong with my code. Currently, I am trying to get the averages of everything with the same temp (ex temp 18 = 225 conductivity average, temp 19 = 15 conductivity average, etc).
Could someone tell me if this is a simple coding mistake or a algorithm mistake and offer some help to fix this problem?
temp = [18,18,19,19,20]
conductivity = [200,250,20,10,15]
tempcheck = temp[0];
conductivitysum = 0;
datapoint = 0;
assert len(temp) == len(conductivity)
for i in range(len(temp)):
if tempcheck == temp[i]:
datapoint+=1
conductivitysum+=conductivity[i]
else:
print conductivitysum/datapoint
datapoint=0
conductivitysum=0
tempcheck=temp[i]
For some reason, it is printing out
225
10
When it should be printing out
225
15
15
in else clause
put :
conductivitysum=0
datapoint=0
tempcheck = temp[i]
conductivitysum+=conductivity[i]
datapoint+=1
because when you go to else clause, you miss that particular conductivity of i. It doesn't get saved. So before moving to next i, save that conductivity
Change the else to:
for i in range(len(temp)):
if tempcheck == temp[i]:
datapoint+=1
conductivitysum+=conductivity[i]
else:
print conductivitysum/datapoint
datapoint=1
conductivitysum=conductivity[i]
tempcheck=temp[i]
When you get to the pair (19, 20) you need to keep them and count one datapoint, not 0 datapoints. At the moment you are skipping them and only keeping the next one - (19, 10).
Alternatively, rewrite it as
>>> temp = [18,18,19,19,20]
>>> conductivity = [200,250,20,10,15]
# build a dictionary to group the conductivities by temperature
>>> groups = {}
>>> for (t, c) in zip(temp, conductivity):
... groups[t] = groups.get(t, []) + [c]
...
# view it
>>> groups
{18: [200, 250], 19: [20, 10], 20: [15]}
# average the conductivities for each temperature
>>> for t, cs in groups.items():
print t, float(sum(cs))/len(cs)
...
18 225
19 15
20 15
>>>
When I saw this code, the first thing that popped into my head was the zip function. I hope that the following code is what you want.
temp = [18,18,19,19,20]
conductivity = [200,250,20,10,15]
assert len(temp) == len(conductivity)
# Matches each temp value to its corresponding conductivity value with zip
relations = [x for x in zip(temp, conductivity)]
for possible_temp in set(temp): # Takes each possible temparature (18,19,20)
total = 0
divide_by = 0
# The next four lines of code will check each match and figure out the
# summed total conductivity value for each temp value and how much it
# should be divided by to create an average.
for relation in relations:
if relation[0] == possible_temp:
total += relation[1]
divide_by += 1
print(int(total / divide_by))