A publishing company needs to plan the optimal strategy for printing books over the next six months. The company has forecasted the monthly demand as follows: Month 1—5,000 copies; Month 2—6,000 copies; Month 3—5,500 copies; Month 4—7,000 copies; Month 5—4,500 copies; Month 6—4,000 copies. The cost of printing one copy is $2 during regular working hours (RT) and $3 during overtime (OT). Each month, regular-time production is limited to 6,000 copies, and overtime production is limited to 2,000 copies. Additionally, the inventory holding cost is $0.5 per copy per month. What is the minimum total cost to meet the demand for the next six months on time?

Problem type: ILP (Small: 18 variables, 24 constraints, 52 nonzeros)

Mathematical model:
Set:
1. Month
The set of months, \( M = \{1, 2, ..., MonthNum\} \)

Parameter:
1. MonthNum
6
2. RegularTimeProductionLimit
6000
3. OvertimeProductionLimit
2000
4. RegularTimeCost
2
5. OvertimeCost
3
6. InventoryHoldingCost
0.5
7. Demand
[5000, 6000, 5500, 7000, 4500, 4000]

Decision variable:
1. RegularTimeProduction
Integer variable, \( RegularTimeProduction_m \forall m \in M \), where \( RegularTimeProduction_m \) is the amount of books produced in month \( m \) during regular time.
2. OvertimeProduction
Integer variable, \( OvertimeProduction_m \forall m \in M \), where \( OvertimeProduction_m \) is the amount of books produced in month \( m \) during overtime.
3. Inventory
Integer variable, \( Inventory_m \forall m \in M \), where \( Inventory_m \) is the amount of books in inventory at the end of month \( m \).

Objective:
1. Minimize the total cost of production and inventory holding.
min: \sum_{m \in M} (RegularTimeCost * RegularTimeProduction[m] + OvertimeCost * OvertimeProduction[m] + InventoryHoldingCost * Inventory[m])

Constraint:
1. Initial inventory constraint.
Inventory[1] = 0 + RegularTimeProduction[1] + OvertimeProduction[1] - Demand[1]
2. Inventory balance constraint.
Inventory[m] = Inventory[m-1] + RegularTimeProduction[m] + OvertimeProduction[m] - Demand[m] \forall m \in M, m > 1
3. Demand constraint.
RegularTimeProduction[1] + OvertimeProduction[1] \geq Demand[1] 
Inventory[m-1]+ RegularTimeProduction[m] + OvertimeProduction[m] \geq Demand[m] \forall m \in M, m > 1
4. Regular time production limit constraint.
RegularTimeProduction[m] \leq RegularTimeProductionLimit \forall m \in M
5. Overtime production limit constraint.
OvertimeProduction[m] \leq OvertimeProductionLimit \forall m \in M
6. Non-negativity constraint.
RegularTimeProduction[m] \geq 0 \forall m \in M
OvertimeProduction[m] \geq 0 \forall m \in M
Inventory[m] \geq 0 \forall m \in M

Type:
Integer, Linear, Linear
ILP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_production_planning(
    RegularTimeProductionLimit=6000,
    OvertimeProductionLimit=2000,
    RegularTimeCost=2,
    OvertimeCost=3,
    InventoryHoldingCost=0.5,
    Demand=[5000, 6000, 5500, 7000, 4500, 4000]):
    """
    Solves the production planning optimization problem.
    """
    # Create a new model
    model = gp.Model("Production Planning")

    # Parameters
    MonthNum = len(Demand)

    # Define month set (1-indexed to match the model description)
    Months = range(1, MonthNum + 1)

    # Decision Variables
    RegularTimeProduction = model.addVars(Months,
                                          vtype=GRB.INTEGER,
                                          name="RegularTimeProduction")
    OvertimeProduction = model.addVars(Months,
                                       vtype=GRB.INTEGER,
                                       name="OvertimeProduction")
    Inventory = model.addVars(Months, vtype=GRB.INTEGER, name="Inventory")

    # Objective: Minimize total cost
    objective = gp.quicksum(RegularTimeCost * RegularTimeProduction[m] +
                            OvertimeCost * OvertimeProduction[m] +
                            InventoryHoldingCost * Inventory[m] for m in Months)
    model.setObjective(objective, GRB.MINIMIZE)

    # Constraint 1: Initial inventory constraint
    model.addConstr(
        Inventory[1] == RegularTimeProduction[1] + OvertimeProduction[1] -
        Demand[0], "InitialInventory")

    # Constraint 2: Inventory balance constraint for months after the first
    model.addConstrs(
        (Inventory[m] == Inventory[m - 1] + RegularTimeProduction[m] +
         OvertimeProduction[m] - Demand[m - 1]
         for m in range(2, MonthNum + 1)), "InventoryBalance")

    # Constraint 3: Demand constraint
    # Note: This is redundant given inventory balance and non-negative inventory
    model.addConstrs(
        (RegularTimeProduction[m] + OvertimeProduction[m] >= Demand[m - 1]
         for m in [1]), "DemandSatisfaction_1")
    model.addConstrs(
        (Inventory[m - 1] + RegularTimeProduction[m] +
         OvertimeProduction[m] >= Demand[m - 1]
         for m in range(2, MonthNum + 1)), "DemandSatisfaction_Rest")

    # Constraint 4: Regular time production limit constraint
    model.addConstrs((RegularTimeProduction[m] <= RegularTimeProductionLimit
                      for m in Months), "RegularTimeLimit")

    # Constraint 5: Overtime production limit constraint
    model.addConstrs((OvertimeProduction[m] <= OvertimeProductionLimit
                      for m in Months), "OvertimeLimit")

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

Solution status: OPTIMAL
Optimal objective value: 64750