I have a pandas df that contains fields A, B, and C. I have a scipy minimization function that uses A, B, and C, and returns x. I want to add the value of x as new field for each row in the same df. I have tested the scipy function separately on single values of A, B, and C entered directly as values instead of as df['name'] and it works, returning the value of x. The code does not work when I try to operate on the entire df. Any ideas? Each of A, B, and C are Float64 in the df. Here is the error message:
Here is the code:
from scipy.optimize import minimize_scalar
import math
# set parameters
current_price_per_share = df['share_price']
shares_outstanding_in_millions = df['shares_(diluted)'] / 1000000
owners_income = df['owners_income'] / 1000000
def growth_needed(x, current_price_per_share, shares_outstanding_in_millions, owners_income):
# set growth rates and cost of capital
fcf_growth_1_to_5 = x / 100
fcf_growth_6_to_10 = fcf_growth_1_to_5 / 2 # half the rate of year 1-5
terminal_growth_rate = .03 # constant
cost_of_capital = .1 #constant
# calculate ten years of Free Cash Flow and the Terminal Value
FCF_01 = owners_income * (1 + fcf_growth_1_to_5)
FCF_02 = FCF_01 * (1 + fcf_growth_1_to_5)
FCF_03 = FCF_02 * (1 + fcf_growth_1_to_5)
FCF_04 = FCF_03 * (1 + fcf_growth_1_to_5)
FCF_05 = FCF_04 * (1 + fcf_growth_1_to_5)
FCF_06 = FCF_05 * (1 + fcf_growth_6_to_10)
FCF_07 = FCF_06 * (1 + fcf_growth_6_to_10)
FCF_08 = FCF_07 * (1 + fcf_growth_6_to_10)
FCF_09 = FCF_08 * (1 + fcf_growth_6_to_10)
FCF_10 = FCF_09 * (1 + fcf_growth_6_to_10)
term_value = (FCF_10 * (1 + terminal_growth_rate)) / (cost_of_capital - terminal_growth_rate)
# calcuate the Present Value for each period
PV_01 = FCF_01 * (1 /(( 1 + cost_of_capital) ** 1))
PV_02 = FCF_02 * (1 /(( 1 + cost_of_capital) ** 2))
PV_03 = FCF_03 * (1 /(( 1 + cost_of_capital) ** 3))
PV_04 = FCF_04 * (1 /(( 1 + cost_of_capital) ** 4))
PV_05 = FCF_05 * (1 /(( 1 + cost_of_capital) ** 5))
PV_06 = FCF_06 * (1 /(( 1 + cost_of_capital) ** 6))
PV_07 = FCF_07 * (1 /(( 1 + cost_of_capital) ** 7))
PV_08 = FCF_08 * (1 /(( 1 + cost_of_capital) ** 8))
PV_09 = FCF_09 * (1 /(( 1 + cost_of_capital) ** 9))
PV_10 = FCF_10 * (1 /(( 1 + cost_of_capital) ** 10))
PV_TV = term_value * (1 /(( 1 + cost_of_capital) ** 11))
#sum the Present Values and calculate the value per share
intrinsic_value = PV_01 + PV_02 + PV_03 + PV_04 + PV_05 + PV_06 + PV_07 + PV_08 + PV_09 + PV_10 + PV_TV
intrinsic_value_per_share = intrinsic_value / shares_outstanding_in_millions
# calculate the growth rate in year 1-5 needed to match the current share price
# the square and square root are to force to zero before adding back the growth rate
return ((math.sqrt((intrinsic_value_per_share - current_price_per_share) ** 2)) + x )
res = minimize_scalar(growth_needed, method='bounded', bounds=(-50, 100), args=(current_price_per_share, shares_outstanding_in_millions, owners_income,))
# notice the trailing comma in the Args function to make it a tuple
df['implied_growth'] = res.x / 100
I am creating a function that calculate the input from an Excel spreadsheet. However, the function is returning #value!. When I checked via MsgBox and debug Watch, the function is able to calculate a correct value.
Public Function pT_Flash_PR(comp, zComp, Temp, Pres) As Variant
NC = UBound(comp) - LBound(comp) + 1
Dim VapFrac As Variant
ReDim Tc(1 To NC)
ReDim Pc(1 To NC)
ReDim w(1 To NC)
ReDim Keq(1 To NC)
ReDim xComp(1 To NC)
ReDim yComp(1 To NC)
ReDim acrit(1 To NC)
ReDim bCrit(1 To NC)
ReDim Kappa(1 To NC)
ReDim alpha(1 To NC)
ReDim PR_Kij(1 To NC, 1 To NC)
ReDim psi_Liq(1 To NC)
ReDim psi_Vap(1 To NC)
ReDim phi_Liq(1 To NC)
ReDim phi_Vap(1 To NC)
ReDim Keq_New(1 To NC)
ReDim fug_Liq(1 To NC)
ReDim fug_Vap(1 To NC)
Dim ResSum(1 To 2) As Double
comp = Application.Transpose(comp)
zComp = Application.Transpose(zComp)
pSat_coeff_Range = Sheets("Database").Range("c3:L18")
PR_Kij_Database = Sheets("Database").Range("y3:an18")
VapFrac = 0.5
dVapFrac = 0.001
RConst = 8.31446261815324
For j = 1 To NC
For i = 1 To NC
rowNum = Application.Match(comp(i), Worksheets("Database").Range("x3:x18"), 0)
ColNum = Application.Match(comp(j), Worksheets("Database").Range("y2:AN2"), 0)
PR_Kij(i, j) = Application.Index(PR_Kij_Database, rowNum, ColNum)
Tc(i) = Application.WorksheetFunction.VLookup(comp(i), pSat_coeff_Range, 8, False) 'Tc in K
Pc(i) = Application.WorksheetFunction.VLookup(comp(i), pSat_coeff_Range, 9, False) * 1000
w(i) = Application.WorksheetFunction.VLookup(comp(i), pSat_coeff_Range, 10, False)
Keq(i) = Pc(i) / Pres * Exp(5.37 * (1 + w(i)) * (1 - Tc(i) / Temp))
Next
Next
For iter02 = 1 To 100
For iter01 = 1 To 100
For j = 1 To 2
VapFrac = VapFrac + (j - 1) * dVapFrac
For i = 1 To NC
Res = zComp(i) * (Keq(i) - 1) / (1 + VapFrac * (Keq(i) - 1))
ResSum(j) = ResSum(j) + Res
Next
Next
dResSum = 1 / dVapFrac * (ResSum(2) - ResSum(1))
VapFracNew = VapFrac - ResSum(1) / dResSum
Dif = Abs(VapFracNew - VapFrac)
If (Dif < 0.00000001) Then
Exit For
End If
VapFrac = VapFracNew
Next iter01
a_alpha_mixL = 0
bmixL = 0
a_alpha_mixV = 0
bmixV = 0
For i = 1 To NC
xComp(i) = zComp(i) / (1 + VapFrac * (Keq(i) - 1))
yComp(i) = zComp(i) * Keq(i) / (1 + VapFrac * (Keq(i) - 1))
acrit(i) = 0.45724 * RConst ^ 2 * Tc(i) ^ 2 / Pc(i) 'ai = 0.45724*R^2*Tci^2/Pci
bCrit(i) = 0.0778 * RConst * Tc(i) / Pc(i) 'bi = 0.07780*R*Tci/Pci
Kappa(i) = 0.37464 + 1.5422 * w(i) - 0.26992 * w(i) ^ 2 'Kappai = 0.3796 + 1.5422*wi - 0.2699*wi^2
alpha(i) = (1 + Kappa(i) * (1 - (Temp / Tc(i)) ^ 0.5)) ^ 2 'alphai = [1+mi*(1-sqrt(Temp/Tci)]^2
Next
For i = 1 To NC
'Calculation OF a*alpha_mix abd bmix for Liquid
bmixL = bmixL + xComp(i) * bCrit(i)
For j = 1 To NC
a_alpha_mixL = a_alpha_mixL + xComp(i) * xComp(j) * (acrit(i) * acrit(j) * alpha(i) * alpha(j)) ^ 0.5 * (1 - PR_Kij(i, j))
Next
'Calculation OF a*alpha_mix abd bmix for Vapour
bmixV = bmixV + yComp(i) * bCrit(i)
For j = 1 To NC
a_alpha_mixV = a_alpha_mixV + yComp(i) * yComp(j) * (acrit(i) * acrit(j) * alpha(i) * alpha(j)) ^ 0.5 * (1 - PR_Kij(i, j))
Next
Next
'Calculation A AND B constant for Compressibility Equation
ALPR = a_alpha_mixL * Pres / (RConst * Temp) ^ 2
BLPR = bmixL * Pres / (RConst * Temp)
AVPR = a_alpha_mixV * Pres / (RConst * Temp) ^ 2
BVPR = bmixV * Pres / (RConst * Temp)
'Iteration of Compressibility Equation for Liquid Phase
ZLiq = 0
For k = 1 To 100
Z = ZLiq
ZForm = Z ^ 3 + (BLPR - 1) * Z ^ 2 + Z * (ALPR - 3 * BLPR ^ 2 - 2 * BLPR) - (ALPR * BLPR - BLPR ^ 2 - BLPR ^ 3)
dZ = 0.0001
Z = ZLiq + dZ
ZFormdZ = Z ^ 3 + (BLPR - 1) * Z ^ 2 + Z * (ALPR - 3 * BLPR ^ 2 - 2 * BLPR) - (ALPR * BLPR - BLPR ^ 2 - BLPR ^ 3)
ZLiq = Z - ZForm / ((ZFormdZ - ZForm) / dZ)
EpsZLiq = Abs(ZLiq - Z)
If (EpsZLiq < 0.0000001) Then
Exit For
End If
Next
'Iteration of Compressibility Equation for Vapour Phase
ZVap = 1
For k = 1 To 100
Z = ZVap
ZForm = Z ^ 3 + (BVPR - 1) * Z ^ 2 + Z * (AVPR - 3 * BVPR ^ 2 - 2 * BVPR) - (AVPR * BVPR - BVPR ^ 2 - BVPR ^ 3)
dZ = 0.0001
Z = ZVap + dZ
ZFormdZ = Z ^ 3 + (BVPR - 1) * Z ^ 2 + Z * (AVPR - 3 * BVPR ^ 2 - 2 * BVPR) - (AVPR * BVPR - BVPR ^ 2 - BVPR ^ 3)
ZVap = Z - ZForm / ((ZFormdZ - ZForm) / dZ)
EpsZVap = Abs(ZVap - Z)
If (EpsZVap < 0.0000001) Then
Exit For
End If
Next
'Calculation of Fug Coefficent for Liq and Gas
EpsK = 0
For i = 1 To NC
' Calculation of psi Liq for individual compoennt for Fug Coefficient Eq
psi_Liq(i) = 0
For j = 1 To NC
psi_Liq(i) = psi_Liq(i) + xComp(j) * (acrit(i) * acrit(j) * alpha(i) * alpha(j)) ^ 0.5 * (1 - PR_Kij(i, j))
Next
' Calculation of Liquid Fug Coefficient, PhiL for individual component
phi_Liq(i) = Exp(bCrit(i) * (ZLiq - 1) / bmixL - logE(ZLiq - BLPR) - ALPR / 8 ^ 0.5 / (BLPR) * (2 * psi_Liq(i) / a_alpha_mixL - bCrit(i) / bmixL) * logE((ZLiq + BLPR * (1 + 2 ^ 0.5)) / (ZLiq + BLPR * (1 - 2 ^ 0.5))))
Cells(i, 17) = PhiL
' Calculation of psi Vap for individual compoennt for Fug Coefficient Eq
psi_Vap(i) = 0
For j = 1 To NC
psi_Vap(i) = psi_Vap(i) + yComp(j) * (acrit(i) * acrit(j) * alpha(i) * alpha(j)) ^ 0.5 * (1 - PR_Kij(i, j))
Next
' Calculation of Vapour Fug Coefficient, PhiL for individual component
phi_Vap(i) = Exp(bCrit(i) * (ZVap - 1) / bmixV - logE(ZVap - BVPR) - AVPR / 8 ^ 0.5 / (BVPR) * (2 * psi_Vap(i) / a_alpha_mixV - bCrit(i) / bmixV) * logE((ZVap + BVPR * (1 + 2 ^ 0.5)) / (ZVap + BVPR * (1 - 2 ^ 0.5))))
Cells(i, 18) = phiV
'Calculation of new K values
Keq_New(i) = phi_Liq(i) / phi_Vap(i)
fug_Liq(i) = xComp(i) * phi_Liq(i)
fug_Vap(i) = yComp(i) * phi_Vap(i)
'Calculation of summation of difference between new and old K values
EpsK = EpsK + (xComp(i) * phi_Liq(i) / (yComp(i) * phi_Vap(i)) - 1) ^ 2
Next
If (EpsK < 0.000000001) Then
Exit For
End If
Keq = Keq_New
Next iter02
pT_Flash_PR = VapFrac
MsgBox VapFrac
End Function
Sub Test()
comp = Range("g33:g35")
zComp = Range("h33:h35")
Temp = Range("h31") + 273.15
Pres = Range("h32")
VF = pT_Flash_PR(comp, zComp, Temp, Pres)
End Sub
VBA functions work differently if called from within excel cells. In your case (for example) all your parameters are read as Variant() arrays if the function is called from within the VB editor. However, if called from an excel cell they are accepted as Ranges and you therefore get an error when you do something like Lbound(someRange) or .Transpose(someRange). A way around is to check for the caller at the very beginning of your function like this:
'Check if called from an excel cell
If TypeName(Application.Caller) = "Range" Then
If TypeName(comp) = "Range" Then comp = comp.Value
If TypeName(zComp) = "Range" Then zComp = zComp.Value
If TypeName(Temp) = "Range" Then Temp = Temp.Value
If TypeName(Pres) = "Range" Then Pres = Pres.Value
End If
Here I am checking the type of each parameter again to allow for passing literal values.
I am trying to calculate velocity and acceleration, with the variable being the time t. I'd like to write these in vba, to make it faster, because later on I'll need velocity and acceleration for several other calculations combined with different logical conditions. I would like the results to be printed in the worksheet, to double check during calculation if the results are realistic.
example of how it should like more or less
t vel a
0.002 39 -777
0.004 38.6 -802
0.006 35 -500
0.008 33.4 -400
0.01 32.1 -12297.1
So I have tried a few different things based on comments:
This first code example works fine I think from the results, but I still can see any of the results in between > so main question: any chance I can change the commend to write to the worksheet without changing much else?
The second code example is an attempt to write everything into arrays: I do understand the principle I think, but here the main error seems to be that my variable t is not getting generated properly and therefore the formulas are not calculated: can't find the mistakes here and I would be grateful for some more help...
Kinematics Calculation
'Set t from 0 to 2 s with time step of 0.002s; Calculate Rot_vel until <= 0,
'Find index t_2 when Rot_vel <= 0
t = 0
Do
t = t + 0.002
Rot_vel = -10356# * t ^ 6 + 24130# * t ^ 5 - 19002# * t ^ 4 + 4933# * t ^ 3 +
362# * t ^ 2 - 213# * t + 39
Worksheets("test").Range(Cells(3, 2), Cells(1003, 2)) = Rot_vel
Loop Until Rot_vel <= 0
If Rot_vel <= 0 Then
t_2 = t
If t_2 > 0 Then
Debug.Print t_2
End If
End If
'Continue calculations for t 0 to t_2 with 0.002 steps
t = 0
Do
t = t + 0.002
A_rot = -62136# * t ^ 5 + 120650# * t ^ 4 - 76008# * t ^ 3 + 14797.8 * t
^ 2 + 723.26 * t - 212.7
Worksheets("test").Range(Cells(3, 3), Cells(1003, 3)).value = A_rot
L = MoI * Rot_vel / 1000
M = MoI * A_rot / 1000
Worksheets("test").Range(Cells(3, 8), Cells(1003, 8)).value = L
Worksheets("test").Range(Cells(3, 9), Cells(1003, 9)).value = M
G = L / t_2
Worksheets("test").Range(Cells(3, 10), Cells(1003, 10)).value = G
Loop Until t = t_2
Second version:
Kinematics Calculation
'Set t from 0 to 2 s with time step of 0.002s; Calculate Rot_vel until <= 0,
'Find index t_2 when Rot_vel <= 0
Dim arrCalc As Variant
Dim i As Integer
Dim j As Integer
ReDim arrCalc(i To 1003, j To 13)
For i = LBound(arrCalc, 2) To UBound(arrCalc, 2)
t = 0
Do
t = t + 0.002
arrCalc(i, 1) = t
arrCalc(i, 2) = -10356# * t ^ 6 + 24130# * t ^ 5 - 19002# * t ^ 4 + 4933#
* t ^ 3 + 362# * t ^ 2 - 213# * t + 39 'Rot_vel
Loop Until arrCalc(i, 2) < 0
Dim pos, val
val = 0
pos = Application.Match(val, arrCalc(i, 2), False)
pos = t_2
t = 0
Do
t = t + 0.002
arrCalc(i, 3) = -62136# * t ^ 5 + 120650# * t ^ 4 - 76008# * t ^ 3 +
14797.8 * t ^ 2 + 723.26 * t - 212.7
arrCalc(i, 8) = MoI * Rot_vel / 1000 'L
arrCalc(i, 9) = MoI * A_rot / 1000 'M
arrCalc(i, 10) = 1 / t_2 * L 'G
Loop Until t = t_2
Next i
With Worksheets("test")
.Cells(2, "A") = 0
.Cells(3, "A").Resize(UBound(arrCalc, 1), UBound(arrCalc, 2)) = Rot_vel
.Cells(2, "A").Resize(UBound(arrCalc, 1) + 1, 1) = t
'.Cells(3, "C").Resize(UBound(arrCalc, 1), UBound(arrCalc, 3)) = A_rot
End With
Your variables a_rot and rot_val don't look to me like arrays but normal variables. Therefore, only one value is stored in them and of course you get only one value as an output.
I see two options: 1) You write all of your values into an array and then copy the array to the sheet. 2) You write each calculation line by line to the sheet. Number 1) is much much faster.
A solution could look something like this:
ReDim Array (Lines, Columns)
For each line
Array (line, Columns1) = Formula1
Array (line, Columns2) = Formula2
Array (line, Columns3) = Formula3
Next
Build a 2-D array with times and calculations then dump the results back onto the worksheet.
Sequential time is very prone to 15 significant digit floating point errors. These errors can be minimized with a form of datum dimensioning that creates all new entries relative to the original starting point instead of the previous value. The former method can have no error greater than a single calculation while the latter can compounding errors by carrying them into the next calculation.
Sub kinematicsCalculation()
Dim t As Double, start As Double, i As Long, arr As Variant
start = TimeSerial(0, 0, 0) 'start at 0 second mark
ReDim arr(1 To 1000, 1 To 2)
For i = LBound(arr, 1) To UBound(arr, 1)
arr(i, 1) = start + TimeSerial(0, 0, 2 * i) / 1000
'cannot attempt velocity calc without Rot_vel_i
arr(i, 2) = "<velocity calculation here>"
Next i
With Worksheets("sheet")
.Cells(2, "B") = start
.Cells(3, "B").Resize(UBound(arr, 1), UBound(arr, 2)) = arr
.Cells(2, "B").Resize(UBound(arr, 1) + 1, 1).NumberFormat = "[hh]:mm:ss.000"
End With
End Sub
I'm trying to minimized the following problem:
for the production of laptops, phones and tablets, there are costs for inventory (1$ per item per month) and for overtime hours (10$ per hour). There is a demand scheme to be met, which functions as a constraint for the minimum number of gadgets in a specific month. Besides this there is a maximum of 20000 hours of production, plus 3000 overtime hours per month.
The problem is that the results python/pulp give me are (with one exception) all the upperbound values that are inserted in the LpVariables: not the minimized costs!
from pulp import *
# Define the LP problem: minimize costs
prob = LpProblem("Minimize costs of production and inventory", LpMinimize)
# Demand schemes
demand_laptops = [75, 125, 1000, 1500, 1000, 500, 1250, 1500, 1000, 500, 500, 400, 300] # Demand laptops
demand_phones = [120, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000] # Demand phones
demand_tablets = [50, 2000, 3000, 2000, 3000, 4000, 5000, 2000, 3000, 4000, 5000, 4000, 5000] # Demand tablets
# Defining variables: normal production hours and overtime production hours.
production_laptop = {x: LpVariable("Production hours for laptop in month {}".format(x), 0, 20000)
for x in range(1, 13)}
production_phone = {x: LpVariable("Production hours for phone in month {}".format(x), 0, 20000)
for x in range(1, 13)}
production_tablet = {x: LpVariable("Production hours for tablet in month {}".format(x), 0, 20000)
for x in range(1, 13)}
overtime_laptop = {x: LpVariable("Overtime hours for laptop in month {}".format(x), 0, 3000)
for x in range(1, 13)}
overtime_phone = {x: LpVariable("Overtime hours for phone in month {}".format(x), 0, 3000)
for x in range(1, 13)}
overtime_tablet = {x: LpVariable("Overtime hours for tablet in month {}".format(x), 0, 3000)
for x in range(1, 13)}
# defining a list of names for the inventory of products
inventory_laptops = {x: "inventory of laptops in month {}".format(x)
for x in range(1, 13)}
inventory_phones = {x: "inventory of phones in month {}".format(x)
for x in range(1, 13)}
inventory_tables = {x: "inventory of tablets in month {}".format(x)
for x in range(1, 13)}
# Inventory (to be minimized)
inventory_laptops[1] = demand_laptops[0] + (1 / 5) * production_laptop[1] + (1 / 5) * overtime_laptop[1] - demand_laptops[1]
inventory_laptops[2] = inventory_laptops[1] + (1 / 5) * production_laptop[2] + (1 / 5) * overtime_laptop[2] - demand_laptops[2]
inventory_laptops[3] = inventory_laptops[2] + (1 / 5) * production_laptop[3] + (1 / 5) * overtime_laptop[3] - demand_laptops[3]
inventory_laptops[4] = inventory_laptops[3] + (1 / 5) * production_laptop[4] + (1 / 5) * overtime_laptop[4] - demand_laptops[4]
inventory_laptops[5] = inventory_laptops[4] + (1 / 5) * production_laptop[5] + (1 / 5) * overtime_laptop[5] - demand_laptops[5]
inventory_laptops[6] = inventory_laptops[5] + (1 / 5) * production_laptop[6] + (1 / 5) * overtime_laptop[6] - demand_laptops[6]
inventory_laptops[7] = inventory_laptops[6] + (1 / 5) * production_laptop[7] + (1 / 5) * overtime_laptop[7] - demand_laptops[7]
inventory_laptops[8] = inventory_laptops[7] + (1 / 5) * production_laptop[8] + (1 / 5) * overtime_laptop[8] - demand_laptops[8]
inventory_laptops[9] = inventory_laptops[8] + (1 / 5) * production_laptop[9] + (1 / 5) * overtime_laptop[9] - demand_laptops[9]
inventory_laptops[10] = inventory_laptops[9] + (1 / 5) * production_laptop[10] + (1 / 5) * overtime_laptop[10] - demand_laptops[10]
inventory_laptops[11] = inventory_laptops[10] + (1 / 5) * production_laptop[11] + (1 / 5) * overtime_laptop[11] - demand_laptops[11]
inventory_laptops[12] = inventory_laptops[11] + (1 / 5) * production_laptop[12] + (1 / 5) * overtime_laptop[12] - demand_laptops[12]
inventory_phones[1] = demand_phones[0] + (1 / 2) * production_phone[1] + (1 / 2) * overtime_phone[1] - demand_phones[1]
inventory_phones[2] = inventory_phones[1] + (1 / 2) * production_phone[2] + (1 / 2) * overtime_phone[2] - demand_phones[2]
inventory_phones[3] = inventory_phones[2] + (1 / 2) * production_phone[3] + (1 / 2) * overtime_phone[3] - demand_phones[3]
inventory_phones[4] = inventory_phones[3] + (1 / 2) * production_phone[4] + (1 / 2) * overtime_phone[4] - demand_phones[4]
inventory_phones[5] = inventory_phones[4] + (1 / 2) * production_phone[5] + (1 / 2) * overtime_phone[5] - demand_phones[5]
inventory_phones[6] = inventory_phones[5] + (1 / 2) * production_phone[6] + (1 / 2) * overtime_phone[6] - demand_phones[6]
inventory_phones[7] = inventory_phones[6] + (1 / 2) * production_phone[7] + (1 / 2) * overtime_phone[7] - demand_phones[7]
inventory_phones[8] = inventory_phones[7] + (1 / 2) * production_phone[8] + (1 / 2) * overtime_phone[8] - demand_phones[8]
inventory_phones[9] = inventory_phones[8] + (1 / 2) * production_phone[9] + (1 / 2) * overtime_phone[9] - demand_phones[9]
inventory_phones[10] = inventory_phones[9] + (1 / 2) * production_phone[10] + (1 / 2) * overtime_phone[10] - demand_phones[10]
inventory_phones[11] = inventory_phones[10] + (1 / 2) * production_phone[11] + (1 / 2) * overtime_phone[11] - demand_phones[11]
inventory_phones[12] = inventory_phones[11] + (1 / 2) * production_phone[12] + (1 / 2) * overtime_phone[12] - demand_phones[12]
inventory_tables[1] = demand_tablets[0] + (1 / 4) * production_tablet[1] + (1 / 4) * overtime_tablet[1] - demand_tablets[1]
inventory_tables[2] = inventory_tables[1] + (1 / 4) * production_tablet[2] + (1 / 4) * overtime_tablet[2] - demand_tablets[2]
inventory_tables[3] = inventory_tables[2] + (1 / 4) * production_tablet[3] + (1 / 4) * overtime_tablet[3] - demand_tablets[3]
inventory_tables[4] = inventory_tables[3] + (1 / 4) * production_tablet[4] + (1 / 4) * overtime_tablet[4] - demand_tablets[4]
inventory_tables[5] = inventory_tables[4] + (1 / 4) * production_tablet[5] + (1 / 4) * overtime_tablet[5] - demand_tablets[5]
inventory_tables[6] = inventory_tables[5] + (1 / 4) * production_tablet[6] + (1 / 4) * overtime_tablet[6] - demand_tablets[6]
inventory_tables[7] = inventory_tables[6] + (1 / 4) * production_tablet[7] + (1 / 4) * overtime_tablet[7] - demand_tablets[7]
inventory_tables[8] = inventory_tables[7] + (1 / 4) * production_tablet[8] + (1 / 4) * overtime_tablet[8] - demand_tablets[8]
inventory_tables[9] = inventory_tables[8] + (1 / 4) * production_tablet[9] + (1 / 4) * overtime_tablet[9] - demand_tablets[9]
inventory_tables[10] = inventory_tables[9] + (1 / 4) * production_tablet[10] + (1 / 4) * overtime_tablet[10] - demand_tablets[10]
inventory_tables[11] = inventory_tables[10] + (1 / 4) * production_tablet[11] + (1 / 4) * overtime_tablet[11] - demand_tablets[11]
inventory_tables[12] = inventory_tables[11] + (1 / 4) * production_tablet[12] + (1 / 4) * overtime_tablet[12] - demand_tablets[12]
# Constraints to meet demand scheme
prob += demand_laptops[0] + (1 / 5) * production_laptop[1] + (1 / 5) * overtime_laptop[1] >= demand_laptops[1]
prob += inventory_laptops[1] + (1 / 5) * production_laptop[2] + (1 / 5) * overtime_laptop[2] >= demand_laptops[2]
prob += inventory_laptops[2] + (1 / 5) * production_laptop[3] + (1 / 5) * overtime_laptop[3] >= demand_laptops[3]
prob += inventory_laptops[3] + (1 / 5) * production_laptop[4] + (1 / 5) * overtime_laptop[4] >= demand_laptops[4]
prob += inventory_laptops[4] + (1 / 5) * production_laptop[5] + (1 / 5) * overtime_laptop[5] >= demand_laptops[5]
prob += inventory_laptops[5] + (1 / 5) * production_laptop[6] + (1 / 5) * overtime_laptop[6] >= demand_laptops[6]
prob += inventory_laptops[6] + (1 / 5) * production_laptop[7] + (1 / 5) * overtime_laptop[7] >= demand_laptops[7]
prob += inventory_laptops[7] + (1 / 5) * production_laptop[8] + (1 / 5) * overtime_laptop[8] >= demand_laptops[8]
prob += inventory_laptops[8] + (1 / 5) * production_laptop[9] + (1 / 5) * overtime_laptop[9] >= demand_laptops[9]
prob += inventory_laptops[9] + (1 / 5) * production_laptop[10] + (1 / 5) * overtime_laptop[10] >= demand_laptops[10]
prob += inventory_laptops[10] + (1 / 5) * production_laptop[11] + (1 / 5) * overtime_laptop[11] >= demand_laptops[11]
prob += inventory_laptops[11] + (1 / 5) * production_laptop[12] + (1 / 5) * overtime_laptop[12] >= demand_laptops[12]
prob += demand_phones[0] + (1 / 2) * production_phone[1] + (1 / 2) * overtime_phone[1] >= demand_phones[1]
prob += inventory_phones[1] + (1 / 2) * production_phone[2] + (1 / 2) * overtime_phone[2] >= demand_phones[2]
prob += inventory_phones[2] + (1 / 2) * production_phone[3] + (1 / 2) * overtime_phone[3] >= demand_phones[3]
prob += inventory_phones[3] + (1 / 2) * production_phone[4] + (1 / 2) * overtime_phone[4] >= demand_phones[4]
prob += inventory_phones[4] + (1 / 2) * production_phone[5] + (1 / 2) * overtime_phone[5] >= demand_phones[5]
prob += inventory_phones[5] + (1 / 2) * production_phone[6] + (1 / 2) * overtime_phone[6] >= demand_phones[6]
prob += inventory_phones[6] + (1 / 2) * production_phone[7] + (1 / 2) * overtime_phone[7] >= demand_phones[7]
prob += inventory_phones[7] + (1 / 2) * production_phone[8] + (1 / 2) * overtime_phone[8] >= demand_phones[8]
prob += inventory_phones[8] + (1 / 2) * production_phone[9] + (1 / 2) * overtime_phone[9] >= demand_phones[9]
prob += inventory_phones[9] + (1 / 2) * production_phone[10] + (1 / 2) * overtime_phone[10] >= demand_phones[10]
prob += inventory_phones[10] + (1 / 2) * production_phone[11] + (1 / 2) * overtime_phone[11] >= demand_phones[11]
prob += inventory_phones[11] + (1 / 2) * production_phone[12] + (1 / 2) * overtime_phone[12] >= demand_phones[12]
prob += demand_tablets[0] + (1 / 4) * production_tablet[1] + (1 / 4) * overtime_tablet[1] >= demand_tablets[1]
prob += inventory_phones[1] + (1 / 4) * production_tablet[2] + (1 / 4) * overtime_tablet[2] >= demand_tablets[2]
prob += inventory_phones[2] + (1 / 4) * production_tablet[3] + (1 / 4) * overtime_tablet[3] >= demand_tablets[3]
prob += inventory_phones[3] + (1 / 4) * production_tablet[4] + (1 / 4) * overtime_tablet[4] >= demand_tablets[4]
prob += inventory_phones[4] + (1 / 4) * production_tablet[5] + (1 / 4) * overtime_tablet[5] >= demand_tablets[5]
prob += inventory_phones[5] + (1 / 4) * production_tablet[6] + (1 / 4) * overtime_tablet[6] >= demand_tablets[6]
prob += inventory_phones[6] + (1 / 4) * production_tablet[7] + (1 / 4) * overtime_tablet[7] >= demand_tablets[7]
prob += inventory_phones[7] + (1 / 4) * production_tablet[8] + (1 / 4) * overtime_tablet[8] >= demand_tablets[8]
prob += inventory_phones[8] + (1 / 4) * production_tablet[9] + (1 / 4) * overtime_tablet[9] >= demand_tablets[9]
prob += inventory_phones[9] + (1 / 4) * production_tablet[10] + (1 / 4) * overtime_tablet[10] >= demand_tablets[10]
prob += inventory_phones[10] + (1 / 4) * production_tablet[11] + (1 / 4) * overtime_tablet[11] >= demand_tablets[11]
prob += inventory_phones[11] + (1 / 4) * production_tablet[12] + (1 / 4) * overtime_tablet[12] >= demand_tablets[12]
# Objective function: inventory costs and overtime costs (10 per hour)
prob += sum(inventory_laptops) + sum(inventory_phones) + sum(inventory_tables) + (10 * (sum(overtime_laptop) + sum(overtime_phone) + sum(overtime_tablet)))
# Solve the problem
prob.solve()
print("Status:", LpStatus[prob.status])
for v in prob.variables():
print(v.name, "=", v.varValue)
print("total costs:", value(prob.objective))
This gives me the following results:
Status: Optimal
Overtime_hours_for_laptop_in_month_1 = 3000.0
Overtime_hours_for_laptop_in_month_10 = 3000.0
Overtime_hours_for_laptop_in_month_11 = 3000.0
Overtime_hours_for_laptop_in_month_12 = 3000.0
Overtime_hours_for_laptop_in_month_2 = 3000.0
Overtime_hours_for_laptop_in_month_3 = 3000.0
Overtime_hours_for_laptop_in_month_4 = 3000.0
Overtime_hours_for_laptop_in_month_5 = 3000.0
Overtime_hours_for_laptop_in_month_6 = 3000.0
Overtime_hours_for_laptop_in_month_7 = 3000.0
Overtime_hours_for_laptop_in_month_8 = 3000.0
Overtime_hours_for_laptop_in_month_9 = 3000.0
Overtime_hours_for_phone_in_month_1 = 3000.0
Overtime_hours_for_phone_in_month_10 = 3000.0
Overtime_hours_for_phone_in_month_11 = 3000.0
Overtime_hours_for_phone_in_month_12 = 3000.0
Overtime_hours_for_phone_in_month_2 = 3000.0
Overtime_hours_for_phone_in_month_3 = 3000.0
Overtime_hours_for_phone_in_month_4 = 3000.0
Overtime_hours_for_phone_in_month_5 = 3000.0
Overtime_hours_for_phone_in_month_6 = 3000.0
Overtime_hours_for_phone_in_month_7 = 3000.0
Overtime_hours_for_phone_in_month_8 = 3000.0
Overtime_hours_for_phone_in_month_9 = 3000.0
Overtime_hours_for_tablet_in_month_1 = 0.0
Overtime_hours_for_tablet_in_month_10 = 3000.0
Overtime_hours_for_tablet_in_month_11 = 3000.0
Overtime_hours_for_tablet_in_month_12 = 3000.0
Overtime_hours_for_tablet_in_month_2 = 3000.0
Overtime_hours_for_tablet_in_month_3 = 3000.0
Overtime_hours_for_tablet_in_month_4 = 3000.0
Overtime_hours_for_tablet_in_month_5 = 3000.0
Overtime_hours_for_tablet_in_month_6 = 3000.0
Overtime_hours_for_tablet_in_month_7 = 3000.0
Overtime_hours_for_tablet_in_month_8 = 3000.0
Overtime_hours_for_tablet_in_month_9 = 3000.0
Production_hours_for_laptop_in_month_1 = 20000.0
Production_hours_for_laptop_in_month_10 = 20000.0
Production_hours_for_laptop_in_month_11 = 20000.0
Production_hours_for_laptop_in_month_12 = 20000.0
Production_hours_for_laptop_in_month_2 = 20000.0
Production_hours_for_laptop_in_month_3 = 20000.0
Production_hours_for_laptop_in_month_4 = 20000.0
Production_hours_for_laptop_in_month_5 = 20000.0
Production_hours_for_laptop_in_month_6 = 20000.0
Production_hours_for_laptop_in_month_7 = 20000.0
Production_hours_for_laptop_in_month_8 = 20000.0
Production_hours_for_laptop_in_month_9 = 20000.0
Production_hours_for_phone_in_month_1 = 20000.0
Production_hours_for_phone_in_month_10 = 20000.0
Production_hours_for_phone_in_month_11 = 20000.0
Production_hours_for_phone_in_month_12 = 20000.0
Production_hours_for_phone_in_month_2 = 20000.0
Production_hours_for_phone_in_month_3 = 20000.0
Production_hours_for_phone_in_month_4 = 20000.0
Production_hours_for_phone_in_month_5 = 20000.0
Production_hours_for_phone_in_month_6 = 20000.0
Production_hours_for_phone_in_month_7 = 20000.0
Production_hours_for_phone_in_month_8 = 20000.0
Production_hours_for_phone_in_month_9 = 20000.0
Production_hours_for_tablet_in_month_1 = 7800.0
Production_hours_for_tablet_in_month_10 = 20000.0
Production_hours_for_tablet_in_month_11 = 20000.0
Production_hours_for_tablet_in_month_12 = 20000.0
Production_hours_for_tablet_in_month_2 = 20000.0
Production_hours_for_tablet_in_month_3 = 20000.0
Production_hours_for_tablet_in_month_4 = 20000.0
Production_hours_for_tablet_in_month_5 = 20000.0
Production_hours_for_tablet_in_month_6 = 20000.0
Production_hours_for_tablet_in_month_7 = 20000.0
Production_hours_for_tablet_in_month_8 = 20000.0
Production_hours_for_tablet_in_month_9 = 20000.0
__dummy = None
total costs: None
Can somebody tell me what I am doing wrong?
There are a few syntactic glitches that result in the solver getting a different model than what you would expect. In addition, I would like to point some syntactic suggestions as well.
What is wrong?
First, the production variables are defined as dictionaries, whose values are LpVariables. I would suggest that you use the pulp data structure designed for this job, LpVariable.dicts. The definition would then be like this:
production_laptop = LpVariable.dicts(
"Production hours for laptop in month ", range(1, 13),
lowBound = 0, upBound = 20000)
Note that I explicitly denote lower and upper bounds: this is a good habit, which helps a lot if you have to reuse the code after a few months (or even weeks).
Later on, you define dictionaries that represent variable names: the inventory_ variables. Then, you redefine the values of these dictionaries to point to a combination of variables and constraints. I would define these variables as the previous ones:
inventory_laptops = LpVariable.dicts(
"inventory of laptops in month ", range(1,13), lowBound = 0)
If there exist relationships among the variables, you can represent them in the constraints, so we do not need to worry about this during the variable definition stage.
Note that after the variable are defined, the objective function should be defined. Its definition is not correct, because instead of sum(inventory_laptops) one has to sum(inventory_laptops[i] for i in range(1,13)), otherwise we try to sum over a dictionary, and in particular over the keys of the dictionary.
Syntax
DRY: You write a lot of repetitive code, which could easily fit into loops. Try to not repeat yourself, because the code is unnecessarily lengthy and complex and prone to errors. In fact, there is an error in your code which might be because of copy-pasting code you already had: the last block of constraints mixes phones and tablets, like this:
prob += inventory_phones[1] + (1 / 4) * production_tablet[2] + (1 / 4) * overtime_tablet[2] >= demand_tablets[2]
probably because you were copy pasting and replacing phones with tablets. You can easily write these lines as
for i in range(1,13):
prob += (demand_tablets[i - 1] if i == 1 else inventory_tables[i - 1]) + \
(1 / 4) * production_tablet[i] + (1 / 4) * overtime_tablet[i] >= \
demand_tablets[i], "demand tablets {}".format(i)
which also has the added benefit that you give your constraint a name, so that you can trace it later if you want to.
Comments: Try to use helpful comments. These are the comments that describe what you intent, not what you actually do. Why comment # Demand laptops when your variable is called demand_laptops?
Consistency: This is a big one. It takes a bit of time to work with your code, mostly because variable names are not consistent: demand_tablets, inventory_tables, overtime_tablet and production_tablet are all very similar and easy to confuse. Try to stick to a more consistent notation.
Line length: Although not necessary, Python developers do not use arbitrary line lengths. Try to use a good IDE (I use Pycharm, sometimes Sublime Text), which is going to steer you to use the regular Python conventions (also in naming variables, functions, etc). This makes the code looking more like Python.
Debugging Math Optimization Models: A very useful habit for small problems, is to print out the formulation you are passing to the solver. This can help you catch many bugs and issues. It would be apparent for example, that you have defined a variable called _dummy, which was done accidentally. This is done with prob.writeLP("DebugThis.lp"). I would also use shorter length variables, so that I can understand what is going on in the constraints and objective function. As a final word, try to avoid hard numbers in your models. The inventory cost might be 10 for the instance you have now, but in the future this might change (it will change in a non-assignment environment). Thus, it is better to define a list of inventory costs (one for each product, and/or period) and update that list only. This is helpful in case you want to add more products, as the constraints and variables will be generated automatically.
Revised Code
I implemented a working version of your code hereby. To keep it close to the original, so that it is easier to understand (for yourself) I did not implement everything I suggest. As a further improvement on my lines below, try to have a list of products and generate the product-month pairs automatically. There are a few ways to do this, and perhaps this example will help.
from pulp import * # I would not import the entire workspace - you might end up shadowing variables names that you need
# Problem formulation starts here
prob = LpProblem("Minimize costs of production and inventory", LpMinimize)
# Problem data: Product Demands
demand_laptops = [75, 125, 1000, 1500, 1000, 500, 1250, 1500, 1000, 500, 500, 400, 300]
demand_phones = [120, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000]
demand_tablets = [50, 2000, 3000, 2000, 3000, 4000, 5000, 2000, 3000, 4000, 5000, 4000, 5000]
# Defining variables: normal production, overtime, and inventories
production_laptop = LpVariable.dicts(
"Production hours for laptop in month ", range(1, 13),
lowBound = 0, upBound = 20000)
production_phone = LpVariable.dicts(
"Production hours for phone in month ", range(1, 13),
lowBound = 0, upBound = 20000)
production_tablet = LpVariable.dicts(
"Production hours for tablet in month ", range(1, 13),
lowBound = 0, upBound = 20000)
overtime_laptop = LpVariable.dicts(
"Overtime hours for laptop in month ", range(1, 13),
lowBound = 0, upBound = 3000)
overtime_phone = LpVariable.dicts(
"Overtime hours for phone in month ", range(1, 13),
lowBound = 0, upBound = 3000)
overtime_tablet = LpVariable.dicts(
"Overtime hours for tablet in month ", range(1, 13),
lowBound = 0, upBound = 3000)
inventory_laptops = LpVariable.dicts(
"inventory of laptops in month ", range(1,13), lowBound = 0)
inventory_phones = LpVariable.dicts(
"inventory of phones in month ", range(1,13), lowBound = 0)
inventory_tables = LpVariable.dicts(
"inventory of tables in month ", range(1,13), lowBound = 0)
# Objective function: inventory costs and overtime costs
prob += (sum(
inventory_laptops[i] + inventory_phones[i] + inventory_tables[i]
for i in range(1,13)) + \
10 * sum(overtime_laptop[i] + overtime_phone[i] + overtime_tablet[i]
for i in range(1,13)))
# Constraint definition part
for i in range(1,13):
# Inventory definition for laptops, phones and tablets
prob += inventory_laptops[i] == (demand_laptops[i - 1] if i == 1 else \
inventory_laptops[i - 1]) + \
(1 / 5) * production_laptop[i] + (1 / 5) * overtime_laptop[i] - \
demand_laptops[i], "inventory_laptops definition {}".format(i)
prob += inventory_phones[i] == (demand_phones[i - 1] if i == 1 else \
inventory_phones[i - 1]) + \
(1 / 2) * production_phone[i] + (1 / 2) * overtime_phone[i] - \
demand_phones[i], "inventory_phones definition {}".format(i)
prob += inventory_tables[i] == (demand_tablets[i - 1] if i == 1 else \
inventory_tables[i - 1]) + \
(1 / 4) * production_tablet[i] + (1 / 4) * overtime_tablet[i] - \
demand_tablets[i], "inventory_tables definition {}".format(i)
# Demand-covering constraints for laptops, phones, tablets
prob += (demand_laptops[i - 1] if i == 1 else inventory_laptops[i - 1]) + \
(1 / 5) * production_laptop[i] + (1 / 5) * overtime_laptop[i] >= \
demand_laptops[i], "demand laptops {}".format(i)
prob += (demand_phones[i - 1] if i == 1 else inventory_phones[i - 1]) + \
(1 / 2) * production_phone[i] + (1 / 2) * overtime_phone[i] >= \
demand_phones[i], "demand phones {}".format(i)
prob += (demand_tablets[i - 1] if i == 1 else inventory_tables[i - 1]) + \
(1 / 4) * production_tablet[i] + (1 / 4) * overtime_tablet[i] >= \
demand_tablets[i], "demand tablets {}".format(i)
# Solve the problem
prob.solve()
# Take a look at what was solved
prob.writeLP("SO40113557.lp")
print("Status:", LpStatus[prob.status])
for v in prob.variables():
print(v.name, "=", v.varValue)
print("total costs:", value(prob.objective))
Output (without printing out the variable names/values):
('Status:', 'Optimal')
('total costs:', 0.0)
I hope this helps!