pythonlinear-programmingpulpobjective-function

PuLP: Objective Function: Adding multiple lpSum in a loop


I am trying to use PuLp for a Blending problem with different Elements (Iron, Mercury..). But instead of max/min some profit/cost I need to maximize the utilization of my constraints. So in Excel I had something like this (in pseudocode):

max Sum (for each Element: (Sumproduct([DecisionVariables] * [Values]) / [MaximumAllowedValueForThisElement]))

I never used an objective function like this but it seems to work in Excel.

Now I want to model the same problem in PuLP. I think what I would need is somethink like this:

for Element in ELEMENTS:
    prob += lpSum(DecisionVariable[Concentrate]*dic[Element][Concentrate]/ MaxAmount[Element] for Concentrate in CONCENTRATES)

Where ELEMENTS is a List containing all Elements, CONCENTRATES is a List of Values from 0 to 100 and dic[Element][Concentrate] stores the values from each Element and all its concentrates.

Now with the code above, the objective function is overwritten in each loop. Instead of overwriting the old objective function I'd need something like append() or alike to add each of the loops=lpSums to my prob variable?class?

I am rather new to programming in general and I guess my problem is more related to my lack of python programming skills than my (also lacking :D) PuLP skills. But I could not find anything in the PuLP documentation, atleast nothing I could connect it to.

Edit: Included a small table to showcase the problem:

+------------------------------+-------------------------------------------+----+------------------------------+---------------+----------------------+---------------+---------------+-------------------------------+
|       Utilization [%]        |       Sumproduct[Quantity] = [LHS]        |    | Constrains[Quantity] = [RHS] |  Concentrate  |    Element 1 [%]     | Element 2 [%] | Element 3 [%] | Decision Variables [Quantity] |
+------------------------------+-------------------------------------------+----+------------------------------+---------------+----------------------+---------------+---------------+-------------------------------+
| u1 = z1 / MaxAmount Element1 | z1 = Col Element1 * Col Decison Variables | <= | MaxAmount Element1           | Concentrate 1 | % Element 1 in Con 1 |               |               | X1                            |
| u2 = z2 / MaxAmount Element2 | z2 = Col Element2 * Col Decison Variables | <= | MaxAmount Elemen2            | Concentrate 2 | % Element 1 in Con 2 |               |               | X2                            |
| u3 = z3 / MaxAmount Element3 | z3 = Col Element3 * Col Decison Variables | <= | MaxAmount Elemen3            | Concentrate 3 | % Element 1 in Con 3 |               |               | X3                            |
+------------------------------+-------------------------------------------+----+------------------------------+---------------+----------------------+---------------+---------------+-------------------------------+

The columns "Element 2" and "Element 3" store the same information as the column "Element 1": the % share of the respective Element in Concentrate 1/2/3.

The objective function is to maximize the sum over all utilizations (u1+u2+u3). So I am trying to determine how much of each concentrate I should use, to utilize as much of the given constraints for each element, as possible. Coming back to my PuLp code, I think I am able to add the equivalent of "u1" to my PuLp "LpProblem Class", but I can't figure out how to add multiple of these LpSums to my "LpProblem Class" in a loop.


Solution

  • Here' one version, with dummy data for illustrative purposes. See if this helps you.

    import pulp
    from pulp import *
    
    ELEMENTS = ['Iron', 'Mercury', 'Silver']
    
    
    Max_Per_Elem = {'Iron': 35, 
             'Mercury': 17, 
             'Silver': 28
                   }
    
    # A dictionary of the Iron percent in each of the CONCs
    IronPercent = {'CONC_1': 20, 'CONC_2': 10, 'CONC_3': 25}
    
    # A dictionary of the Hg percent in each of the CONCs
    MercPercent = {'CONC_1': 15, 'CONC_2': 18, 'CONC_3': 12}
    
    # A dictionary of the Silver percent in each of the CONCs
    SilverPercent = {'CONC_1': 30,  'CONC_2': 40, 'CONC_3': 20}
    
    CONCENTRATE_DIC = {'Iron': IronPercent,
                  'Mercury': MercPercent,
                  'Silver': SilverPercent              
                  }
    
    # Creates a list of Decision Variables
    concs = ['CONC_1', 'CONC_2', 'CONC_3']
    

    Now, we are ready to call puLP functions.

    conc_vars = LpVariable.dicts("Util", concs, 0, 1.0)
    
    # Create the 'prob' variable to contain the problem data
    prob = LpProblem("Elements Concentration Problem", LpMaximize)
    
    # The objective function
    prob += lpSum([conc_vars[i] for i in concs]), "Total Utilization is maximized"
    
    for elem in ELEMENTS:
        prob += lpSum([CONCENTRATE_DIC[elem][i]/Max_Per_Elem[elem] * conc_vars[i] for i in concs]) <= Max_Per_Elem[elem]/100, elem+"Percent"
    

    To verify, you can print the prob to see what it looks like:

    Elements Concentration Problem:
    MAXIMIZE
    1*Util_CONC_1 + 1*Util_CONC_2 + 1*Util_CONC_3 + 0
    SUBJECT TO
    IronPercent: 0.571428571429 Util_CONC_1 + 0.285714285714 Util_CONC_2
     + 0.714285714286 Util_CONC_3 <= 0.35
    
    MercuryPercent: 0.882352941176 Util_CONC_1 + 1.05882352941 Util_CONC_2
     + 0.705882352941 Util_CONC_3 <= 0.17
    
    SilverPercent: 1.07142857143 Util_CONC_1 + 1.42857142857 Util_CONC_2
     + 0.714285714286 Util_CONC_3 <= 0.28
    
    VARIABLES
    Util_CONC_1 <= 1 Continuous
    Util_CONC_2 <= 1 Continuous
    Util_CONC_3 <= 1 Continuous
    

    Once you are happy with the formulation, solve the problem.

    prob.writeLP("ElemUtiliztionModel.lp")
    prob.solve()
    print("Status:", LpStatus[prob.status])
    for v in prob.variables():
        print(v.name, "=", v.varValue)
    

    To get,

    Status: Optimal
    Util_CONC_1 = 0.0
    Util_CONC_2 = 0.0
    Util_CONC_3 = 0.24083333
    

    Hope that helps you move forward.