AutoParts Ltd. manufactures three types of automotive components: Engine Block, Transmission, and Brake System. The company sources raw materials at a cost of $60 per unit. Each unit of raw material can be processed into 7 units of Engine Block and 3 units of Transmission. Processing one unit of raw material requires $8 in energy costs and 7 hours of machine time.  

Engine Block can be used in three ways:  
- Sold directly for $25 per unit.  
- Processed into 1 unit of Transmission, requiring 4 hours of machine time and $5 in additional costs.  
- Processed into 1 unit of Brake System, requiring 6 hours of machine time and $10 in additional costs.  

Transmission can be used in two ways:  
- Sold directly for $35 per unit.  
- Processed into 1 unit of Brake System, requiring 5 hours of machine time and $12 in additional costs.  

Brake System is sold for $45 per unit. The company has a maximum machine time capacity of 60,000 hours per year. Additionally, there is a contractual obligation to produce at least 4,000 units of Brake System annually. The maximum market demand for each component is as follows:  
- Engine Block: 20,000 units  
- Transmission: 15,000 units  
- Brake System: 12,000 units  

Determine how AutoParts Ltd. can maximize its annual profit while meeting all constraints.

Problem type: ILP (Small: 10 variables, 11 constraints, 26 nonzeros)

Mathematical model:
Set:
1. ComponentType
The set of component types, \( C = \{1, 2, 3\} \)  # 1: Engine Block, 2: Transmission, 3: Brake System

Parameter:
1. RawMaterialCost
60
2. ComponentPerRawMaterial
[7, 3, 0]
3. EnergyCostPerRawMaterial
8
4. MachineTimePerRawMaterial
7
5. SellingPrice
[25, 35, 45]
6. Engine2TransmissionCost
5
7. Engine2TransmissionMachineTime
4
8. Engine2BrakeSystemCost
10
9. Engine2BrakeSystemMachineTime
6
10. Transmission2BrakeSystemCost
12
11. Transmission2BrakeSystemMachineTime
5
12. MaxMachineTime
60000
13. MinBrakeSystemProduction
4000
14. MaxDemand
[20000, 15000, 12000]

Decision variable:
1. RawMaterialPurchased
Integer variable, \( RawMaterialPurchased \), the amount of raw material purchased in units.
2. ComponentProducedfromRawMaterial
Integer variable, \( ComponentProducedfromRawMaterial[c] \forall c \in C \), the amount of component \( c \) produced from raw material in units.
3. Engine2TransmissionProduced
Integer variable, \( Engine2TransmissionProduced \), the amount of Engine Block processed into Transmission in units.
4. Engine2BrakeSystemProduced
Integer variable, \( Engine2BrakeSystemProduced \), the amount of Engine Block processed into Brake System in units.
5. Transmission2BrakeSystemProduced
Integer variable, \( Transmission2BrakeSystemProduced \), the amount of Transmission processed into Brake System in units.
6. ComponentSold
Integer variable, \( ComponentSold[c] \forall c \in C \), the amount of component \( c \) sold in units.

Objective:
1. Maximize the total profit.
max: \sum_{c \in C} SellingPrice[c] * ComponentSold[c] - RawMaterialCost * RawMaterialPurchased - Engine2TransmissionCost * Engine2TransmissionProduced - Engine2BrakeSystemCost * Engine2BrakeSystemProduced - Transmission2BrakeSystemCost * Transmission2BrakeSystemProduced - EnergyCostPerRawMaterial * RawMaterialPurchased

Constraint:
1. Machine time constraint.
MachineTimePerRawMaterial * RawMaterialPurchased + Engine2TransmissionMachineTime * Engine2TransmissionProduced + Engine2BrakeSystemMachineTime * Engine2BrakeSystemProduced + Transmission2BrakeSystemMachineTime * Transmission2BrakeSystemProduced \leq MaxMachineTime
2. Minimum Brake System production constraint.
Engine2BrakeSystemProduced + Transmission2BrakeSystemProduced \geq MinBrakeSystemProduction
3. Demand constraint.
ComponentSold[c] \leq MaxDemand[c] \forall c \in C
4. Production constraint.
ComponentProducedfromRawMaterial[c] == ComponentPerRawMaterial[c] * RawMaterialPurchased \forall c \in C
5. Production constraint - Engine Block.
ComponentSold[0] == ComponentProducedfromRawMaterial[0] -  Engine2TransmissionProduced - Engine2BrakeSystemProduced
6. Production constraint - Transmission.
ComponentSold[1] == ComponentProducedfromRawMaterial[0] + Engine2TransmissionProduced - Transmission2BrakeSystemProduced
7. Production constraint - Brake System.
ComponentSold[2] == ComponentProducedfromRawMaterial[0] + Engine2BrakeSystemProduced + Transmission2BrakeSystemProduced
8. Non-negativity constraint.
ComponentProducedfromRawMaterial[c] \geq 0 \forall c \in C
ComponentSold[c] \geq 0 \forall c \in C
RawMaterialPurchased \geq 0
Engine2TransmissionProduced \geq 0
Engine2BrakeSystemProduced \geq 0
Transmission2BrakeSystemProduced \geq 0

Type:
Integer, Linear, Linear
ILP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_component_manufacturing_optimization(
    RawMaterialCost=60,
    ComponentPerRawMaterial=[7, 3, 0],
    EnergyCostPerRawMaterial=8,
    MachineTimePerRawMaterial=7,
    SellingPrice=[25, 35, 45],
    Engine2TransmissionCost=5,
    Engine2TransmissionMachineTime=4,
    Engine2BrakeSystemCost=10,
    Engine2BrakeSystemMachineTime=6,
    Transmission2BrakeSystemCost=12,
    Transmission2BrakeSystemMachineTime=5,
    MaxMachineTime=60000,
    MinBrakeSystemProduction=4000,
    MaxDemand=[20000, 15000, 12000]
):
    """
    Models and solves the component manufacturing and processing optimization problem.
    """
    # Create a new model
    model = gp.Model("Component Manufacturing and Processing Optimization")

    # Sets
    Components = range(len(SellingPrice))

    # Decision Variables
    RawMaterialPurchased = model.addVar(vtype=GRB.INTEGER, name="RawMaterialPurchased")
    ComponentProducedfromRawMaterial = model.addVars(Components, vtype=GRB.INTEGER, name="ComponentProducedfromRawMaterial")
    Engine2TransmissionProduced = model.addVar(vtype=GRB.INTEGER, name="Engine2TransmissionProduced")
    Engine2BrakeSystemProduced = model.addVar(vtype=GRB.INTEGER, name="Engine2BrakeSystemProduced")
    Transmission2BrakeSystemProduced = model.addVar(vtype=GRB.INTEGER, name="Transmission2BrakeSystemProduced")
    ComponentSold = model.addVars(Components, vtype=GRB.INTEGER, name="ComponentSold")

    # Objective: Maximize total profit
    revenue = gp.quicksum(SellingPrice[c] * ComponentSold[c] for c in Components)
    raw_material_cost = (RawMaterialCost + EnergyCostPerRawMaterial) * RawMaterialPurchased
    processing_cost = (Engine2TransmissionCost * Engine2TransmissionProduced +
                       Engine2BrakeSystemCost * Engine2BrakeSystemProduced +
                       Transmission2BrakeSystemCost * Transmission2BrakeSystemProduced)

    model.setObjective(revenue - raw_material_cost - processing_cost, GRB.MAXIMIZE)

    # Constraint 1: Machine time constraint
    machine_time = (MachineTimePerRawMaterial * RawMaterialPurchased +
                    Engine2TransmissionMachineTime * Engine2TransmissionProduced +
                    Engine2BrakeSystemMachineTime * Engine2BrakeSystemProduced +
                    Transmission2BrakeSystemMachineTime * Transmission2BrakeSystemProduced)
    model.addConstr(machine_time <= MaxMachineTime, "MachineTime")

    # Constraint 2: Minimum Brake System production constraint
    model.addConstr(
        Engine2BrakeSystemProduced + Transmission2BrakeSystemProduced >= MinBrakeSystemProduction,
        "MinBrakeSystemProduction"
    )

    # Constraint 3: Demand constraint
    for c in Components:
        model.addConstr(ComponentSold[c] <= MaxDemand[c], f"MaxDemand_{c}")

    # Constraint 4: Production constraint - components from raw material
    for c in Components:
        model.addConstr(
            ComponentProducedfromRawMaterial[c] == ComponentPerRawMaterial[c] * RawMaterialPurchased,
            f"RawMaterialProduction_{c}"
        )

    # Constraint 5: Production constraint - Engine Block
    model.addConstr(
        ComponentSold[0] == ComponentProducedfromRawMaterial[0] - Engine2TransmissionProduced - Engine2BrakeSystemProduced,
        "EngineBlockBalance"
    )

    # Constraint 6: Production constraint - Transmission
    model.addConstr(
        ComponentSold[1] == ComponentProducedfromRawMaterial[1] + Engine2TransmissionProduced - Transmission2BrakeSystemProduced,
        "TransmissionBalance"
    )

    # Constraint 7: Production constraint - Brake System
    model.addConstr(
        ComponentSold[2] == ComponentProducedfromRawMaterial[2] + Engine2BrakeSystemProduced + Transmission2BrakeSystemProduced,
        "BrakeSystemBalance"
    )

    # 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_component_manufacturing_optimization()
    print(result)


Solution status: OPTIMAL
Optimal objective value: 851452