A logistics company needs to optimize the loading of 5 different types of cargo items (A, B, C, D, E) onto a single truck with a maximum weight capacity of 2000 kg. Each item has a base profit value and weight, but certain item combinations generate additional synergy profits when transported together (e.g., perishable items with refrigeration equipment). The item characteristics and synergy matrix are given below:

Item Data:
| Item | Weight (kg) | Base Profit ($) |
|------|------------|----------------|
| A    | 400        | 120            |
| B    | 600        | 180            |
| C    | 300        | 90             |
| D    | 500        | 150            |
| E    | 700        | 210            |

Synergy Profit Matrix ($):
|   | A  | B  | C  | D  | E  |
|---|---|---|---|---|---|
| A | 0  | 25 | 15 | 0  | -10|
| B | 25 | 0  | 30 | 20 | 0  |
| C | 15 | 30 | 0  | 10 | 5  |
| D | 0  | 20 | 10 | 0  | 15 |
| E |-10 | 0  | 5  | 15 | 0  |

Negative values indicate incompatibility costs (e.g., item A and E require separate storage space).

Problem type: NLP (Toy: 5 variables, 1 constraints, 5 nonzeros)

Mathematical model:
Set:
1. Items
The set of cargo items, \( I = \{A, B, C, D, E\} \)

Parameter:
1. Weight
The weight of each item \( w_i \) where \( i \in I \):
\( w = \{A:400, B:600, C:300, D:500, E:700\} \)
2. BaseProfit
The base profit of each item \( c_i \) where \( i \in I \):
\( c = \{A:120, B:180, C:90, D:150, E:210\} \)
3. Synergy
The synergy profit matrix \( Q_{ij} \) for all \( i,j \in I \) (as shown in the table)
4. Capacity
The truck's maximum weight capacity \( W = 2000 \)

Decision variable:
1. Selection
Binary variable \( x_i \in \{0, 1\} \) for all \( i \in I \), where \( x_i = 1 \) if item \( i \) is selected, and \( x_i = 0 \) otherwise

Objective:
Maximize total profit including base and synergy values
max: \( \sum_{i \in I} \sum_{j \in I} Q_{ij} x_i x_j + \sum_{i \in I} c_i x_i \)

Constraint:
1. Weight capacity limit
\( \sum_{i \in I} w_i x_i \leq W \)

Type:
Binary, Quadratic, Linear
NLP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB


def solve_logistics_knapsack(
    items=['A', 'B', 'C', 'D', 'E'],
    weights={'A': 400, 'B': 600, 'C': 300, 'D': 500, 'E': 700},
    base_profits={'A': 120, 'B': 180, 'C': 90, 'D': 150, 'E': 210},
    synergy={
        'A': {'A': 0, 'B': 25, 'C': 15, 'D': 0, 'E': -10},
        'B': {'A': 25, 'B': 0, 'C': 30, 'D': 20, 'E': 0},
        'C': {'A': 15, 'B': 30, 'C': 0, 'D': 10, 'E': 5},
        'D': {'A': 0, 'B': 20, 'C': 10, 'D': 0, 'E': 15},
        'E': {'A': -10, 'B': 0, 'C': 5, 'D': 15, 'E': 0}
    },
    truck_capacity=2000
):
    """
    Solves the logistics knapsack problem.
    """
    # Create a new model
    model = gp.Model("Logistics_Knapsack")

    # Create binary decision variables for each item
    x = model.addVars(items, vtype=GRB.BINARY, name="select")

    # Set objective: maximize total profit (base + synergy)
    base_profit = gp.quicksum(base_profits[i] * x[i] for i in items)
    synergy_profit = gp.quicksum(synergy[i][j] * x[i] * x[j] for i in items for j in items if i <= j)
    model.setObjective(base_profit + synergy_profit, GRB.MAXIMIZE)

    # Add weight capacity constraint
    model.addConstr(gp.quicksum(weights[i] * x[i] for i in items) <= truck_capacity, "weight_limit")

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


Solution status: OPTIMAL
Optimal objective value: 665