Figuring out a Balanced Diet at Wendy's with Python & PuLP
Tags: python, programming, tutorial, database
Published: 2020-11-13 14:25:09

## Overview
We'll use Python to solve a linear programming problem of **finding what items to eat at Wendy's for a nutritional and balanced diet**. You can expand this project to a food and diet app, or apply it to solve [other LP problems](http://home.ubalt.edu/ntsbarsh/opre640a/partviii.htm#rCarPr). 

## Linear Programming
Linear Programming (LP) is used to generate optimal solutions that satisfy several constraints at once. One of the problems it can solve is **the diet problem**. 

> The goal of the diet problem is to select a set of foods that will satisfy a set of daily nutritional requirement at minimum cost. *- NEOS Guide*

## Prequisites

- Python 3
- PuLP 

```bash
pip install pulp
```
PuLP is a LP modeler written in Python. For this project, we don't need to write any LP algorithms. We simply define our problem, send the data, and PuLP's API will do the rest.

## Breaking It Down 

Let's break down the LP problem to three parts: the **objective function**, the **constraints**, and our working **data**.

- **Objective Function**: Minimize calorie intake
- **Constraints**: Satisfy nutritional requirements
- **Data**: Nutritional information of Wendy's menu

## Declaring Problem and Objective

```python
# Declare problem and whether the objective function is to maximize or minimize
problem = pulp.LpProblem(name="Calories",sense=pulp.LpMinimize)

# Declare menu list and nutritional information of each item
menu_list = ["single_cheeseburger", "double_cheeseburger", "small_fries", "small_cola"]
calories = [580, 820, 310, 160]
protein = [30, 48, 4, 0]
carbohydrates = [42, 42, 40, 55]
sodium = [1220, 1510, 330, 0]

# Declare nutritional requirements
nutrition_requirement_dict ={
    "Calories": 2700.0, 
    "Protein" : 65.0, # g
    "Carbohydrates" : 330.6, # g
    "Sodium" : 7500, # mg
}

# Declare LpVariables for each menu item  
LpVariableList = [pulp.LpVariable('{}'.format(x), cat='Integer', lowBound=0) for item in menu_list]

# Declare objective: to minimize calories
problem += pulp.lpDot(calories, LpVariableList)
```

We first declare our **LpProblem** *problem* and **LpVariables** for each menu item. In the LpProblem declaration we use **LpMinimize** for a minimization objective. To declare the objective, we pass *calories* as the first argument for the value to minimize, and the LpVariable list as the second argument.

## Declaring Constraints
```python
# Declare constraints
# Assume the daily energy requirements of a man aged 30-49 with low activity 
problem += pulp.lpDot(protein, LpVariableList) >= nutrition_requirement_dict["Protein"]
problem += pulp.lpDot(carbohydrates, LpVariableList) >= nutrition_requirement_dict["Carbohydrates"]
problem += pulp.lpDot(sodium, LpVariableList) <= nutrition_requirement_dict["Sodium"]
```
We call **lpDot()** with each of the nutrient that we would like to set a constraint on and evaluate it to our nutrient requirement to create a boolean expression. **Whenever we add a boolean expression to our problem it becomes an additional constraint.** In this example, we want to satisfy the requirements of protein and carbohydrates but limit sodium intake.

## Solving
When we are done defining our problem and constraints, we can simply call **problem.solve()**

```python
# Solve
status = problem.solve()
print(pulp.LpStatus[status])

# Output Result
print("Result")
for menu_item in LpVariableList:
  print(str(menu_item) + " × "+ str(int(menu_item.value())) )

for nutrient_name, nutrient_value in {"Calories": calories, "Protein": protein, "Carbohydrates": carbohydrates, "Sodium": sodium}.items():
		print("*{}: {} Reference: {}".format(nutrient_name, str(round(pulp.lpDot(nutrient_value, LpVariableList).value())), nutrition_requirement_dict[nutrient_name]))
```
For the output, we first iterate through *LpVariableList* and show the value of each *menu_item*, which is the number of times they are chosen for the solution. Then we iterate through our nutrients and show their quantity by picking the above menu items.

{% replit @yuispl2/Wendys-Diet-Problem %}

```bash
Result
single_cheeseburger × 1
double_cheeseburger × 1
small_fries × 0
small_cola × 5
*Calories: 2200 Reference: 2700.0
*Protein: 78 Reference: 65.0
*Carbohydrates: 359 Reference: 330.6
*Sodium: 2730 Reference: 7500
```

Let's look at how well it did. One of each burger is selected with 5 cups of small cola. The final calorie count is 2200 which is way below 2700. Protein and carbohydrates also look good with a healthy amount of sodium. 

**Could this be the perfect meal?**

Not yet. 5 cups of cola screams excess sugar and we haven't added vitamins and other nutrients to the equation. We will expand on this project in our next post. 

*Note: I'm still working on the second part of this post. You can follow me if you want to stay posted.*

## Summary
- What Linear Programming (LP) is and what problems it solves
- Defining a LP problem, an objective, and constraints
- Implementing LP with PuLP

Let me know if you found this helpful. :)

---

![2-Wendys](https://dev-to-uploads.s3.amazonaws.com/i/d9dk7cxgn2f08494s53n.jpg)

*Part of the reason I chose Wendy's for this post is because of these funny tweets.*

## References
- [Introduction to PuLP](https://www.y-shinno.com/pulp-intro/)
- [Figuring out a Balanced Diet at McDonald's with Linear Programming](https://qiita.com/youwht/items/9098d560f28d16aa5567)
- [The Diet Problem](https://neos-guide.org/content/diet-problem)
- [Energy Requirements](https://jp.glico.com/navi/e07.html)