A fruit processing company, NatureBounty, specializes in producing two product lines: packaged fresh apples and bottled apple juice. NatureBounty grades apples on a scale of 1 (poor) to 10 (excellent). The company currently has 90,000 lbs of grade 7 apples and 110,000 lbs of grade 4 apples in inventory. To maintain product quality, the average grade of apples sold in packages must be at least 5, and the average grade of apples used for juice production must be at least 6. Additionally, the company has a production capacity constraint: it can process no more than 150,000 lbs of apples in total due to equipment limitations. Each pound of apples used for juice generates a revenue of $1.10 and incurs a variable cost of $0.80. Each pound of apples sold in packages generates a revenue of $0.35 and incurs a variable cost of $0.12. The company also has a marketing constraint: at least 20% of the total processed apples must be sold in packages to meet market demand. How to help NatureBounty maximize its profit while adhering to all constraints.

Problem type: LP (Toy: 4 variables, 6 constraints, 16 nonzeros)

Mathematical model:
Set:
1. Product
The set of products, \( P = \{1, 2\} \)  # 1: packaged fresh apples, 2: bottled apple juice
2. Grade
The set of apple grades the company has, \( G = \{7, 4\} \)

Parameter:
1. Inventory
[90000, 110000]
2. Revenue
[0.35, 1.10]
3. VariableCost
[0.12, 0.80]
4. AverageGrade
[5, 6]
5. MaxProcessingCapacity
150000
6. MinPackagePercentage
0.20

Decision variable:
1. ProcessedAmount
Continuous variable, \( ProcessedAmount[p][g] \forall p \in P, g \in G \), where \( ProcessedAmount[p][g] \) is the amount of apples of grade \( g \) used for product \( p \).

Objective:
1. Maximize the profit from selling apples.
max: \sum_{p \in P} \sum_{g \in G} (Revenue[p] - VariableCost[p]) * ProcessedAmount[p][g]

Constraint:
1. Inventory constraint.
\sum_{p \in P} ProcessedAmount[p][g] \leq Inventory[g] \forall g \in G
2. Average grade constraint.
AverageGrade[p] * \sum_{g \in G} ProcessedAmount[p][g] \leq \sum_{g \in G} g * ProcessedAmount[p][g] \forall p \in P
3. Processing capacity constraint.
\sum_{p \in P} \sum_{g \in G} ProcessedAmount[p][g] \leq MaxProcessingCapacity
4. Marketing constraint.
\sum_{g \in G} ProcessedAmount[1][g] \geq MinPackagePercentage * \sum_{p \in P} \sum_{g \in G} ProcessedAmount[p][g]
5. Non-negativity constraint.
ProcessedAmount[p][g] \geq 0 \forall p \in P, g \in G

Type:
Continuous, Linear, Linear
LP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_apple_processing_optimization(
    Inventory=[90000, 110000],
    Revenue=[0.35, 1.10],
    VariableCost=[0.12, 0.80],
    AverageGrade=[5, 6],
    MaxProcessingCapacity=150000,
    MinPackagePercentage=0.20,
    Grades=[7, 4]
):
    # Create a new model
    model = gp.Model("Apple Processing Optimization")

    # Sets
    Products = range(len(Revenue))

    # Profit per unit for each product
    Profit = [Revenue[p] - VariableCost[p] for p in Products]

    # Create decision variables dictionary
    ProcessedAmount = {}
    for p in Products:
        for g_idx, g in enumerate(Grades):
            ProcessedAmount[p, g_idx] = model.addVar(
                vtype=GRB.CONTINUOUS,
                name=f"ProcessedAmount_{p+1}_{g}"
            )

    # Objective: Maximize profit
    obj = gp.quicksum(
        Profit[p] * ProcessedAmount[p, g_idx]
        for p in Products
        for g_idx in range(len(Grades))
    )
    model.setObjective(obj, GRB.MAXIMIZE)

    # Constraint 1: Inventory constraint
    for g_idx in range(len(Grades)):
        model.addConstr(
            gp.quicksum(ProcessedAmount[p, g_idx] for p in Products) <= Inventory[g_idx],
            f"Inventory_{Grades[g_idx]}"
        )

    # Constraint 2: Average grade constraint
    for p in Products:
        model.addConstr(
            AverageGrade[p] * gp.quicksum(ProcessedAmount[p, g_idx] for g_idx in range(len(Grades))) <=
            gp.quicksum(Grades[g_idx] * ProcessedAmount[p, g_idx] for g_idx in range(len(Grades))),
            f"AverageGrade_{p+1}"
        )

    # Constraint 3: Processing capacity constraint
    model.addConstr(
        gp.quicksum(ProcessedAmount[p, g_idx] for p in Products for g_idx in range(len(Grades))) <= MaxProcessingCapacity,
        "ProcessingCapacity"
    )

    # Constraint 4: Marketing constraint
    model.addConstr(
        gp.quicksum(ProcessedAmount[0, g_idx] for g_idx in range(len(Grades))) >=
        MinPackagePercentage * gp.quicksum(ProcessedAmount[p, g_idx]
                                           for p in Products
                                           for g_idx in range(len(Grades))),
        "Marketing"
    )

    # Optimize the model
    model.optimize()

    # Return results
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.objVal}
    else:
        return {"status": f"{model.status}"}


if __name__ == "__main__":
    result = solve_apple_processing_optimization()
    print(result)

Solution status: OPTIMAL
Optimal objective value: 42900