{"id":261,"date":"2019-05-05T07:33:29","date_gmt":"2019-05-05T07:33:29","guid":{"rendered":"http:\/\/nathankjer.com\/?p=261"},"modified":"2023-01-18T17:37:30","modified_gmt":"2023-01-19T01:37:30","slug":"linear-programming-python","status":"publish","type":"post","link":"https:\/\/nathankjer.com\/linear-programming-python\/","title":{"rendered":"Optimizing Fast Food Orders: A Linear Programming Tutorial"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">What is linear programming?<\/h2>\n\n\n\n<p>Linear programming is a powerful mathematical tool to optimize the mix of items in a set with associated costs and benefits.<\/p>\n\n\n\n<p>Constraints are used to form the feasible region, and the optimal solution is found at one of the corner points. This method is helpful in various fields, including Fast Food Order optimization.<\/p>\n\n\n\n<p>This tutorial will show you how to use linear programming to optimize fast food orders for cost-effectiveness.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/nathankjer.com\/wp-content\/uploads\/2019\/05\/feasible-region.png\" alt=\"\" class=\"wp-image-323\"\/><figcaption class=\"wp-element-caption\">Feasible region of two variables with three constraints.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Setup<\/h2>\n\n\n\n<p>The first step in using linear programming to optimize fast food orders is setting up the necessary libraries and data. <\/p>\n\n\n\n<p>To begin, we&#8217;ll need to import the pulp and pandas libraries. These libraries provide the tools we need to create and solve linear programming models. If you don&#8217;t have these libraries installed, you can easily install them by running the commands &#8220;pip install pulp&#8221; and &#8220;pip install pandas&#8221; in your command line.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nimport pulp\nimport pandas as pd\n<\/pre><\/div>\n\n\n<p>Once you have the libraries installed, we&#8217;ll need to download the <a href=\"https:\/\/github.com\/nathankjer\/fast-food-menus\">menu data<\/a> to the same directory as your code. It&#8217;s important to note that menu information may not always be accurate, as prices can vary by region.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Optimizing Taco Bell orders<\/h2>\n\n\n\n<p>In this example, we&#8217;ll use Taco Bell as the fast food chain and create a model for one person.<\/p>\n\n\n\n<p>The first step is to create a new linear programming model using the pulp library. This is done by defining a new LpProblem object and specifying that we want to minimize the cost of the order.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nmodel = pulp.LpProblem(&quot;Fast Food Order&quot;, pulp.LpMinimize)\n<\/pre><\/div>\n\n\n<p>Next, we&#8217;ll define some constants that will make the code more dynamic. In this case, we&#8217;re specifying that we&#8217;re ordering from Taco Bell and that the order is for one person.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nrestaurant = &#039;taco_bell&#039;\nnum_people = 1\n<\/pre><\/div>\n\n\n<p>We can then import the menu data stored in a CSV file in the same directory as our code. The menu data contains information about the available items, such as their cost and nutritional information.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nfoods = pd.read_csv(&#039;{0}.csv&#039;.format(restaurant),index_col=&#039;name&#039;)\n<\/pre><\/div>\n\n\n<p>We can then define the limit conditions for our variables. In this case, we&#8217;re setting a limit of one item per person to improve the variety, and the orders must be integer values.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nx = pulp.LpVariable.dict(&#039;x_%s&#039;, foods.index.tolist(), lowBound=0, upBound=num_people, cat=pulp.LpInteger)\n<\/pre><\/div>\n\n\n<p>To add user preferences, we can specify a must-have item. In this example, we&#8217;ve chosen the Crunchwrap Supreme, a Taco Bell classic.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nx&#x5B;&#039;Crunchwrap Supreme&#039;].lowBound = 1\n<\/pre><\/div>\n\n\n<p>We must then specify what we&#8217;re trying to minimize. In this case, we use the &#8220;price&#8221; column to minimize the order cost. However, a user may choose to minimize something else, such as calories.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;price&#039;] * x&#x5B;i] for i in foods.index.tolist()])\n<\/pre><\/div>\n\n\n<p>Finally, we&#8217;ll specify the nutritional requirements for the order. In this example, we&#8217;re setting requirements for calories, total fat, protein, sugars, and sodium.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;calories&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &gt;= 1320\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;total_fat&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &gt;= 63\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;protein&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &gt;= 10\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;sugars&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &lt;= 30\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;sodium&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &lt;= 100\n<\/pre><\/div>\n\n\n<p>Once all the constraints and objectives have been defined, the model can be solved to find the optimal combination of items that meet the specified requirements and minimize the order cost.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Results<\/h2>\n\n\n\n<p>Once the model has been defined, and all the necessary constraints and objectives have been set, it&#8217;s time to solve the model to find the optimal solution. The pulp library makes this easy with the built-in &#8220;solve()&#8221; function.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nmodel.solve()\n<\/pre><\/div>\n\n\n<p>It&#8217;s important to note that not all solutions are feasible. For example, if the requirement is less than 10 grams of sugar, and an item contains 12 grams of sugar, then there are no possible solutions. Therefore, it&#8217;s a good idea to check the model status after solving to ensure everything goes smoothly.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nprint(&quot;nStatus: {0}&quot;.format(pulp.LpStatus&#x5B;model.status]))\n<\/pre><\/div>\n\n\n<p>Once the model has been solved, you can output the results. This can be done by iterating through the model&#8217;s variables and printing their values. The solution will show you the optimal combination of items that meet the specified requirements and minimize the cost of the order.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nfor food in model.variables():\n    if food.varValue:\n        print(&quot;{0:0.0f} {1}&quot;.format(food.varValue, food.name))\nprint(&quot;\\nPrice \/ person = ${0:0.2f}&quot;.format(pulp.value(model.objective) \/ num_people))\n<\/pre><\/div>\n\n\n<p>In this example, the output shows that the optimal solution includes 1 Beefy Fritos Burrito, 1 Brisk Unsweetened No Lemon Iced Tea (16 fl oz), 1 Crunchwrap Supreme, and 1 Quesarito &#8211; Chicken. The total cost of the order is $8.97 per person.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nStatus: Optimal\n\n1 x_Beefy_Fritos_Burrito\n1 x_Brisk_Unsweetened_No_Lemon_Iced_Tea_(16_fl_oz)\n1 x_Crunchwrap_Supreme\n1 x_Quesarito___Chicken\n\nPrice \/ person = $8.97\n<\/pre><\/div>\n\n\n<p>As we can see, the example shows how the model includes 1 Crunchwrap Supreme, which is the must-have item that we specified.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Optimizing McDonald&#8217;s orders<\/h2>\n\n\n\n<p>In this section, we&#8217;ll show you how to use linear programming to optimize fast food orders from McDonald&#8217;s.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nrestaurant = &#039;mcdonalds&#039;\n<\/pre><\/div>\n\n\n<p>We&#8217;ll remove the Crunchwrap Supreme item and re-run the code. The results show that the model is infeasible unless we increase the sodium limit by five times or increase the sugar limit by three times.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nStatus: Infeasible\n\n-10 x_2_Apple_Pies\n1 x_Caramel_Frappe_(Small)\n1 x_Chocolate_Chip_Frappe\n14 x_Mocha_Frappe\n\nPrice \/ person = $38.34\n<\/pre><\/div>\n\n\n<p>To make the solution feasible, we&#8217;ll increase the sugar limit to 50 grams and the sodium limit to 2000 milligrams.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\nStatus: Optimal\n\n1 x_3_Pack_Of_Cookies\n1 x_McChicken\n1 x_Sausage_Biscuit\n\nPrice \/ person = $4.27\n<\/pre><\/div>\n\n\n<p>The solution is now feasible.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Trying it out in real life<\/h2>\n\n\n\n<p>The previous sections show how to use linear programming to find the best combination of items, but how does this approach work in real life?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">McDonald&#8217;s<\/h3>\n\n\n\n<p>I went to McDonald&#8217;s with one other person to test it out. They wanted a vanilla shake included in the order, which resulted in the following program:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"737\" src=\"http:\/\/nathankjer.com\/wp-content\/uploads\/2019\/05\/mcdonalds-order-1024x737.jpeg\" alt=\"\" class=\"wp-image-376\" srcset=\"https:\/\/nathankjer.com\/wp-content\/uploads\/2019\/05\/mcdonalds-order-1024x737.jpeg 1024w, https:\/\/nathankjer.com\/wp-content\/uploads\/2019\/05\/mcdonalds-order-300x216.jpeg 300w, https:\/\/nathankjer.com\/wp-content\/uploads\/2019\/05\/mcdonalds-order-768x553.jpeg 768w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\n2 x_McChicken\n1 x_1_Cookie\n1 x_Double_Hamburger\n1 x_Sausage_Biscuit\n1 x_Sausage_McMuffin\n1 x_Vanilla_Shake\n\nPrice \/ person = $5.37\n<\/pre><\/div>\n\n\n<p>The total was $2 more because they could not find the Sausage McMuffin, only a Sausage Egg McMuffin. Oh well&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Taco Bell<\/h3>\n\n\n\n<p>I also tried this approach at Taco Bell. Due to the convexity of the problem, if all you eat are Beefy Fritos Burritos, you can eat them for as little as ~$4.00. I opted for more variety by setting each item to a maximum of 1.<\/p>\n\n\n\n<p>The result was a good variety of items that were more complimentary to the food format than McDonald&#8217;s, and it ended up being ~$8 per person, which is more than the $6 at McDonald&#8217;s.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Full code<\/h2>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nimport pulp\nimport pandas as pd\n\nmodel = pulp.LpProblem(&quot;Fast Food Meal&quot;, pulp.LpMinimize)\n\nrestaurant = &#039;taco_bell&#039;\nnum_people = 1\n\nfoods = pd.read_csv(&#039;{0}.csv&#039;.format(restaurant),index_col=&#039;name&#039;)\n\n# Variables\nx = pulp.LpVariable.dict(&#039;x_%s&#039;, foods.index.tolist(), lowBound=0, upBound=num_people, cat=pulp.LpInteger)\n\n# Fixed items\nx&#x5B;&#039;Crunchwrap Supreme&#039;].lowBound = 1\n\n# Objective function based on cost of the foods\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;price&#039;] * x&#x5B;i] for i in foods.index.tolist()])\n\n# Dietary Constraints\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;calories&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &gt;= 1320 * num_people\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;total_fat&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &gt;= 63 * num_people\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;protein&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &gt;= 10 * num_people\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;sugars&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &lt;= 50 * num_people\nmodel += pulp.lpSum(&#x5B;foods.loc&#x5B;i]&#x5B;&#039;sodium&#039;] * x&#x5B;i] for i in foods.index.tolist()]) &lt;= 2000 * num_people\n\nmodel.solve()\n\nprint(&quot;\\nStatus: {0}\\n&quot;.format(pulp.LpStatus&#x5B;model.status]))\n\nfor food in model.variables():\n    if food.varValue:\n        print(&quot;{0:0.0f} {1}&quot;.format(food.varValue, food.name))\nprint(&quot;\\nPrice \/ person = ${0:0.2f}&quot;.format(pulp.value(model.objective) \/ num_people))\n<\/pre><\/div>","protected":false},"excerpt":{"rendered":"<p>What is linear programming? Linear programming is a powerful mathematical tool to optimize the mix of items in a set with associated costs and benefits. Constraints are used to form the feasible region, and the optimal solution is found at one of the corner points. This method is helpful in various fields, including Fast Food&#8230;<\/p>\n<p><a class=\"btn btn-primary btn-sm submarine-read-more-link\" href=\"https:\/\/nathankjer.com\/linear-programming-python\/\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":263,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[6],"tags":[9,16],"yst_prominent_words":[],"class_list":["post-261","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-programming","tag-optimization","tag-python"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/nathankjer.com\/wp-content\/uploads\/2019\/04\/taco_bell.png","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9Zlb2-4d","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/posts\/261","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/comments?post=261"}],"version-history":[{"count":102,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/posts\/261\/revisions"}],"predecessor-version":[{"id":2721,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/posts\/261\/revisions\/2721"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/media\/263"}],"wp:attachment":[{"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/media?parent=261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/categories?post=261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/tags?post=261"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/nathankjer.com\/wp-json\/wp\/v2\/yst_prominent_words?post=261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}