To strengthen grassroots medical services, the Health Bureau of a city has equipped a mobile medical vehicle to regularly provide basic medical check-ups and medicines to various communities in the suburbs. To improve service efficiency, it is necessary to optimize the route of the mobile medical vehicle. The starting point of the medical vehicle is the Central City Hospital, located at coordinates (0, 0). The information for the 10 communities to be served is as follows:

|  COMMUNITY ID |        (x, y)       | TIME WINDOW |
|---------------|---------------------|-------------|
| C1            | (-5.87, -3.3)       | [8, 12]     |
| C2            | (-4.1, 4.6)         | [9, 13]     |
| C3            | (-2.27, -7.45)      | [10, 15]    |
| C4            | (4.36, 3.38)        | [11, 15]    |
| C5            | (9.05, 5.02)        | [9, 16]     |
| C6            | (-7.46, 6.97)       | [9, 17]     |
| C7            | (7.75, 7.38)        | [10, 18]    |
| C8            | (0.06, -4.25)       | [13, 19]    |
| C9            | (-6.13, -9.78)      | [8, 16]     |
| C10           | (3.25, 9.86)        | [8, 16]     |

Each community has a time window during which the medical vehicle must arrive. The medical vehicle departs from the Central City Hospital at 8:00 AM and travels at a speed of 15 km/h. After visiting all communities, the vehicle must return to the Central City Hospital. What is the shortest travel distance? (Distances are calculated as straight-line distances in kilometers.)

Set:
1. Node
The set of nodes, \( N = \{0, 1, \dots, CommunityNum\} \)
# where node 0 is the starting point (Central City Hospital) and nodes 1 to n are the communities to be served.

Parameter:
1. CommunityNum
10
2. Coordinate
[(0, 0), (-5.87, -3.3), (-4.1, 4.6), (-2.27, -7.45), (4.36, 3.38), (9.05, 5.02), (-7.46, 6.97), (7.75, 7.38), (0.06, -4.25), (-6.13, -9.78), (3.25, 9.86)]
3. Timewindow
[(8, 16), (8, 12), (9, 13), (10, 15), (11, 15), (9, 16), (9, 17), (10, 18), (13, 19), (8, 16), (8, 16)]
4. StartTime
8
5. Speed
15
6. M
10000

Decision variable:
1. X
Binary variable, \( x_{ij} \forall i \in N, j \in N\), where \( x_{ij} = 1 \) if the vehicle departs from node \( i \) and arrives at node \( j \), otherwise \( x_{ij} = 0 \).
2. T^a
Continous variable, \( t^a_i \forall i \in N\), where \( t^a_i \) is the time at which the vehicle arrives at node \( i \).
3. T^d
Continous variable, \( t^d_i \forall i \in N\), where \( t^d_i \) is the time at which the vehicle departs from node \( i \).

Objective:
1. Minimize the total distance traveled by the vehicle.
min: \sum_{i \in N}\sum_{j \in N} Distance[i][j] * x_{ij}
# Where \( Distance[i][j] \) is the Euclidean distance between node \( i \) and node \( j \).

Constraint:
1. Each community must be visited exactly once.
\sum_{i \in N} x_{ij} = 1 \forall j \in N
2. Each community must be exited exactly once.
\sum_{j \in N} x_{ij} = 1 \forall i \in N
3. Eliminate sub-tours.
\sum_{i \in N} \sum_{j \in N} x_{ij} \leq |S| - 1 \forall S \subseteq N, 1 < |S| < |N|
4. The vehicle must arrive at each community within its time window.
t^a_i \geq Timewindow[i][0] \forall i \in N
5. The vehicle must depart from each community within its time window.
t^d_i \leq Timewindow[i][1] \forall i \in N
6. The vehicle's arriveal time at each node must be equal to the departure time from the previous node plus the travel time between the two nodes.
t^d_i + \frac{Distance[i][j]}{Speed} - M * (1 - x_{ij}) \leq t^a_j \forall i, j \in N
t^d_i + \frac{Distance[i][j]}{Speed} + M * (1 - x_{ij}) \geq t^a_j \forall i, j \in N

Type:
Mixed, Linear, Linear
MILP

import gurobipy as gp
from gurobipy import GRB
import math

def solve_time_windowed_tsp(
    coordinates=[(0, 0), (-5.87, -3.3), (-4.1, 4.6), (-2.27, -7.45),
                 (4.36, 3.38), (9.05, 5.02), (-7.46, 6.97), (7.75, 7.38),
                 (0.06, -4.25), (-6.13, -9.78), (3.25, 9.86)],
    time_windows=[(8, 16), (8, 12), (9, 13), (10, 15), (11, 15), (9, 16),
                  (9, 17), (10, 18), (13, 19), (8, 16), (8, 16)],
    speed=15,
    M=10000,
    start_time=8):
    """
    Solves the Traveling Salesperson Problem with Time Windows (TSPTW).
    """
    community_num = len(coordinates) - 1
    nodes = range(community_num + 1)  # 0 is hospital, 1-10 are communities

    # Calculate distance matrix
    def calculate_distance(coord1, coord2):
        return math.sqrt(
            (coord1[0] - coord2[0])**2 + (coord1[1] - coord2[1])**2)

    distance = {}
    for i in nodes:
        for j in nodes:
            if i != j:
                distance[i, j] = calculate_distance(coordinates[i],
                                                    coordinates[j])

    # --- Model Initialization ---
    model = gp.Model("TimeWindowedTSP")

    # --- Decision variables ---
    # x[i,j] = 1 if path goes from i to j
    x = model.addVars(nodes, nodes, vtype=GRB.BINARY, name='x')
    for i in nodes:
        x[i, i].ub = 0

    # t_a[i] = arrival time at node i
    t_a = model.addVars(nodes,
                        lb=[tw[0] for tw in time_windows],
                        ub=[tw[1] for tw in time_windows],
                        vtype=GRB.CONTINUOUS,
                        name='t_a')

    # t_d[i] = departure time from node i
    t_d = model.addVars(nodes,
                        lb=[tw[0] for tw in time_windows],
                        ub=[tw[1] for tw in time_windows],
                        vtype=GRB.CONTINUOUS,
                        name='t_d')

    # Hospital departure time is fixed
    t_d[0].lb = start_time
    t_d[0].ub = start_time

    # --- Objective Function ---
    # minimize total distance
    objective = gp.quicksum(distance[i, j] * x[i, j] for i in nodes
                            for j in nodes if i != j)
    model.setObjective(objective, GRB.MINIMIZE)

    # --- Constraints ---
    # Constraint 1: Each node must be visited exactly once
    model.addConstrs((gp.quicksum(x[i, j] for i in nodes if i != j) == 1
                      for j in nodes if j != 0),
                     name='visit_to')

    # Constraint 2: Must leave each node exactly once
    model.addConstrs((gp.quicksum(x[i, j] for j in nodes if i != j) == 1
                      for i in nodes if i != 0),
                     name='leave_from')

    # Hospital constraints: must leave once and return once
    model.addConstr(gp.quicksum(x[0, j] for j in nodes if j != 0) == 1,
                    name='leave_hospital')
    model.addConstr(gp.quicksum(x[i, 0] for i in nodes if i != 0) == 1,
                    name='return_hospital')

    # Time window constraints
    for i in nodes[1:]:  # Skip hospital
        # Departure time must be after arrival time
        model.addConstr(t_d[i] >= t_a[i], name=f'depart_after_arrival_{i}')

    # Time consistency constraints
    for i in nodes:
        for j in nodes:
            if i != j:
                # If we go from i to j, arrival time at j must be after departure from i plus travel time
                model.addConstr(
                    t_d[i] + distance[i, j] / speed - M * (1 - x[i, j]) <=
                    t_a[j], f'time_consistency_min_{i}_{j}')
                model.addConstr(
                    t_d[i] + distance[i, j] / speed + M * (1 - x[i, j]) >=
                    t_a[j], f'time_consistency_max_{i}_{j}')

    def find_subtours(edges):
        # Create adjacency list
        adj = {i: [] for i in nodes}
        for i, j in edges:
            adj[i].append(j)

        # Find all subtours
        unvisited = set(nodes)
        subtours = []
        while unvisited:
            current = next(iter(unvisited))
            subtour = []
            while current in unvisited:
                unvisited.remove(current)
                subtour.append(current)
                next_nodes = adj.get(current, [])
                if not next_nodes:
                    break
                current = next_nodes[0]
            if len(subtour) < len(nodes):
                subtours.append(subtour)
        return subtours

    def subtour_cb(model, where):
        if where == GRB.Callback.MIPSOL:
            # Get values of binary variables
            x_vals = model.cbGetSolution(model._vars)
            edges = [(i, j) for i, j in x_vals.keys() if x_vals[i, j] > 0.5]

            # Find subtours
            subtours = find_subtours(edges)

            # Add subtour elimination constraints
            for S in subtours:
                if len(S) < len(nodes):
                    model.cbLazy(
                        gp.quicksum(x[i, j] for i in S for j in S
                                    if i != j) <= len(S) - 1)

    # Enable lazy constraints
    model._vars = x
    model.Params.lazyConstraints = 1

    # Optimize model with callback
    model.optimize(subtour_cb)

    # --- Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

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


A fresh food e-commerce platform has one distribution center in the city and needs to use a fleet of refrigerated trucks to deliver fresh products to 12 supermarkets. Each supermarket has a specified time window for receiving goods and a specified unloading time. The distribution center has 5 identical refrigerated trucks, each with a maximum load of 3 tons and a speed of 40 km/h. The coordinates of the distribution center are (0, 0), and the information of the supermarkets is as follows:

| SUPERMARKET ID | (x,y) | DEMAND(kg) | TIME WINDOW | UNLOADING TIME(min) |
|---------|-----------|------------|--------------|--------------|
| S1      | (-5, 4)   | 800        | [7:00-9:00]  | 20           |
| S2      | (3, 6)    | 600        | [8:00-10:00] | 15           |
| S3      | (-2, -3)  | 900        | [7:30-9:30]  | 25           |
| S4      | (4, -2)   | 700        | [8:30-10:30] | 20           |
| S5      | (-4, -5)  | 500        | [9:00-11:00] | 15           |
| S6      | (6, 3)    | 850        | [7:00-9:00]  | 25           |
| S7      | (-3, 5)   | 750        | [8:00-10:00] | 20           |
| S8      | (5, -4)   | 600        | [9:30-11:30] | 15           |
| S9      | (-6, -2)  | 450        | [8:00-10:00] | 15           |
| S10     | (2, 7)    | 800        | [7:30-9:30]  | 20           |
| S11     | (-1, -6)  | 550        | [9:00-11:00] | 15           |
| S12     | (4, 5)    | 700        | [8:30-10:30] | 20           |

The 5 refrigerated trucks leave the distribution center after 6:30 AM to deliver fresh products to the designated supermarkets and must return to the distribution center before 12:00 PM. What is the shortest total travel distance for the refrigerated trucks? (Distances are calculated as straight-line distances in kilometers.)

Set:
1. Node
The set of nodes, \( N = \{0, 1, \dots, SupermarketNum\} \)
# where node 0 is the starting point (Distribution Center) and nodes 1 to n are the supermarkets to be served.
2. Trucks
The set of trucks, \( K = \{1, 2, \dots, TruckNum\} \)

Parameter:
1. SupermarketNum
12
2. Coordinate
[(0, 0), (-5, 4), (3, 6), (-2, -3), (4, -2), (-4, -5), (6, 3), (-3, 5), (5, -4), (-6, -2), (2, 7), (-1, -6), (4, 5)]
3. Demand
[0, 800, 600, 900, 700, 500, 850, 750, 600, 450, 800, 550, 700]
4. Timewindow
[(6.5, 12), (7, 9), (8, 10), (7.5, 9.5), (8.5, 10.5), (9, 11), (7, 9), (8, 10), (9.5, 11.5), (8, 10), (7.5, 9.5), (9, 11), (8.5, 10.5)]
5. UnloadingTime
[0, 20, 15, 25, 20, 15, 25, 20, 15, 15, 20, 15, 20]
6. TruckNum
5
7. Speed
40
8. M
50000

Decision variable:
1. X
Binary variable, \( x_{ijk} \forall i \in N, j \in N, k \in K\), where \( x_{ijk} = 1 \) if truck \( k \) departs from node \( i \) and arrives at node \( j \), otherwise \( x_{ijk} = 0 \).
2. T^a
Continous variable, \( t^a_i \forall i \in N\), where \( t^a_i \) is the time at which the truck arrives at node \( i \).
3. T^d
Continous variable, \( t^d_i \forall i \in N\), where \( t^d_i \) is the time at which the truck departs from node \( i \).

Objective:
1. Minimize the total distance traveled by the trucks.
min: \sum_{k \in K}\sum_{i \in N} \sum_{j \in N} Distance[i][j] * x_{ijk}
# Where \( Distance[i][j] \) is the Euclidean distance between node \( i \) and node \( j \).

Constraint:
1. Each supermarket must be visited exactly once by one truck.
\sum_{k \in K} \sum_{i \in N} x_{ijk} = 1 \forall j \in N/{0}
2. Each supermarket must be exited exactly once by one truck.
\sum_{k \in K} \sum_{j \in N} x_{ijk} = 1 \forall i \in N/{0}
3. Each truck must leave the distribution center once.
\sum_{j \in N} x_{0jk} = 1 \forall k \in K
4. Each truck must return to the distribution center once.
\sum_{i \in N} x_{i0k} = 1 \forall k \in K
5. Eliminate sub-tours.
\sum_{i \in N} \sum_{j \in N} x_{ijk} \leq |S| - 1 \forall S \subseteq N, 1 < |S| < |N|, k \in K
6. The truck must arrive at each supermarket within its time window.
t^a_i \geq Timewindow[i][0] \forall i \in N/{0}
7. The truck must depart from each supermarket within its time window.
t^d_i \leq Timewindow[i][1] \forall i \in N/{0}
8. The truck's arrival time at each node must be more than or equal to the departure time from the previous node plus the travel time between the two nodes.
t^d_i + \frac{Distance[i][j]}{Speed} - M * (1 - x_{ijk}) \leq t^a_j \forall i, j \in N, k \in K
t^d_i + \frac{Distance[i][j]}{Speed} + M * (1 - x_{ijk}) \geq t^a_j \forall i, j \in N, k \in K

Type:
Mixed, Linear, Linear
MILP

import gurobipy as gp
from gurobipy import GRB
import math

def solve_vrptw(
    coordinates=[(0, 0), (-5, 4), (3, 6), (-2, -3), (4, -2), (-4, -5), (6, 3),
                 (-3, 5), (5, -4), (-6, -2), (2, 7), (-1, -6), (4, 5)],
    time_windows=[(6.5, 12), (7, 9), (8, 10), (7.5, 9.5), (8.5, 10.5),
                  (9, 11), (7, 9), (8, 10), (9.5, 11.5), (8, 10), (7.5, 9.5),
                  (9, 11), (8.5, 10.5)],
    demands=[0, 800, 600, 900, 700, 500, 850, 750, 600, 450, 800, 550, 700],
    unloading_times=[0, 20, 15, 25, 20, 15, 25, 20, 15, 15, 20, 15, 20],
    truck_num=5,
    speed=40,
    M=50000):
    """
    Solves the Vehicle Routing Problem with Time Windows (VRPTW).
    """
    supermarket_num = len(coordinates) - 1
    nodes = range(supermarket_num + 1)  # 0 is DC, 1-12 are supermarkets
    trucks = range(truck_num)  # 0-4 are trucks

    # Calculate distance matrix
    def calculate_distance(coord1, coord2):
        return math.sqrt((coord1[0] - coord2[0])**2 +
                         (coord1[1] - coord2[1])**2)

    distance = {}
    for i in nodes:
        for j in nodes:
            if i != j:
                distance[i, j] = calculate_distance(coordinates[i],
                                                    coordinates[j])

    # --- Model Initialization ---
    model = gp.Model("MultiVehicleTimeWindowedVRP")

    # --- Decision variables ---
    # x[i,j,k] = 1 if truck k travels from i to j
    x = {}
    for i in nodes:
        for j in nodes:
            if i != j:
                for k in trucks:
                    x[i, j, k] = model.addVar(vtype=GRB.BINARY,
                                              name=f'x_{i}_{j}_{k}')

    # t_a[i] = arrival time at node i
    t_a = model.addVars(nodes,
                        lb=[time_windows[i][0] for i in nodes],
                        ub=[time_windows[i][1] for i in nodes],
                        name='t_a')

    # t_d[i] = departure time from node i
    t_d = model.addVars(nodes,
                        lb=[time_windows[i][0] for i in nodes],
                        ub=[time_windows[i][1] for i in nodes],
                        name='t_d')

    # --- Objective function ---
    # Set objective: minimize total distance
    objective = gp.quicksum(distance[i, j] * x[i, j, k] for i in nodes
                            for j in nodes for k in trucks if i != j)
    model.setObjective(objective, GRB.MINIMIZE)

    # --- Constraints ---
    # Constraint 1: Each supermarket must be visited exactly once by one truck
    for j in nodes:
        if j != 0:  # Skip DC
            model.addConstr(
                gp.quicksum(x[i, j, k] for i in nodes for k in trucks
                            if i != j) == 1,
                name=f'visit_to_{j}')

    # Constraint 2: Each supermarket must be exited exactly once by one truck
    for i in nodes:
        if i != 0:  # Skip DC
            model.addConstr(
                gp.quicksum(x[i, j, k] for j in nodes for k in trucks
                            if i != j) == 1,
                name=f'leave_from_{i}')

    # Constraint 3: Each truck must leave the DC once
    for k in trucks:
        model.addConstr(gp.quicksum(x[0, j, k] for j in nodes if j != 0) == 1,
                        name=f'truck_{k}_leaves_dc')

    # Constraint 4: Each truck must return to the DC once
    for k in trucks:
        model.addConstr(gp.quicksum(x[i, 0, k] for i in nodes if i != 0) == 1,
                        name=f'truck_{k}_returns_dc')

    # Flow conservation for each truck at each supermarket
    for k in trucks:
        for h in nodes:
            if h != 0:  # Skip DC
                model.addConstr(
                    gp.quicksum(x[i, h, k] for i in nodes if i != h) ==
                    gp.quicksum(x[h, j, k] for j in nodes if j != h),
                    name=f'flow_cons_truck_{k}_node_{h}')

    # Time window constraints
    for i in nodes[1:]:  # Skip DC
        # Departure time must be after arrival time plus unloading time
        model.addConstr(t_d[i] >= t_a[i] + unloading_times[i] / 60,
                        name=f'depart_after_unload_{i}')

    # Time consistency constraints
    for i in nodes:
        for j in nodes:
            if i != j:
                for k in trucks:
                    # If truck k goes from i to j, arrival time at j must be after
                    # departure from i plus travel time
                    model.addConstr(
                        t_d[i] + distance[i, j] / speed - M *
                        (1 - x[i, j, k]) <= t_a[j],
                        name=f'time_cons_min_{i}_{j}_{k}')
                    model.addConstr(
                        t_d[i] + distance[i, j] / speed + M *
                        (1 - x[i, j, k]) >= t_a[j],
                        name=f'time_cons_max_{i}_{j}_{k}')

    def find_subtours(edges):
        # Create adjacency list
        adj = {i: [] for i in nodes}
        for i, j in edges:
            adj[i].append(j)

        # Find all subtours
        unvisited = set(nodes)
        subtours = []
        while unvisited:
            current = next(iter(unvisited))
            subtour = []
            while current in unvisited:
                unvisited.remove(current)
                subtour.append(current)
                next_nodes = adj.get(current, [])
                if not next_nodes:
                    break
                current = next_nodes[0]
            if len(subtour) < len(nodes):
                subtours.append(subtour)
        return subtours

    def subtour_cb(model, where):
        if where == GRB.Callback.MIPSOL:
            # Get values of binary variables
            x_vals = model.cbGetSolution(model._vars)
            edges = [(i, j, k) for i in nodes for j in nodes for k in trucks
                     if i != j and x_vals[i, j, k] > 0.5]

            # Find subtours for each truck
            for k in trucks:
                truck_edges = [(i, j) for i, j, k_idx in edges if k_idx == k]
                subtours = find_subtours(truck_edges)

                # Add subtour elimination constraints
                for S in subtours:
                    if len(S) < len(nodes) and 0 not in S:
                        model.cbLazy(
                            gp.quicksum(x[i, j, k] for i in S for j in S
                                        if i != j) <= len(S) - 1)

    # Enable lazy constraints
    model._vars = x
    model.Params.lazyConstraints = 1

    # Optimize model with callback
    model.optimize(subtour_cb)

    # --- Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

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


A delivery company has a fleet of vehicles at a certain location, consisting of 3 trucks. Each truck has a load limit of 1 ton (i.e., 1000 kg) and a speed of 30 km/h. The delivery fleet needs to provide pickup and delivery services for a group of customers. Each order has independent time windows for the pickup point and the delivery point. Below are the detailed customer order details:

| Order ID | Pickup Point (x, y) | Pickup Time Window | Delivery Point (x, y) | Delivery Time Window | Delivery Weight (kg) |
|----------|---------------------|---------------|---------------------|---------------|-------------------|
| 1        | (1.41, 2.64)        | [9:30, 10:00] | (8.56, 1.11)        | [9:40, 10:40] | 198               |
| 2        | (6.34, -4.45)       | [12:00, 12:30]| (3.63, -7.65)       | [12:10, 13:10]| 252               |
| 3        | (3.04, 7.82)        | [10:30, 11:00]| (-8.88, 9.6)        | [10:50, 11:50]| 94                |
| 4        | (8.18, -6.27)       | [12:00, 12:30]| (-4.31, -5.73)      | [12:20, 12:20]| 223               |
| 5        | (3.03, 2.34)        | [12:30, 13:00]| (7.93, 5.0)         | [12:40, 13:40]| 163               |
| 6        | (0.07, 9.32)        | [8:30, 9: 00] | (-7.54, -3.57)      | [9:00, 10:00] | 247               |
| 7        | (0.57, -1.09)       | [8:00, 8:30]  | (-5.15, -4.55)      | [8:10, 9:10]  | 133               |
| 8        | (8.87, 2.71)        | [14:00, 14:30]| (-9.87, 0.49)       | [14:30, 15:30]| 188               |
| 9        | (-3.97, -3.81)      | [10:00, 10:30]| (-3.91, -2.12)      | [10:00, 11:00]| 216               |
| 10       | (-0.08, -8.95)      | [9:30, 10:00] | (-6.29, -7.8)       | [9:40, 10:40] | 225               |

The delivery fleet departs from the depot at coordinates (0, 0), serves all orders, and then returns to the depot. How should the routes be planned to minimize the total distance traveled by the trucks? (Distances are calculated as straight-line distances.)


A logistics company needs to arrange forklifts for a newly built warehouse to meet daily operational demands. The forklift requirements for the warehouse over the next 7 weeks are 6, 9, 12, 8, 5, 7 and 4. The logistics company plans to lease forklifts from a rental company, which offers the following leasing options:

1. Long-term forklift rental: The lease term is fixed at 7 weeks, with a rental cost of 240 yuan per week. Forklifts under long-term lease are available for the entire lease period.
2. Short-term forklift rental: The lease term is flexible, available on a weekly basis, with a rental cost of 390 yuan per week. Short-term leased forklifts are only available during the week they are leased.
3. Shared forklifts: These are vehicles jointly leased with other warehouses, but they are only available in weeks 2, 4, and 5. The rental cost is 220 yuan per week.
4. Promotional forklifts: These forklifts can be leased at a discounted rate during the last 4 weeks, with a rental cost of 190 yuan per week. A maximum of 2 promotional forklifts can be leased per week.

Please design a leasing plan that fully meets the warehouse's forklift requirements over the next 7 weeks while minimizing the total leasing cost.

Set:
1. Week
The set of weeks, \( W = \{1, 2, \dots, WeekNum\} \)

Parameter:
1. WeekNum
7
2. Demand
[6, 9, 12, 8, 5, 7, 4]
3. LongTermCost
240
4. LongTermWeekNum
7
5. ShortTermCost
390
6. SharedCost
220
7. SharedWeek
[2, 4, 5]
8. PromoCost
190
9. PromoMaxNum
2
10. PromoWeek
[4, 5, 6, 7]

Decision variable:
1. LongTermNum
Integer variable, \( LongTermNum \), where \( LongTermNum \) is the number of long-term forklifts leased.
2. ShortTermNum
Integer variable, \( ShortTermNum_{i} \forall i \in W\), where \( ShortTermNum_i \) is the number of short-term forklifts leased in week \( i \).
3. SharedNum
Integer variable, \( SharedNum \), where \( SharedNum \) is the number of shared forklifts leased.
4. PromoNum
Integer variable, \( PromoNum_{i} \forall i \in W\), where \( PromoNum_i \) is the number of promotional forklifts leased in week \( i \).

Objective:
1. Minimize the total leasing cost.
min: LongTermCost * LongTermNum * LongTermWeekNum + ShortTermCost * \sum_{i \in W} ShortTermNum_i + SharedCost * SharedNum * len(SharedWeek) + PromoCost * \sum_{i \in PromoWeek} PromoNum_i

Constraint:
1. The total number of forklifts leased must meet the demand for each week.
LongTermNum + ShortTermNum_i + PromoNum_i \geq Demand_i \forall i \in (W / SharedWeek)
2. The total number of forklifts leased must meet the demand for each week, considering shared forklifts.
LongTermNum + ShortTermNum_i + SharedNum + PromoNum_i \geq Demand_i \forall i \in SharedWeek
3. The number of promotional forklifts leased in each week must not exceed the maximum limit.
PromoNum_i \leq PromoMaxNum \forall i \in PromoWeek
4. The promotional forklifts can't be leased in weeks other than the promotional weeks.
PromoNum_i = 0 \forall i \in (W / PromoWeek)

Type:
Integer, Linear, Linear
ILP

import gurobipy as gp
from gurobipy import GRB

def solve_forklift_leasing(
    demand=[6, 9, 12, 8, 5, 7, 4],
    long_term_cost=240,
    short_term_cost=390,
    shared_cost=220,
    shared_weeks=[2, 4, 5],
    promo_cost=190,
    promo_max_num=2,
    promo_weeks=[4, 5, 6, 7]):
    """
    Models and solves the forklift leasing optimization problem.
    """
    # --- 1. Model Creation ---
    model = gp.Model("ForkliftLeasing")

    # --- 2. Parameters & Sets ---
    week_num = len(demand)
    weeks = range(1, week_num + 1)
    long_term_weeks = week_num

    # --- 3. Decision Variables ---
    long_term_num = model.addVar(vtype=GRB.INTEGER, name="LongTermNum")
    short_term_num = model.addVars(weeks, vtype=GRB.INTEGER, name="ShortTermNum")
    shared_num = model.addVar(vtype=GRB.INTEGER, name="SharedNum")
    promo_num = model.addVars(weeks, vtype=GRB.INTEGER, name="PromoNum")

    # --- 4. Objective Function ---
    objective = long_term_cost * long_term_num * long_term_weeks
    objective += short_term_cost * gp.quicksum(short_term_num[i] for i in weeks)
    objective += shared_cost * shared_num * len(shared_weeks)
    objective += promo_cost * gp.quicksum(promo_num[i]
                                          for i in weeks if i in promo_weeks)
    model.setObjective(objective, GRB.MINIMIZE)

    # --- 5. Constraints ---
    # Constraint 1: Total forklifts per week must meet demand (non-shared weeks)
    for i in weeks:
        if i not in shared_weeks:
            model.addConstr(long_term_num + short_term_num[i] + promo_num[i]
                            >= demand[i - 1],
                            name=f"Demand_Week_{i}")

    # Constraint 2: Total forklifts during shared weeks must meet demand
    for i in shared_weeks:
        model.addConstr(long_term_num + short_term_num[i] + shared_num +
                        promo_num[i] >= demand[i - 1],
                        name=f"Demand_SharedWeek_{i}")

    # Constraint 3: Promotional forklift quantity must not exceed maximum limit
    for i in promo_weeks:
        model.addConstr(promo_num[i] <= promo_max_num, name=f"PromoLimit_Week_{i}")

    # Constraint 4: Non-promotional weeks cannot lease promotional forklifts
    for i in weeks:
        if i not in promo_weeks:
            model.addConstr(promo_num[i] == 0, name=f"NoPromo_Week_{i}")

    # --- 6. Solve the Model ---
    model.optimize()

    # --- 7. Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

# Run the solver function
if __name__ == "__main__":
    result = solve_forklift_leasing()
    print(result)


A manufacturing plant specializes in producing customized parts, where production is scheduled based on customer orders. The factory has five machines (M1-M5), each with distinct functions: M1 for cutting, M2 for drilling, M3 for welding, M4 for polishing, and M5 for final assembly. Currently, four orders (J1-J4) have been received, each requiring a fixed sequence of processing steps that must be completed on specific machines in a prescribed order. The processing steps, required machines, and execution times for each task are as follows:

J1: (1) Cutting on M1, taking 3 hours; (2) Drilling on M2, taking 2 hours; (3) Assembly on M5, taking 4 hours.
J2: (1) Welding on M3, taking 4 hours; (2) Polishing on M4, taking 3 hours; (3) Assembly on M5, taking 2 hours.
J3: (1) Drilling on M2, taking 3 hours; (2) Cutting on M1, taking 5 hours; (3) Polishing on M4, taking 2 hours.
J4: (1) Cutting on M1, taking 2 hours; (2) Welding on M3, taking 3 hours; (3) Polishing on M4, taking 4 hours.

Each machine can only handle one task at a time and cannot process multiple tasks simultaneously. Once a step begins processing, it cannot be paused or interrupted midway. How should the order processing be scheduled to minimize the total completion time for all tasks?

Set:
1. Machine
The set of machines, \( M = \{1, 2, 3, 4, 5\} \)
2. Job
The set of jobs, \( J = \{1, 2, 3, 4\} \)

Parameter:
1. JobProcess
[
[(1, 3), (2, 2), (5, 4)],
[(3, 4), (4, 3), (5, 2)],
[(2, 3), (1, 5), (4, 2)],
[(1, 2), (3, 3), (4, 4)]
]
2. MachineID
['M1', 'M2', 'M3', 'M4', 'M5']
3. JobID
['J1', 'J2', 'J3', 'J4']
4. BigM
A sufficiently large number, computed as the sum of all processing times.

Decision variable:
1. StartTime
Integer variable, \( StartTime_{j,k} \forall j \in J, k \in \{0, 1, ..., |JobProcess_j|-1\}\), where \( StartTime_{j,k} \) is the start time of the \( k^{th} \) process of job \( j \).
2. Makespan
Integer variable representing the maximum completion time of all jobs.
3. Indicator
Binary variable, \( Indicator_{j_1,k_1,j_2,k_2} \) for each pair of processes on the same machine, where value 1 means process (j_1,k_1) comes before (j_2,k_2), and 0 means (j_2,k_2) comes before (j_1,k_1).

Objective:
1. Minimize the makespan.
min: Makespan

Constraint:
1. The start time of the next process must be greater than or equal to the end time of the previous process.
StartTime_{j,k+1} \geq StartTime_{j,k} + JobProcess_j[k][1] \forall j \in J, k \in \{0, 1, ..., |JobProcess_j|-2\}

2. One machine can only process one job at a time (disjunctive constraints using binary indicators).
For each machine m and each pair of processes (j_1,k_1) and (j_2,k_2) that use machine m:
a. StartTime_{j_1,k_1} + JobProcess_{j_1}[k_1][1] \leq StartTime_{j_2,k_2} + BigM * Indicator_{j_1,k_1,j_2,k_2}
b. StartTime_{j_2,k_2} + JobProcess_{j_2}[k_2][1] \leq StartTime_{j_1,k_1} + BigM * (1 - Indicator_{j_1,k_1,j_2,k_2})

3. The start time of all processes must be non-negative.
StartTime_{j,k} \geq 0 \forall j \in J, k \in \{0, 1, ..., |JobProcess_j|-1\}

4. Makespan definition: makespan must be at least the completion time of every job.
Makespan \geq StartTime_{j,|JobProcess_j|-1} + JobProcess_j[|JobProcess_j|-1][1] \forall j \in J

Type:
Integer, Linear, Linear
ILP

import gurobipy as gp
from gurobipy import GRB
import json
import os
machines = list(range(1, 6))  # M = {1, 2, 3, 4, 5}
jobs = list(range(1, 5))  # J = {1, 2, 3, 4}

# JobProcess[j-1] contains a list of tuples (machine, process_time) for job j
job_processes = [
    [(1, 3), (2, 2), (5, 4)],  # Job 1 processes
    [(3, 4), (4, 3), (5, 2)],  # Job 2 processes
    [(2, 3), (1, 5), (4, 2)],  # Job 3 processes
    [(1, 2), (3, 3), (4, 4)]  # Job 4 processes
]

machine_id = ['M1', 'M2', 'M3', 'M4', 'M5']
job_id = ['J1', 'J2', 'J3', 'J4']

def solve_job_shop_scheduling(job_processes=[[(1, 3), (2, 2), (5, 4)], [(3, 4), (4, 3), (5, 2)], [(2, 3), (1, 5), (4, 2)], [(1, 2), (3, 3), (4, 4)]]):
    # Data
    machines = list(range(1, 6))
    jobs = list(range(1, 5))

    # Create a new model
    model = gp.Model("JobShopScheduling")

    # Calculate a big-M value (an upper bound on the makespan)
    big_M = sum(duration for job in job_processes for _, duration in job)

    # Create decision variables
    start_times = {}
    for j in range(len(jobs)):
        for k in range(len(job_processes[j])):
            start_times[j, k] = model.addVar(vtype=GRB.INTEGER,
                                             lb=0,
                                             name=f"StartTime_{j+1}_{k+1}")

    # Create makespan variable
    makespan = model.addVar(vtype=GRB.INTEGER, name="Makespan")

    # Set objective
    model.setObjective(makespan, GRB.MINIMIZE)

    # Constraint 1: The start time of the next process must be greater than or equal to the end time of the previous process
    for j in range(len(jobs)):
        for k in range(len(job_processes[j]) - 1):
            model.addConstr(
                start_times[j,
                            k + 1] >= start_times[j, k] + job_processes[j][k][1],
                f"PrecedenceJob_{j+1}_Process_{k+1}_to_{k+2}")

    # Constraint 2: One machine can only process one job at a time
    for m in machines:
        # Find all processes that use this machine
        processes_on_machine = []
        for j in range(len(jobs)):
            for k in range(len(job_processes[j])):
                if job_processes[j][k][0] == m:
                    processes_on_machine.append((j, k))

        # Add non-overlap constraints for each pair of processes on this machine
        for i in range(len(processes_on_machine)):
            for j in range(i + 1, len(processes_on_machine)):
                j1, k1 = processes_on_machine[i]
                j2, k2 = processes_on_machine[j]

                # Either j1,k1 finishes before j2,k2 starts OR j2,k2 finishes before j1,k1 starts
                indicator = model.addVar(
                    vtype=GRB.BINARY,
                    name=f"Indicator_{j1+1}_{k1+1}_{j2+1}_{k2+1}")
                model.addConstr(
                    start_times[j1, k1] + job_processes[j1][k1][1]
                    <= start_times[j2, k2] + big_M * (1 - indicator),
                    f"NoOverlap1_{j1+1}_{k1+1}_{j2+1}_{k2+1}")
                model.addConstr(
                    start_times[j2, k2] + job_processes[j2][k2][1]
                    <= start_times[j1, k1] + big_M * indicator,
                    f"NoOverlap2_{j1+1}_{k1+1}_{j2+1}_{k2+1}")

    # Constraint 3: The start time of the first process must be greater than or equal to 0
    for j in range(len(jobs)):
        model.addConstr(start_times[j, 0] >= 0, f"NonNegativeStart_{j+1}")

    # Constraint for makespan: makespan is the maximum completion time among all jobs
    for j in range(len(jobs)):
        last_process = len(job_processes[j]) - 1
        model.addConstr(
            makespan
            >= start_times[j, last_process] + job_processes[j][last_process][1],
            f"MakespanDef_{j+1}")

    # Solve the model
    model.optimize()

    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

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


HAPPY pharmaceutical produces the drug Panacea from four chemicals. Today they must produce 1,000 lb of the drug. The three active ingredients in Panacea are A, B, and C. By weight, at least 8% of Panacea must consist of A, at least 4% of B, and at least 2% of C. The cost per pound of each chemical and the amount of each active ingredient in 1 lb of each chemical are given in Table as follows:

| Chemical | Cost ($ per Lb) | A    | B    | C    |
|----------|-----------------|------|------|------|
| 1        | 8               | .03  | .02  | .01  |
| 2        | 10              | .06  | .04  | .01  |
| 3        | 11              | .10  | .03  | .04  |
| 4        | 14              | .12  | .09  | .04  |

It is necessary that at least 100 lb of chemical 2 be used. Find a solution would determine the cheapest way of producing today's batch of Panacea.

Set:
1. Chemical
The set of chemicals, \( C = \{1, 2, 3, 4\} \)
2. Ingredient
The set of ingredients, \( I = \{1, 2, 3\} \)

Parameter:
1. ChemicalID
['1', '2', '3', '4']
2. IngredientID
['A', 'B', 'C']
3. ChemicalCost
[8, 10, 11, 14]
4. IngredientContent
# units/lb
[
[0.03, 0.02, 0.01],
[0.06, 0.04, 0.01],
[0.10, 0.03, 0.04],
[0.12, 0.09, 0.04]
]
5. IngredientMin
[0.08, 0.04, 0.02]
6. ChemicalMin
[0, 100, 0, 0]
7. ProduceTarget
1000

Decision variable:
1. ChemicalAmount
Continuous variable, \( ChemicalAmount_i \forall i \in C \), where \( ChemicalAmount_i \) is the amount of chemical \( i \) used in the production of Panacea.

Objective:
1. Minimize the total cost of chemicals used.
min: \sum_{i \in C} ChemicalCost[i] * ChemicalAmount[i]

Constraint:
1. Ingredient minimum content.
\sum_{i \in C} IngredientContent[i][j] * ChemicalAmount[i] \geq IngredientMin[j] * ProduceTarget \forall j \in I
2. Chemical minimum content.
ChemicalAmount[i] \geq ChemicalMin[i] \forall i \in C
3. Total amount of chemicals used.
\sum_{i \in C} ChemicalAmount[i] = ProduceTarget
4. Non-negativity constraint.
ChemicalAmount[i] \geq 0 \forall i \in C

Type:
Continuous, Linear, Linear
LP

import gurobipy as gp
from gurobipy import GRB

def solve_chemical_mixing(
    chemical_cost=[8, 10, 11, 14],
    ingredient_content=[
        [0.03, 0.02, 0.01],  # Chemical 1
        [0.06, 0.04, 0.01],  # Chemical 2
        [0.10, 0.03, 0.04],  # Chemical 3
        [0.12, 0.09, 0.04]  # Chemical 4
    ],
    ingredient_min=[0.08, 0.04, 0.02],
    chemical_min=[0, 100, 0, 0],
    produce_target=1000):
    """
    Solves the chemical mixing problem to minimize cost.
    """
    # --- 1. Model Creation ---
    model = gp.Model("ChemicalMixing")

    # --- 2. Parameters & Sets ---
    chemicals = range(len(chemical_cost))
    ingredients = range(len(ingredient_min))

    # --- 3. Decision Variables ---
    chemical_amount = model.addVars(chemicals, lb=0, name="ChemicalAmount")

    # --- 4. Objective Function ---
    # Minimize the total cost
    model.setObjective(chemical_amount.prod(chemical_cost), GRB.MINIMIZE)

    # --- 5. Constraints ---
    # Constraint 1: Ingredient minimum content
    model.addConstrs(
        (gp.quicksum(ingredient_content[i][j] * chemical_amount[i]
                     for i in chemicals) >= ingredient_min[j] * produce_target
         for j in ingredients), "IngredientMin")

    # Constraint 2: Chemical minimum content
    model.addConstrs((chemical_amount[i] >= chemical_min[i] for i in chemicals),
                     "ChemicalMin")

    # Constraint 3: Total amount constraint
    model.addConstr(
        chemical_amount.sum() == produce_target,
        "TotalAmount")

    # --- 6. Solve the Model ---
    model.optimize()

    # --- 7. Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

# Run the solver function
if __name__ == "__main__":
    result = solve_chemical_mixing()
    print(result)


A commodities trader oversees a secure vault facility with a maximum capacity of 500 kilograms, dedicated to storing refined platinum. At the beginning of Quarter 1, the trader holds 150 kilograms of platinum. Each quarter, the trader assesses the current inventory, sells any amount of platinum up to the available stock at the prevailing selling price, then purchase platinum at the current purchase price for the season, while ensuring that the total inventory does not exceed the vault's capacity (requires selling first before buying). The selling and purchase prices for platinum for each quarter are as follows:

| Quarter | Selling Price ($/kg) | Purchase Price ($/kg) |
|---------|-----------------------|-----------------------|
| 1       | 30,000               | 32,000               |
| 2       | 35,000               | 36,000               |
| 3       | 40,000               | 38,000               |
| 4       | 36,000               | 33,000               |
| 5       | 38,000               | 39,000               |
| 6       | 42,000               | 40,000               |

The trader's objective is to devise an optimal strategy for buying and selling platinum over the six quarters to maximize total profit. What is the maximum achievable profit?

Set:
1. Quarter
The set of quarters, \( Q = \{1, 2, ..., QuarterNum\} \)

Parameter:
1. QuarterNum
6
2. VaultCapacity
500
3. InitialInventory
150
4. SellingPrice
[30000, 35000, 40000, 36000, 38000, 42000]
5. PurchasePrice
[32000, 36000, 38000, 33000, 39000, 40000]

Decision variable:
1. SellingAmount
Continuous variable, \( SellingAmount_q \forall q \in Q \), where \( SellingAmount_q \) is the amount of platinum sold in quarter \( q \).
2. PurchaseAmount
Continuous variable, \( PurchaseAmount_q \forall q \in Q \), where \( PurchaseAmount_q \) is the amount of platinum purchased in quarter \( q \).
3. Inventory
Continuous variable, \( Inventory_q \forall q \in Q \), where \( Inventory_q \) is the amount of platinum in the vault at the end of quarter \( q \).

Objective:
1. Maximize the total profit from selling platinum.
max: \sum_{q \in Q} (SellingPrice[q] * SellingAmount[q] - PurchasePrice[q] * PurchaseAmount[q])

Constraint:
1. Initial inventory constraint.
Inventory[1] = InitialInventory + PurchaseAmount[1] - SellingAmount[1]
2. Inventory balance constraint.
Inventory[q] = Inventory[q-1] + PurchaseAmount[q] - SellingAmount[q] \forall q \in Q, q > 1
3. Inventory capacity constraint.
Inventory[q] \leq VaultCapacity \forall q \in Q
4. Selling amount constraint.
SellingAmount[q] \leq Inventory[q-1] \forall q \in Q, q > 1
5. Initial selling amount constraint.
SellingAmount[1] \leq InitialInventory
6. Non-negativity constraint.
PurchaseAmount[q] \geq 0 \forall q \in Q
SellingAmount[q] \geq 0 \forall q \in Q
7. Inventory non-negativity constraint.
Inventory[q] \geq 0 \forall q \in Q

Type:
Continuous, Linear, Linear
LP

import gurobipy as gp
from gurobipy import GRB

def solve_platinum_trading(
    vault_capacity=500,
    initial_inventory=150,
    selling_price=[30000, 35000, 40000, 36000, 38000, 42000],
    purchase_price=[32000, 36000, 38000, 33000, 39000, 40000]):
    """
    Solves the platinum trading optimization problem.
    """
    # --- 1. Model Creation ---
    model = gp.Model("PlatinumTrading")

    # --- 2. Parameters & Sets ---
    quarter_num = len(selling_price)
    quarters = range(1, quarter_num + 1)

    # --- 3. Decision Variables ---
    selling_amount = model.addVars(quarters, lb=0, name="SellingAmount")
    purchase_amount = model.addVars(quarters, lb=0, name="PurchaseAmount")
    inventory = model.addVars(quarters,
                              lb=0,
                              ub=vault_capacity,
                              name="Inventory")

    # --- 4. Objective Function ---
    # Maximize total profit
    objective = gp.quicksum(selling_price[q - 1] * selling_amount[q] -
                            purchase_price[q - 1] * purchase_amount[q]
                            for q in quarters)
    model.setObjective(objective, GRB.MAXIMIZE)

    # --- 5. Constraints ---
    # Constraint 1: Initial inventory constraint
    model.addConstr(inventory[1] == initial_inventory + purchase_amount[1] -
                    selling_amount[1], "InitialInventory")

    # Constraint 2: Inventory balance constraint
    model.addConstrs(
        (inventory[q] == inventory[q - 1] + purchase_amount[q] -
         selling_amount[q] for q in range(2, quarter_num + 1)),
        "InventoryBalance")

    # Constraint 3: Selling amount must be less than or equal to inventory
    model.addConstr(selling_amount[1] <= initial_inventory,
                    "InitialInventoryLimit")
    model.addConstrs((selling_amount[q] <= inventory[q - 1]
                      for q in range(2, quarter_num + 1)), "InventoryLimit")

    # --- 6. Solve the Model ---
    model.optimize()

    # --- 7. Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

# Run the solver function
if __name__ == "__main__":
    result = solve_platinum_trading()
    print(result)


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?

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

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)


GreenWeave specializes in transforming raw materials such as organic cotton, jute, and linen into eco-friendly textiles: organic cotton fabric, jute fabric, and linen fabric. Organic cotton fabric can be manufactured by processing either organic cotton or jute. Linen fabric can be produced from any of the raw materials—organic cotton, jute, or linen. Jute fabric, on the other hand, can be created by processing jute or linen. The production process for organic cotton fabric reduces the input fiber by 30%, leaving 70% for the final product. For linen fabric, the process removes 20% of the input fiber. For jute fabric, the process removes 25% of the input fiber. The costs of purchasing and processing each raw material, along with their availability, are detailed as follows:

| Raw Material      | Purchasing Cost ($/ton) | Processing Cost ($/ton)  | Availability (tons) |
|-------------------|-------------------------|--------------------------|---------------------|
| Organic Cotton    | 350                     | 100                      | 1000                |
| Jute              | 400                     | 80                       | 800                 |
| Linen             | 300                     | 150                      | 600                 |

To fulfill market requirements, GreenWeave must produce a minimum of 300 tons of organic cotton fabric fiber, 400 tons of jute fabric fiber, and 200 tons of linen fabric fiber. What is the minimum total cost to meet GreenWeave's production needs?

Set:
1. RawMaterials
The set of raw materials, \( R = \{"cotton", "jute", "linen"\} \)
2. Products
The set of products, \( P = \{"cotton_fabric", "jute_fabric", "linen_fabric"\} \)
3. ProducedFrom
The set of products and their corresponding raw materials, \( PF = \{"cotton_fabric": ("cotton", "jute"), "linen_fabric": ("linen", "jute", "cotton"), "jute_fabric": ("jute", "linen")\} \)

Parameter:
1. ReducedFiberRatio
The ratio of fiber remaining after processing, \( RFR = \{ "cotton_fabric": 0.7, "jute_fabric": 0.75, "linen_fabric": 0.8 \} \)
2. PurchasingCost
The cost of purchasing each raw material, \( PC = \{ "cotton": 350, "jute": 400, "linen": 300 \} \) # ($/ton)
3. ProcessingCost
The cost of processing each raw material, \( PrC = \{ "cotton": 100, "jute": 80, "linen": 150 \} \) # ($/ton)
4. Availability
The availability of each raw material, \( A = \{ "cotton": 1000, "jute": 800, "linen": 600 \} \) # (tons)
5. Demand
The demand for each product, \( D = \{ "cotton_fabric": 300, "jute_fabric": 400, "linen_fabric": 200 \} \) # (tons)

Decision variable:
1. X
Continuous variable, \( X_{r,p} \forall r \in PF[p], p \in P \), where \( X_{r,p} \) is the amount of raw material \( r \) used to produce product \( p \).

Objective:
1. Minimize the total cost of purchasing and processing raw materials.
min: \sum_{p \in P} \sum_{r \in PF[p]} (PC[r] + PrC[r]) * X_{r,p}

Constraint:
1. Demand constraint.
\sum_{r \in PF[p]} RFR[p] * X_{r,p} \geq D[p] \forall p \in P
2. Availability constraint.
\sum_{p \in P, r \in PF[p]} X_{r,p} \leq A[r] \forall r \in R

Type:
Continuous, Linear, Linear
LP

import gurobipy as gp
from gurobipy import GRB

def solve_textile_manufacturing(
    ProducedFrom={
        "cotton_fabric": ["cotton", "jute"],
        "linen_fabric": ["linen", "jute", "cotton"],
        "jute_fabric": ["jute", "linen"]
    },
    ReducedFiberRatio={
        "cotton_fabric": 0.7,
        "jute_fabric": 0.75,
        "linen_fabric": 0.8
    },
    PurchasingCost={
        "cotton": 350,
        "jute": 400,
        "linen": 300
    },
    ProcessingCost={
        "cotton": 100,
        "jute": 80,
        "linen": 150
    },
    Availability={
        "cotton": 1000,
        "jute": 800,
        "linen": 600
    },
    Demand={
        "cotton_fabric": 300,
        "jute_fabric": 400,
        "linen_fabric": 200
    }):
    """
    Solves the textile manufacturing optimization problem.
    """
    # Create a new model
    model = gp.Model("Textile Manufacturing Optimization")

    # Sets
    RawMaterials = list(PurchasingCost.keys())
    Products = list(Demand.keys())

    # Decision variables
    X = model.addVars(RawMaterials, Products, vtype=GRB.CONTINUOUS, name="X")

    # Objective: Minimize total cost
    obj = gp.quicksum(
        (PurchasingCost[r] + ProcessingCost[r]) * X[r, p] for p in Products
        for r in RawMaterials if r in ProducedFrom.get(p, []))
    model.setObjective(obj, GRB.MINIMIZE)

    # Constraint 1: Demand constraint
    for p in Products:
        model.addConstr(
            gp.quicksum(ReducedFiberRatio[p] * X[r, p]
                        for r in ProducedFrom[p]) >= Demand[p], f"Demand_{p}")

    # Constraint 2: Availability constraint
    for r in RawMaterials:
        model.addConstr(
            gp.quicksum(X[r, p] for p in Products
                        if r in ProducedFrom.get(p, [])) <= Availability[r],
            f"Availability_{r}")

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


A national e-commerce company operates three distribution centers in the Metro region. Each center currently stores different quantities of standard and premium products as shown below:

| Distribution Center | Standard Products | Premium Products |
|---------------------|-------------------|------------------|
| Center 1            | 150               | 30               |
| Center 2            | 250               | 100              |
| Center 3            | 300               | 70               |

The company plans to open three retail fulfillment hubs that will serve customers with same-day delivery. Corporate policy requires that each fulfillment hub must maintain exactly 300 products in total inventory, and each hub must stock the same number of premium products to ensure consistent customer experience across all locations. The shipping distances (in kilometers) between distribution centers and fulfillment hubs are shown as follows:

| From / To           | Hub 1 | Hub 2 | Hub 3 |
|---------------------|-------|-------|-------|
| Center 1            | 0     | 12    | 18    |
| Center 2            | 12    | 0     | 15    |
| Center 3            | 18    | 15    | 0     |

What is the minimum total transportation distance required to move the products under the company's inventory requirements?

Set:
1. DistributionCenter
The set of distribution centers, \( D = \{1, 2, ..., DistributionCenterNum\} \)
2. FulfillmentHub
The set of fulfillment hubs, \( H = \{1, 2, ..., FulfillmentHubNum\} \)
3. ProductType
The set of product types, \( P = \{1, 2, ..., ProductTypeNum\} \)

Parameter:
1. DistributionCenterNum
3
2. FulfillmentHubNum
3
3. ProductTypeNum
2
4. HubInventoryRequirement
300
5. StoreStandardProducts
[150, 250, 300]
6. StorePremiumProducts
[30, 100, 70]
7. ShippingDistance
[
    [0, 12, 18],
    [12, 0, 15],
    [18, 15, 0]
]

Decision variable:
1. ShipAmount
Integer variable, \( ShipAmount_{d,h,p} \forall d \in D, h \in H, p \in P \), where \( ShipAmount_{d,h,p} \) is the amount of product type \( p \) shipped from distribution center \( d \) to fulfillment hub \( h \).

Objective:
1. Minimize the total transportation distance required to move the products.
min: \sum_{d \in D} \sum_{h \in H} \sum_{p \in P} (ShippingDistance[d][h] * ShipAmount[d][h][p])

Constraint:
1. Fulfillment hub inventory constraint.
HubInventoryRequirement = \sum_{d \in D} \sum_{p \in P} ShipAmount[d][h][p] \forall h \in H
2. Product type balance constraint.
ShipAmount[d][h][1] \leq StoreStandardProducts[d] \forall d \in D, h \in H

Type:
Integer, Linear, Linear
ILP

import gurobipy as gp
from gurobipy import GRB

def solve_product_distribution(
    HubInventoryRequirement=300,
    StoreStandardProducts=[150, 250, 300],
    StorePremiumProducts=[30, 100, 70],
    ShippingDistance=[[0, 12, 18], [12, 0, 15], [18, 15, 0]]):
    """
    Solves the product distribution optimization problem.
    """
    # Create a new model
    model = gp.Model("Product Distribution Optimization")

    # Parameters and Sets
    DistributionCenterNum = len(StoreStandardProducts)
    FulfillmentHubNum = len(ShippingDistance)
    ProductTypeNum = 2  # 0 for standard, 1 for premium

    DCs = range(DistributionCenterNum)
    Hubs = range(FulfillmentHubNum)
    Products = range(ProductTypeNum)

    # Decision Variables
    ShipAmount = model.addVars(DCs,
                               Hubs,
                               Products,
                               vtype=GRB.INTEGER,
                               name="ShipAmount")

    # Objective: Minimize total transportation distance
    model.setObjective(
        gp.quicksum(ShippingDistance[d][h] * ShipAmount[d, h, p]
                    for d in DCs for h in Hubs for p in Products),
        GRB.MINIMIZE)

    # Constraint 1: Fulfillment hub inventory constraint
    model.addConstrs(
        (gp.quicksum(ShipAmount[d, h, p] for d in DCs for p in Products) ==
         HubInventoryRequirement for h in Hubs),
        name="Hub_Inventory")

    # Constraint 2: Product type balance constraint (standard products)
    # This constraint limits the amount shipped on each individual path
    # based on the total stock at the distribution center.
    model.addConstrs(
        (ShipAmount[d, h, 0] <= StoreStandardProducts[d] for d in DCs
         for h in Hubs),
        name="Standard_Product_Limit")

    # Constraint 3: Product type balance constraint (premium products)
    model.addConstrs(
        (ShipAmount[d, h, 1] <= StorePremiumProducts[d] for d in DCs
         for h in Hubs),
        name="Premium_Product_Limit")

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


A small-scale bakery operates in four towns: Greenville, Springfield, Riverside, and Hilltop. The bakery produces fresh bread, which is then delivered to local stores located in Maplewood, Oakdale, Pineville, Cedarhurst, and Brookside. The number of loaves of bread available at each bakery is provided in Table 1. Each store requires a specific number of loaves to meet daily customer demand, as shown in Table 2. The distances (in kilometers) between the bakeries and stores are given in Table 3.  Assuming the delivery cost (in USD) for each loaf of bread is calculated as the square root of the distance between the origin and destination towns, determine the optimal delivery schedule that minimizes total transportation costs while meeting all store requirements.

Table 1: Bread Availability at Bakeries
| Bakery       | Loaves Available |
|--------------|------------------|
| Greenville   | 200              |
| Springfield  | 150              |
| Riverside    | 250              |
| Hilltop      | 180              |

Table 2: Bread Requirements at Stores
| Store        | Loaves Required |
|--------------|-----------------|
| Maplewood    | 120             |
| Oakdale      | 100             |
| Pineville    | 130             |
| Cedarhurst   | 90              |
| Brookside    | 80              |

Table 3: Distances Between Bakeries and Stores (km)
| From / To    | Maplewood | Oakdale | Pineville | Cedarhurst | Brookside |
|--------------|-----------|---------|-----------|------------|-----------|
| Greenville   | 10        | 15      | 20        | 25         | 30        |
| Springfield  | 12        | 8       | 18        | 22         | 28        |
| Riverside    | 14        | 10      | 16        | 20         | 26        |
| Hilltop      | 16        | 12      | 14        | 18         | 24        |

Set:
1. Bakery
The set of bakeries, \( B = \{1, 2, ..., BakeryNum\} \)
2. Store
The set of stores, \( S = \{1, 2, ..., StoreNum\} \)

Parameter:
1. BakeryNum
4
2. StoreNum
5
3. BakerySupply
[200, 150, 250, 180]
4. StoreDemand
[120, 100, 130, 90, 80]
5. ShippingDistance
[
    [10, 15, 20, 25, 30],
    [12, 8, 18, 22, 28],
    [14, 10, 16, 20, 26],
    [16, 12, 14, 18, 24]
]
6. ShippingCost
[
    [sqrt(10), sqrt(15), sqrt(20), sqrt(25), sqrt(30)],
    [sqrt(12), sqrt(8), sqrt(18), sqrt(22), sqrt(28)],
    [sqrt(14), sqrt(10), sqrt(16), sqrt(20), sqrt(26)],
    [sqrt(16), sqrt(12), sqrt(14), sqrt(18), sqrt(24)]
]

Decision variable:
1. ShipAmount
Integer variable, \( ShipAmount_{b,s} \forall b \in B, s \in S \), where \( ShipAmount_{b,s} \) is the amount of bread shipped from bakery \( b \) to store \( s \).

Objective:
1. Minimize the total transportation cost required to deliver the bread.
min: \sum_{b \in B} \sum_{s \in S} ShippingCost[b][s] * ShipAmount[b][s]

Constraint:
1. Bakery supply constraint.
\sum_{s \in S} ShipAmount[b][s] \leq BakerySupply[b] \forall b \in B
2. Store demand constraint.
\sum_{b \in B} ShipAmount[b][s] = StoreDemand[s] \forall s \in S

Type:
Integer, Linear, Linear
ILP

import gurobipy as gp
from gurobipy import GRB
import math

def solve_bakery_distribution(
    BakerySupply=[200, 150, 250, 180],
    StoreDemand=[120, 100, 130, 90, 80],
    ShippingDistance=[[10, 15, 20, 25, 30], [12, 8, 18, 22, 28],
                      [14, 10, 16, 20, 26], [16, 12, 14, 18, 24]]):
    """
    Solves the bakery distribution (transportation) problem.
    """
    # --- 1. Model Creation ---
    model = gp.Model("Bakery Distribution Optimization")

    # --- 2. Parameters & Sets ---
    BakeryNum = len(BakerySupply)
    StoreNum = len(StoreDemand)
    Bakeries = range(BakeryNum)
    Stores = range(StoreNum)

    # Calculate shipping costs with square roots
    ShippingCost = [[math.sqrt(dist) for dist in row]
                    for row in ShippingDistance]

    # --- 3. Decision Variables ---
    ShipAmount = model.addVars(Bakeries,
                               Stores,
                               vtype=GRB.INTEGER,
                               name="ShipAmount")

    # --- 4. Objective Function ---
    # Minimize total transportation cost
    model.setObjective(
        gp.quicksum(ShippingCost[b][s] * ShipAmount[b, s] for b in Bakeries
                    for s in Stores), GRB.MINIMIZE)

    # --- 5. Constraints ---
    # Constraint 1: Bakery supply constraint
    model.addConstrs(
        (gp.quicksum(ShipAmount[b, s] for s in Stores) <= BakerySupply[b]
         for b in Bakeries),
        name="BakerySupply")

    # Constraint 2: Store demand constraint
    model.addConstrs(
        (gp.quicksum(ShipAmount[b, s] for b in Bakeries) == StoreDemand[s]
         for s in Stores),
        name="StoreDemand")

    # --- 6. Solve the Model ---
    model.optimize()

    # --- 7. Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

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


A company produces seven types of food containers, ranging in volume from 17 to 33 liters. The demand and size of each container are given in Table as follows:

| Container Type | Volume (liters) | Demand (units) |
|----------------|-----------------|----------------|
| 1              | 17              | 500            |
| 2              | 19              | 400            |
| 3              | 21              | 300            |
| 4              | 23              | 250            |
| 5              | 25              | 200            |
| 6              | 29              | 150            |
| 7              | 33              | 100            |

The variable cost (in dollars) of producing each container is equal to its volume. A fixed cost of $1,000 is incurred to produce any of a particular container type. If the company desires, demand for a container may be satisfied by a container of larger size. Formulate and solve a shortest-path problem whose solution will minimize the cost of meeting the demand for food containers.

Set:
1. Containers
The set of container types, \( C = \{1, 2, ..., 7\} \)

Parameter:
1. Volume
The volume of each container type (in liters):
[17, 19, 21, 23, 25, 29, 33]

2. Demand
The demand for each container type (in units):
[500, 400, 300, 250, 200, 150, 100]

3. VariableCost
The variable cost of producing each container type (equals its volume in dollars):
[17, 19, 21, 23, 25, 29, 33]

4. FixedCost
The fixed cost of producing any container type:
1000

Decision variable:
1. Produce
Binary variable, \( Produce_j \forall j \in C \), where \( Produce_j = 1 \) if container type j is produced, 0 otherwise.

2. Assign
Integer variable, \( Assign_{i,j} \forall i,j \in C, i \leq j \), where \( Assign_{i,j} \) is the number of demand for container type i satisfied by container type j.

Objective:
1. Minimize the total cost of production.
min: \sum_{j \in C} (FixedCost * Produce_j + VariableCost[j] * \sum_{i \in C, i \leq j} Assign_{i,j})

Constraint:
1. Demand satisfaction constraint.
\sum_{j \in C, j \geq i} Assign_{i,j} = Demand[i] \forall i \in C

2. Production activation constraint.
\sum_{i \in C, i \leq j} Assign_{i,j} \leq M * Produce_j \forall j \in C
(where M is a sufficiently large number, such as the sum of all demands)

Type:
Mixed-integer, Linear, Linear
MIP

import gurobipy as gp
from gurobipy import GRB

def solve_container_optimization(
    volumes=[17, 19, 21, 23, 25, 29, 33],
    demands=[500, 400, 300, 250, 200, 150, 100],
    fixed_cost=1000):
    """
    Solves the container production and assignment optimization problem.
    """
    # --- 1. Model Creation ---
    model = gp.Model("Container_Optimization")

    # --- 2. Parameters & Sets ---
    container_types = range(len(volumes))
    variable_costs = volumes  # Variable cost equals volume

    # --- 3. Decision Variables ---
    # produce[j] = 1 if container type j is produced, 0 otherwise
    produce = model.addVars(container_types, vtype=GRB.BINARY, name="Produce")

    # assign[i, j] = number of units of demand type i satisfied by container type j
    assign_keys = [(i, j) for i in container_types for j in container_types
                   if j >= i]
    assign = model.addVars(assign_keys, vtype=GRB.INTEGER, name="Assign")

    # --- 4. Objective Function ---
    # Minimize total cost (fixed production costs + variable assignment costs)
    total_cost = gp.quicksum(fixed_cost * produce[j]
                             for j in container_types) + \
                 gp.quicksum(variable_costs[j] * assign[i, j]
                             for i, j in assign_keys)
    model.setObjective(total_cost, GRB.MINIMIZE)

    # --- 5. Constraints ---
    # Demand satisfaction: All demand for type i must be met.
    model.addConstrs(
        (assign.sum(i, '*') == demands[i] for i in container_types),
        name="Demand")

    # Production activation: If any containers of type j are used, we must incur the fixed cost.
    M = sum(demands)  # Big-M: an upper bound on the total number of containers of one type
    model.addConstrs(
        (assign.sum('*', j) <= M * produce[j] for j in container_types),
        name="Activation")

    # --- 6. Solve the Model ---
    model.optimize()

    # --- 7. Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

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


LogiTrans operates two distribution centers to manage the shipment of three types of goods to various regional hubs. Each distribution center can be staffed by up to seven logistics coordinators at a time. Coordinators are paid $500 per week at distribution center 1 and $900 per week at distribution center 2. Activating a distribution center for a week incurs a fixed cost of $1,000 for center 1 and $2,000 for center 2. During a week at a distribution center, each coordinator can process the number of shipments of each type of goods as shown in Table as follows:

| Goods Type \ Distribution Center | Center 1 | Center 2 |
|----------------------------------|----------|----------|
| Type 1                           | 20       | 25       |
| Type 2                           | 18       | 22       |
| Type 3                           | 15       | 20       |

Each week, LogiTrans must ensure that at least 120 shipments of goods type 1, at least 150 shipments of goods type 2, and at least 200 shipments of goods type 3 are processed. How to minimize the total cost of meeting weekly shipment demands?

Set:
1. Center
The set of distribution centers, \( C = \{1, 2\} \)
2. GoodsType
The set of goods types, \( G = \{1, 2, 3\} \)

Parameter:
1. FixedCost
[1000, 2000]
2. StaffCost
[500, 900]
3. MaxStaffNum
[7, 7]
4. ProcessRate
[
    [20, 25],
    [18, 22],
    [15, 20]
]
5. Demand
[120, 150, 200]

Decision variable:
1. ActivatingCenter
Binary variable, \( ActivatingCenter[c] \forall c \in C \), where \( ActivatingCenter[c] = 1 \) if distribution center \( c \) is activated, and 0 otherwise.
2. StaffNum
Integer variable, \( StaffNum[c] \forall c \in C \), where \( StaffNum[c] \) is the number of staff at distribution center \( c \).

Objective:
1. Minimize the total cost of activating distribution centers and paying staff.
min: \sum_{c \in C} (FixedCost[c] * ActivatingCenter[c] + StaffCost[c] * StaffNum[c])

Constraint:
1. Staff number constraint.
StaffNum[c] \leq MaxStaffNum[c] * ActivatingCenter[c] \forall c \in C
2. Demand satisfaction constraint.
\sum_{c \in C} ProcessRate[g][c] * StaffNum[c] \geq Demand[g] \forall g \in G
3. Staff number non-negativity constraint.
StaffNum[c] \geq 0 \forall c \in C

Type:
Integer, Linear, Linear
ILP

import gurobipy as gp
from gurobipy import GRB

def solve_staffing_optimization(
    FixedCost=[1000, 2000],
    StaffCost=[500, 900],
    MaxStaffNum=[7, 7],
    ProcessRate=[[20, 25], [18, 22], [15, 20]],
    Demand=[120, 150, 200]):
    """
    Solves the distribution center staffing optimization problem.
    """
    # --- 1. Model Creation ---
    model = gp.Model("Distribution Center Staffing")

    # --- 2. Parameters & Sets ---
    Centers = range(len(FixedCost))
    GoodsTypes = range(len(Demand))

    # --- 3. Decision Variables ---
    # ActivatingCenter[c] = 1 if center c is activated, 0 otherwise
    ActivatingCenter = model.addVars(Centers,
                                     vtype=GRB.BINARY,
                                     name="ActivatingCenter")

    # StaffNum[c] = number of staff at center c
    StaffNum = model.addVars(Centers, vtype=GRB.INTEGER, name="StaffNum")

    # --- 4. Objective Function ---
    # Minimize total cost (fixed costs + staff costs)
    obj = gp.quicksum(FixedCost[c] * ActivatingCenter[c] +
                      StaffCost[c] * StaffNum[c] for c in Centers)
    model.setObjective(obj, GRB.MINIMIZE)

    # --- 5. Constraints ---
    # Constraint 1: Staff number constraint
    model.addConstrs(
        (StaffNum[c] <= MaxStaffNum[c] * ActivatingCenter[c] for c in Centers),
        name="StaffLimit")

    # Constraint 2: Demand satisfaction constraint
    model.addConstrs(
        (gp.quicksum(ProcessRate[g][c] * StaffNum[c] for c in Centers) >=
         Demand[g] for g in GoodsTypes),
        name="DemandSatisfaction")

    # --- 6. Solve the Model ---
    model.optimize()

    # --- 7. Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.ObjVal}
    else:
        return {"status": f"{model.status}"}

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


Global Freight Solutions (GFS), a logistics company, needs to transport goods across three regions using services from three shipping providers. Each provider offers different cost structures and service mixes for large, medium, and small shipments, as detailed in Table as follows:

| Supplier | Cost Per Shipment ($) | Percent Large | Percent Medium | Percent Small |
|----------|-----------------------|---------------|----------------|---------------|
| 1        | 5.2                   | 45            | 35             | 20            |
| 2        | 4.7                   | 30            | 45             | 25            |
| 3        | 3.5                   | 15            | 20             | 65            |

GFS must fulfill a minimum monthly demand of at least 500 large shipments, 300 medium shipments, and 300 small shipments while minimizing total costs. Additionally, due to capacity constraints, no more than 700 shipments can be contracted from any single provider each month. Determine what the lowest possible cost is.

Set:
1. Supplier
The set of suppliers, \( S = \{1, 2, 3\} \)
2. ShipmentType
The set of shipment types, \( T = \{1, 2, 3\} \)  # 1: large, 2: medium, 3: small

Parameter:
1. CostPerShipment
[5.2, 4.7, 3.5]
2. Percent
[[45, 35, 20], [30, 45, 25], [15, 20, 65]]
3. Demand
[500, 300, 300]
4. MaxShipment
[700, 700, 700]

Decision variable:
1. ShipmentNum
Integer variable, \( ShipmentNum[s] \forall s \in S \), where \( ShipmentNum[s] \) is the number of shipments contracted from supplier \( s \).

Objective:
1. Minimize the total cost of shipments.
min: \sum_{s \in S} CostPerShipment[s] * ShipmentNum[s]

Constraint:
1. Shipment number constraint.
ShipmentNum[s] \leq MaxShipment[s] \forall s \in S
2. Demand satisfaction constraint.
\sum_{s \in S} Percent[s][g] * ShipmentNum[s] \geq Demand[g] \forall g \in T
3. Shipment number non-negativity constraint.
ShipmentNum[s] \geq 0 \forall s \in S

Type:
Integer, Linear, Linear
ILP

import gurobipy as gp
from gurobipy import GRB

def solve_supplier_shipment_optimization(
    CostPerShipment=[5.2, 4.7, 3.5],
    Percent=[
        [45, 35, 20],
        [30, 45, 25],
        [15, 20, 65]
    ],
    Demand=[500, 300, 300],
    MaxShipment=[700, 700, 700]
):
    # Create a new model
    model = gp.Model("Supplier Shipment Optimization")

    # Sets
    Suppliers = range(len(CostPerShipment))
    ShipmentTypes = range(len(Demand))

    # Decision Variables
    ShipmentNum = model.addVars(Suppliers, vtype=GRB.INTEGER, name="ShipmentNum")

    # Objective: Minimize total cost of shipments
    obj = gp.quicksum(CostPerShipment[s] * ShipmentNum[s] for s in Suppliers)
    model.setObjective(obj, GRB.MINIMIZE)

    # Constraint 1: Shipment number constraint (cannot exceed maximum)
    for s in Suppliers:
        model.addConstr(
            ShipmentNum[s] <= MaxShipment[s],
            f"MaxShipment_{s+1}"
        )

    # Constraint 2: Demand satisfaction constraint
    for t in ShipmentTypes:
        # Convert percentage to decimal
        model.addConstr(
            gp.quicksum((Percent[s][t] / 100) * ShipmentNum[s] for s in Suppliers) >= Demand[t],
            f"DemandSatisfaction_{t+1}"
        )

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


A manufacturing company operates three production facilities (1, 2, and 3) that produce two types of components (X and Y). Each facility generates waste during production, which can be treated to reduce environmental impact. Treating the waste incurs a cost and reduces the amount of pollutants emitted. It costs $12 to treat a ton of waste from facility 1, and each ton treated reduces the amount of pollutant X by 0.15 ton and the amount of pollutant Y by 0.35 ton. It costs $8 to treat a ton of waste from facility 2, and each ton treated reduces the amount of pollutant X by 0.25 ton and the amount of pollutant Y by 0.20 ton. It costs $18 to treat a ton of waste from facility 3, and each ton treated reduces the amount of pollutant X by 0.30 ton and the amount of pollutant Y by 0.40 ton. The company aims to reduce the amount of pollutant X by at least 25 tons and the amount of pollutant Y by at least 35 tons. How to minimize the total cost of treating waste while meeting the pollution reduction targets?

Set:
1. facility
The set of production facilities, \( F = \{1, 2, 3\} \)
2. pollutant
The set of pollutants, \( P = \{1, 2\} \)  # 1: pollutant X, 2: pollutant Y

Parameter:
1. TreatmentCost
[12, 8, 18]
2. PollutantReduction
[
    [0.15, 0.35],  # facility 1
    [0.25, 0.20],  # facility 2
    [0.30, 0.40]   # facility 3
]
3. PollutionTarget
[25, 35]

Decision variable:
1. TreatmentAmount
Continuous variable, \( TreatmentAmount[f] \forall f \in F \), where \( TreatmentAmount[f] \) is the amount of waste treated at facility \( f \).

Objective:
1. Minimize the total cost of treating waste.
min: \sum_{f \in F} TreatmentCost[f] * TreatmentAmount[f]

Constraint:
1. Pollution reduction constraint.
PollutantReduction[f][p] * TreatmentAmount[f] \geq PollutionTarget[p] \forall p \in P, f \in F
2. Treatment amount non-negativity constraint.
TreatmentAmount[f] \geq 0 \forall f \in F

Type:
Continuous, Linear, Linear
LP

import gurobipy as gp
from gurobipy import GRB

def solve_waste_treatment_optimization(
    TreatmentCost=[12, 8, 18],
    PollutantReduction=[
        [0.15, 0.35],
        [0.25, 0.20],
        [0.30, 0.40]
    ],
    PollutionTarget=[25, 35]
):
    # Create a new model
    model = gp.Model("Waste Treatment Optimization")

    # Sets
    Facilities = range(len(TreatmentCost))
    Pollutants = range(len(PollutionTarget))

    # Decision Variables
    TreatmentAmount = model.addVars(Facilities, vtype=GRB.CONTINUOUS, name="TreatmentAmount")

    # Objective: Minimize total treatment cost
    obj = gp.quicksum(TreatmentCost[f] * TreatmentAmount[f] for f in Facilities)
    model.setObjective(obj, GRB.MINIMIZE)

    # Constraint 1: Pollution reduction constraint
    for p in Pollutants:
        model.addConstr(
            gp.quicksum(PollutantReduction[f][p] * TreatmentAmount[f] for f in Facilities) >= PollutionTarget[p],
            f"PollutionReduction_{p+1}"
        )

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


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.

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

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)


Green Energy Solutions specializes in producing two types of renewable fuel: bioethanol and biodiesel. The company has 8,000 liters of feedstock A and 12,000 liters of feedstock B in stock. The quality rating of each feedstock is as follows: feedstock A—12; feedstock B—6. Bioethanol must have an average quality rating of at least 10, while biodiesel must have an average quality rating of at least 8. The demand for each product is driven by marketing efforts. Each dollar spent on marketing bioethanol generates 8 liters of demand, and each dollar spent on marketing biodiesel generates 15 liters of demand. Bioethanol is sold for $30 per liter, and biodiesel is sold for $25 per liter. Help Green Energy Solutions maximize its profit. Assume that the company cannot purchase additional feedstock of either type.

Set:
1. Fuel
The set of fuels, \( F = \{1, 2\} \)  # 1: gasoline, 2: diesel
2. Feedstock
The set of feedstocks, \( S = \{1, 2\} \)  # 1: feedstock A, 2: feedstock B

Parameter:
1. Inventory
[8000, 12000]
2. QualityRating
[12, 6]
3. Revenue
[30, 25]
4. DemandperMarketingDollar
[8, 15]
5. MinQualityRating
[10, 8]

Decision variable:
1. ProductionAmount
Continuous variable, \( ProductionAmount[f][s] \forall f \in F, s \in S \), where \( ProductionAmount[f][s] \) is the amount of fuel \( f \) produced from feedstock \( s \).
2. MarketingBudget
Continuous variable, \( MarketingBudget[f] \forall f \in F \), where \( MarketingBudget[f] \) is the marketing budget for fuel \( f \).

Objective:
1. Maximize the profit from selling fuels.
max: \sum_{f \in F} Revenue[f] * \sum_{s \in S} ProductionAmount[f][s] - \sum_{f \in F} MarketingBudget[f]

Constraint:
1. Inventory constraint.
\sum_{f \in F} ProductionAmount[f][s] \leq Inventory[s] \forall s \in S
2. Quality rating constraint.
\sum_{s \in S} QualityRating[s] * ProductionAmount[f][s] \geq MinQualityRating[f] * \sum_{s \in S} ProductionAmount[f][s] \forall f \in F
3. Demand constraint.
DemandperMarketingDollar[f] * MarketingBudget[f] \geq \sum_{s \in S} ProductionAmount[f][s] \forall f \in F
4. Non-negativity constraint.
ProductionAmount[f][s] \geq 0 \forall f \in F, s \in S
MarketingBudget[f] \geq 0 \forall f \in F

Type:
Continuous, Linear, Linear
LP

import gurobipy as gp
from gurobipy import GRB

def solve_fuel_production_optimization(
    Inventory=[8000, 12000],
    QualityRating=[12, 6],
    Revenue=[30, 25],
    DemandperMarketingDollar=[8, 15],
    MinQualityRating=[10, 8]
):
    """
    Models and solves the fuel production and marketing optimization problem.
    """
    # Create a new model
    model = gp.Model("Fuel Production and Marketing Optimization")

    # Sets
    Fuels = range(len(Revenue))
    Feedstocks = range(len(Inventory))

    # Decision Variables
    # Amount of fuel f produced from feedstock s
    ProductionAmount = {}
    for f in Fuels:
        for s in Feedstocks:
            ProductionAmount[f, s] = model.addVar(
                vtype=GRB.CONTINUOUS,
                name=f"ProductionAmount_{f+1}_{s+1}"
            )

    # Marketing budget for each fuel
    MarketingBudget = model.addVars(
        Fuels,
        vtype=GRB.CONTINUOUS,
        name="MarketingBudget"
    )

    # Objective: Maximize profit (revenue - marketing costs)
    obj = gp.quicksum(
        Revenue[f] * gp.quicksum(ProductionAmount[f, s] for s in Feedstocks)
        for f in Fuels
    ) - gp.quicksum(MarketingBudget[f] for f in Fuels)

    model.setObjective(obj, GRB.MAXIMIZE)

    # Constraint 1: Inventory constraint
    for s in Feedstocks:
        model.addConstr(
            gp.quicksum(ProductionAmount[f, s] for f in Fuels) <= Inventory[s],
            f"Inventory_{s+1}"
        )

    # Constraint 2: Quality rating constraint
    for f in Fuels:
        model.addConstr(
            gp.quicksum(QualityRating[s] * ProductionAmount[f, s] for s in Feedstocks) >=
            MinQualityRating[f] * gp.quicksum(ProductionAmount[f, s] for s in Feedstocks),
            f"Quality_{f+1}"
        )

    # Constraint 3: Demand constraint
    for f in Fuels:
        model.addConstr(
            DemandperMarketingDollar[f] * MarketingBudget[f] >=
            gp.quicksum(ProductionAmount[f, s] for s in Feedstocks),
            f"Demand_{f+1}"
        )

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


FreshFoods operates three farms (Farm A, Farm B, and Farm C) to grow and supply fresh produce to four grocery stores (Store 1, Store 2, Store 3, and Store 4). The production cost, yield capacity, water usage (per ton), and fertilizer usage (per ton) for each farm are given in the table below:

| Farm | Production Cost ($/ton) | Yield Capacity (tons) | Water Usage (m³/ton) | Fertilizer Usage (kg/ton) |
| ---- | ----------------------- | --------------------- | -------------------- | ------------------------- |
| A    | 100                     | 500                   | 6                    | 5                         |
| B    | 120                     | 400                   | 8                    | 4                         |
| C    | 150                     | 600                   | 6                    | 3                         |

The demand (in tons) for each grocery store is as follows:

| Store 1 | Store 2 | Store 3 | Store 4 |
| ------- | ------- | ------- | ------- |
| 200     | 300     | 400     | 500     |

The transportation cost (in $/ton) from each farm to each grocery store is given in the table below:

| Farm | Store 1 | Store 2 | Store 3 | Store 4 |
| ---- | ------- | ------- | ------- | ------- |
| A    | 20      | 25      | 30      | 15      |
| B    | 22      | 26      | 32      | 18      |
| C    | 18      | 24      | 28      | 14      |

To meet sustainability goals, the total produce shipped must use no more than 7 m³ of water per ton and no more than 4 kg of fertilizer per ton. How to minimize the total cost while satisfying the demand and sustainability constraints?

Set:
1. Farm
The set of farms, \( F = \{1, 2, 3\} \)  # 1: Farm A, 2: Farm B, 3: Farm C
2. Store
The set of stores, \( S = \{1, 2, 3, 4\} \)  # 1: Store 1, 2: Store 2, 3: Store 3, 4: Store 4

Parameter:
1. ProductionCost
[100, 120, 150]
2. YieldCapacity
[500, 400, 600]
3. WaterUsage
[6, 8, 6]
4. FertilizerUsage
[5, 4, 3]
5. Demand
[200, 300, 400, 500]
6. TransportationCost
[[20, 25, 30, 15],
 [22, 26, 32, 18],
 [18, 24, 28, 14]]
7. MaxWaterUsageperTon
7
8. MaxFertilizerUsageperTon
4

Decision variable:
1. AmountProduced
Continuous variable, \( AmountProduced[f][s] \forall f \in F, s \in S \), where \( AmountProduced[f][s] \) is the amount of produce shipped from farm \( f \) to store \( s \).

Objective:
1. Minimize the total cost of production and transportation.
min: \sum_{f \in F} ProductionCost[f] * \sum_{s \in S} AmountProduced[f][s] + \sum_{f \in F} \sum_{s \in S} TransportationCost[f][s] * AmountProduced[f][s]

Constraint:
1. Demand constraint.
\sum_{f \in F} AmountProduced[f][s] \geq Demand[s] \forall s \in S
2. Yield capacity constraint.
\sum_{s \in S} AmountProduced[f][s] \leq YieldCapacity[f] \forall f \in F
3. Water usage constraint.
\sum_{f \in F}\sum_{s \in S} WaterUsage[f] * AmountProduced[f][s] \leq MaxWaterUsageperTon * \sum_{s \in S}\sum_{f \in F} AmountProduced[f][s]
4. Fertilizer usage constraint.
\sum_{f \in F}\sum_{s \in S} FertilizerUsage[f] * AmountProduced[f][s] \leq MaxFertilizerUsageperTon * \sum_{s \in S}\sum_{f \in F} AmountProduced[f][s]
5. Non-negativity constraint.
AmountProduced[f][s] \geq 0 \forall f \in F, s \in S

Type:
Continuous, Linear, Linear
LP

import gurobipy as gp
from gurobipy import GRB

def solve_farm_production_optimization(
    ProductionCost=[100, 120, 150],
    YieldCapacity=[500, 400, 600],
    WaterUsage=[6, 8, 6],
    FertilizerUsage=[5, 4, 3],
    Demand=[200, 300, 400, 500],
    TransportationCost=[
        [20, 25, 30, 15],
        [22, 26, 32, 18],
        [18, 24, 28, 14]
    ],
    MaxWaterUsageperTon=7,
    MaxFertilizerUsageperTon=4
):
    """
    Models and solves the farm production and distribution optimization problem.
    """
    # Create a new model
    model = gp.Model("Farm Production and Distribution Optimization")

    # Sets
    Farms = range(len(ProductionCost))
    Stores = range(len(Demand))

    # Decision Variables
    AmountProduced = {}
    for f in Farms:
        for s in Stores:
            AmountProduced[f, s] = model.addVar(
                vtype=GRB.CONTINUOUS,
                name=f"AmountProduced_{f+1}_{s+1}"
            )

    # Objective: Minimize total production and transportation costs
    obj = gp.quicksum(
        ProductionCost[f] * AmountProduced[f, s] + TransportationCost[f][s] * AmountProduced[f, s]
        for f in Farms
        for s in Stores
    )
    model.setObjective(obj, GRB.MINIMIZE)

    # Constraint 1: Demand constraint - each store's demand must be met
    for s in Stores:
        model.addConstr(
            gp.quicksum(AmountProduced[f, s] for f in Farms) >= Demand[s],
            f"Demand_{s+1}"
        )

    # Constraint 2: Yield capacity constraint - each farm's production cannot exceed capacity
    for f in Farms:
        model.addConstr(
            gp.quicksum(AmountProduced[f, s] for s in Stores) <= YieldCapacity[f],
            f"YieldCapacity_{f+1}"
        )

    # Constraint 3: Water usage constraint - average water usage cannot exceed maximum
    total_production = gp.quicksum(AmountProduced[f, s] for f in Farms for s in Stores)
    total_water_usage = gp.quicksum(WaterUsage[f] * AmountProduced[f, s] for f in Farms for s in Stores)
    model.addConstr(
        total_water_usage <= MaxWaterUsageperTon * total_production,
        "WaterUsage"
    )

    # Constraint 4: Fertilizer usage constraint - average fertilizer usage cannot exceed maximum
    total_fertilizer_usage = gp.quicksum(FertilizerUsage[f] * AmountProduced[f, s] for f in Farms for s in Stores)
    model.addConstr(
        total_fertilizer_usage <= MaxFertilizerUsageperTon * total_production,
        "FertilizerUsage"
    )

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