## Problem Description and Formulation

The coffee shop has two types of coffees: large and small. The resources required for each type are as follows:
- Large coffee: 12 units of coffee beans, 20 units of milk, and a profit of $5.
- Small coffee: 8 units of coffee beans, 15 units of milk, and a profit of $3.

The shop has 1000 units of coffee beans and 1500 units of milk available. The goal is to determine how many large and small coffees the shop should make to maximize profit.

## Mathematical Formulation

Let \(L\) be the number of large coffees and \(S\) be the number of small coffees.

The objective function to maximize profit is:
\[ \text{Maximize:} \quad 5L + 3S \]

Subject to the constraints:
\[ 12L + 8S \leq 1000 \] (coffee beans constraint)
\[ 20L + 15S \leq 1500 \] (milk constraint)
\[ L \geq 0 \] (non-negativity constraint for large coffees)
\[ S \geq 0 \] (non-negativity constraint for small coffees)

## Gurobi Code

```python
import gurobi

def coffee_shop_optimization():
    # Create a new model
    model = gurobi.Model()

    # Define variables
    L = model.addVar(name="Large_Coffee", lb=0, ub=gurobi.GRB.INFINITY)
    S = model.addVar(name="Small_Coffee", lb=0, ub=gurobi.GRB.INFINITY)

    # Objective function: Maximize profit
    model.setObjective(5*L + 3*S, gurobi.GRB.MAXIMIZE)

    # Constraints
    model.addConstr(12*L + 8*S <= 1000, name="Coffee_Beans_Constraint")
    model.addConstr(20*L + 15*S <= 1500, name="Milk_Constraint")

    # Optimize
    model.optimize()

    # Print solution
    if model.status == gurobi.GRB.OPTIMAL:
        print(f"Optimal solution: Large Coffees = {L.varValue}, Small Coffees = {S.varValue}")
        print(f"Max Profit: ${model.objVal:.2f}")
    else:
        print("No optimal solution found.")

if __name__ == "__main__":
    coffee_shop_optimization()
```