{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "sns.set_style('whitegrid')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 206,
   "metadata": {},
   "outputs": [],
   "source": [
    "summary = pd.read_csv('summary_stats.csv')\n",
    "df = pd.read_excel('XYZ-1.xlsx', 'Data')\n",
    "demand = df[6:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 207,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Purchase Cost</th>\n",
       "      <th>Lead Time</th>\n",
       "      <th>Size</th>\n",
       "      <th>Selling Price</th>\n",
       "      <th>Starting Stock</th>\n",
       "      <th>Mean</th>\n",
       "      <th>Std Dev</th>\n",
       "      <th>Co</th>\n",
       "      <th>Ch</th>\n",
       "      <th>Probability</th>\n",
       "      <th>Demand_lead</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>12</td>\n",
       "      <td>9</td>\n",
       "      <td>0.570</td>\n",
       "      <td>16.1</td>\n",
       "      <td>2750</td>\n",
       "      <td>103.501805</td>\n",
       "      <td>37.317994</td>\n",
       "      <td>1000</td>\n",
       "      <td>20</td>\n",
       "      <td>0.756831</td>\n",
       "      <td>705</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>7</td>\n",
       "      <td>6</td>\n",
       "      <td>0.052</td>\n",
       "      <td>8.6</td>\n",
       "      <td>22500</td>\n",
       "      <td>648.551913</td>\n",
       "      <td>26.446797</td>\n",
       "      <td>1200</td>\n",
       "      <td>20</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>3891</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>2</td>\n",
       "      <td>6</td>\n",
       "      <td>16</td>\n",
       "      <td>0.530</td>\n",
       "      <td>10.2</td>\n",
       "      <td>5200</td>\n",
       "      <td>201.677043</td>\n",
       "      <td>31.079301</td>\n",
       "      <td>1000</td>\n",
       "      <td>20</td>\n",
       "      <td>0.702186</td>\n",
       "      <td>2266</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>3</td>\n",
       "      <td>37</td>\n",
       "      <td>22</td>\n",
       "      <td>1.050</td>\n",
       "      <td>68.0</td>\n",
       "      <td>1400</td>\n",
       "      <td>150.068965</td>\n",
       "      <td>3.215598</td>\n",
       "      <td>1200</td>\n",
       "      <td>20</td>\n",
       "      <td>0.237705</td>\n",
       "      <td>785</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   Purchase Cost  Lead Time   Size  Selling Price  Starting Stock        Mean  \\\n",
       "0             12          9  0.570           16.1            2750  103.501805   \n",
       "1              7          6  0.052            8.6           22500  648.551913   \n",
       "2              6         16  0.530           10.2            5200  201.677043   \n",
       "3             37         22  1.050           68.0            1400  150.068965   \n",
       "\n",
       "     Std Dev    Co  Ch  Probability  Demand_lead  \n",
       "0  37.317994  1000  20     0.756831          705  \n",
       "1  26.446797  1200  20     1.000000         3891  \n",
       "2  31.079301  1000  20     0.702186         2266  \n",
       "3   3.215598  1200  20     0.237705          785  "
      ]
     },
     "execution_count": 207,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "summary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1a1bf09ad0>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 17304 characters]\n",
      "text/plain": [
       "<Figure size 864x576 with 4 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))\n",
    "demand[1].plot.hist(ax=axes[0, 0])\n",
    "demand[2].plot.hist(ax=axes[0, 1])\n",
    "demand[3].plot.hist(ax=axes[1, 0])\n",
    "demand[4].plot.hist(ax=axes[1, 1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 208,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Product:\n",
    "    def __init__(self, i):\n",
    "        \"\"\"\n",
    "        :type i: int - Product number\n",
    "        \"\"\"\n",
    "        self.i = i\n",
    "        self.unit_cost = summary['Purchase Cost'].iloc[i - 1]\n",
    "        self.lead_time = summary['Lead Time'].iloc[i - 1]\n",
    "        self.size = summary['Size'].iloc[i - 1]\n",
    "        self.selling_price = summary['Selling Price'].iloc[i - 1]\n",
    "        self.holding_cost = summary['Ch'].iloc[i - 1]\n",
    "        self.ordering_cost = summary['Co'].iloc[i - 1]\n",
    "        self.probability = summary['Probability'].iloc[i - 1]\n",
    "        self.starting_stock = summary['Starting Stock'].iloc[i - 1]\n",
    "        self.demand_lead = summary['Demand_lead'].iloc[i - 1]\n",
    "\n",
    "        self.mean = np.mean([np.log(j) for j in demand[demand[i] > 0][i]])\n",
    "        self.sd = np.std([np.log(j) for j in demand[demand[i] > 0][i]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Drawing from a log normal distribution\n",
    "\n",
    "def daily_demand(mean, sd, probability):\n",
    "    random_num = np.random.uniform(0, 1)\n",
    "    if random_num > probability:\n",
    "        return 0\n",
    "    else:\n",
    "        return np.exp(np.random.normal(mean, sd))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Simulating a Monte Carlo ray for 1 year\n",
    "\n",
    "def monte_carlo_ray(M, product, review_period=30):\n",
    "    inventory = product.starting_stock\n",
    "    mean = product.mean\n",
    "    sd = product.sd\n",
    "    lead_time = product.lead_time\n",
    "    probability = product.probability\n",
    "\n",
    "    q = 0\n",
    "    stock_out = 0\n",
    "    # dictionary to store all the information\n",
    "    data = {'inv_level': [], 'daily_demand': [], 'units_sold': [], 'units_lost': [], 'orders': []}\n",
    "\n",
    "    for day in range(1, 365):\n",
    "        day_demand = daily_demand(mean, sd, probability)\n",
    "        data['daily_demand'].append(day_demand)\n",
    "\n",
    "        if day % (review_period + lead_time) == 0:\n",
    "            # Restocking day\n",
    "            q = M - inventory\n",
    "            data['orders'].append(q)\n",
    "            inventory += q\n",
    "\n",
    "        if inventory - day_demand >= 0:\n",
    "            data['units_sold'].append(day_demand)\n",
    "            inventory -= day_demand\n",
    "        elif inventory - day_demand < 0:\n",
    "            data['units_sold'].append(inventory)\n",
    "            data['units_lost'].append(day_demand - inventory)\n",
    "            inventory = 0\n",
    "            stock_out += 1\n",
    "\n",
    "        data['inv_level'].append(inventory)\n",
    "\n",
    "    return data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 240,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Newer version\n",
    "            \n",
    "def monte_carlo_ray(M, product, review_period=30):\n",
    "    inventory = product.starting_stock\n",
    "    mean = product.mean\n",
    "    sd = product.sd\n",
    "    lead_time = product.lead_time\n",
    "    probability = product.probability\n",
    "    demand_lead = product.demand_lead\n",
    "\n",
    "    q = 0\n",
    "    stock_out = 0\n",
    "    counter = 0\n",
    "    order_placed = False\n",
    "    # dictionary to store all the information\n",
    "    data = {'inv_level': [], 'daily_demand': [], 'units_sold': [], 'units_lost': [], 'orders': []}\n",
    "\n",
    "    for day in range(1, 365):\n",
    "        day_demand = daily_demand(mean, sd, probability)\n",
    "        data['daily_demand'].append(day_demand)\n",
    "\n",
    "        if day % review_period == 0:\n",
    "            # Placing the order\n",
    "            q = M - inventory + demand_lead\n",
    "            order_placed = True\n",
    "            data['orders'].append(q)\n",
    "            \n",
    "            \n",
    "        if order_placed:\n",
    "            counter += 1\n",
    "        \n",
    "        if counter == lead_time:\n",
    "            # Restocking day\n",
    "            inventory += q\n",
    "            order_placed = False\n",
    "            counter = 0\n",
    "\n",
    "        if inventory - day_demand >= 0:\n",
    "            data['units_sold'].append(day_demand)\n",
    "            inventory -= day_demand\n",
    "        elif inventory - day_demand < 0:\n",
    "            data['units_sold'].append(inventory)\n",
    "            data['units_lost'].append(day_demand - inventory)\n",
    "            inventory = 0\n",
    "            stock_out += 1\n",
    "\n",
    "        data['inv_level'].append(inventory)\n",
    "\n",
    "    return data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 229,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_profit(data, product):\n",
    "    unit_cost = product.unit_cost\n",
    "    selling_price = product.selling_price\n",
    "    holding_cost = product.holding_cost\n",
    "    order_cost = product.ordering_cost\n",
    "    size = product.size\n",
    "    days = 365\n",
    "\n",
    "    revenue = sum(data['units_sold']) * selling_price\n",
    "    Co = len(data['orders']) * order_cost\n",
    "    Ch = sum(data['inv_level']) * holding_cost * size * (1 / days)\n",
    "    cost = sum(data['orders']) * unit_cost\n",
    "\n",
    "    profit = revenue - cost - Co - Ch\n",
    "\n",
    "    return profit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 230,
   "metadata": {},
   "outputs": [],
   "source": [
    "def mc_simulation(product, M, num_simulations=100):\n",
    "    profit_list = []\n",
    "    orders_lost_list = []\n",
    "    for sim in range(num_simulations):\n",
    "        data = monte_carlo_ray(M, product)\n",
    "        # Calculating the profit\n",
    "        profit = calculate_profit(data, product)\n",
    "        profit_list.append(profit)\n",
    "        # Calculating the orders lost\n",
    "        total_demand = sum(data['daily_demand'])\n",
    "        unsold_orders = sum(data['units_lost'])\n",
    "        orders_lost_list.append(unsold_orders/total_demand)\n",
    "\n",
    "    return profit_list, orders_lost_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[47828.27874700583, 47951.66395478855]\n"
     ]
    }
   ],
   "source": [
    "product_1 = Product(1)\n",
    "p_list = mc_simulation(product_1, 3000, 1000)\n",
    "print([np.mean(p_list), np.std(p_list)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 6612 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "p_list, o_list = mc_simulation(Product(1), 3000, 1000)\n",
    "sns.distplot(\n",
    "    p_list,\n",
    "    kde=False\n",
    ")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[95454.12823215405, 4924.7318025215345]\n"
     ]
    }
   ],
   "source": [
    "print([np.mean(p_list), np.std(p_list)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0665380956183336\n"
     ]
    }
   ],
   "source": [
    "print(np.mean(o_list))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 90224 characters]\n",
      "text/plain": [
       "<Figure size 1800x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "data = monte_carlo_ray(3000, product_1)\n",
    "plt.figure(figsize=(25,8))\n",
    "plt.plot(data['inv_level'])\n",
    "plt.axhline(3000, linewidth=1, color=\"grey\", linestyle=\":\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 231,
   "metadata": {},
   "outputs": [],
   "source": [
    "def periodic_review(product, low, high, step=50):\n",
    "    m_range = [i for i in range(low, high, step)]\n",
    "    review_dict = {}\n",
    "\n",
    "    for M in m_range:\n",
    "        p_list, o_list = mc_simulation(product, M)\n",
    "        review_dict[M] = (np.mean(p_list), np.quantile(p_list, 0.05), np.quantile(p_list, 0.95), np.std(p_list), np.mean(o_list))\n",
    "\n",
    "    return review_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 233,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 13388 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "review = periodic_review(Product(1), 1500, 4500)\n",
    "xx = [i for i in review.keys()]\n",
    "yy = []\n",
    "for val in review.values():\n",
    "    yy.append(val[0])\n",
    "\n",
    "plt.plot(xx, yy)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 234,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2200,\n",
       " (88716.48491604134,\n",
       "  82120.05828728252,\n",
       "  95951.45670102465,\n",
       "  4160.112019918497,\n",
       "  0.061310704954492735))"
      ]
     },
     "execution_count": 234,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max(review.items(), key=operator.itemgetter(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 243,
   "metadata": {},
   "outputs": [],
   "source": [
    "# creating a dataframe with all the results\n",
    "idx = ['Order-point M', 'Expected Profit', 'Profit Standard Deviation', 'Proportion of Lost Orders']\n",
    "review = periodic_review(Product(1), 2500, 4000, 10)\n",
    "prod_review_1 = max(review.items(), key=operator.itemgetter(1))\n",
    "\n",
    "review = periodic_review(Product(2), 23000, 24000, 10)\n",
    "prod_review_2 = max(review.items(), key=operator.itemgetter(1))\n",
    "\n",
    "review = periodic_review(Product(3), 6200, 6800, 10)\n",
    "prod_review_3 = max(review.items(), key=operator.itemgetter(1))\n",
    "\n",
    "review = periodic_review(Product(4), 2000, 2400, 10)\n",
    "prod_review_4 = max(review.items(), key=operator.itemgetter(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 244,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_product_review = pd.DataFrame(\n",
    "    {'1': [prod_review_1[0], prod_review_1[1][0], prod_review_1[1][3], prod_review_1[1][4]],\n",
    "     '2': [prod_review_2[0], prod_review_2[1][0], prod_review_2[1][3], prod_review_2[1][4]],\n",
    "     '3': [prod_review_3[0], prod_review_3[1][0], prod_review_3[1][3], prod_review_3[1][4]],\n",
    "     '4': [prod_review_4[0], prod_review_4[1][0], prod_review_4[1][3], prod_review_4[1][4]]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 245,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>1</th>\n",
       "      <th>2</th>\n",
       "      <th>3</th>\n",
       "      <th>4</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>Order-point M</td>\n",
       "      <td>2500.000000</td>\n",
       "      <td>23020.000000</td>\n",
       "      <td>6240.000000</td>\n",
       "      <td>2010.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Expected Profit</td>\n",
       "      <td>86659.288707</td>\n",
       "      <td>341387.265477</td>\n",
       "      <td>143267.169829</td>\n",
       "      <td>302223.308928</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Profit Standard Deviation</td>\n",
       "      <td>4599.749013</td>\n",
       "      <td>1086.006075</td>\n",
       "      <td>9752.115263</td>\n",
       "      <td>35574.330385</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Proportion of Lost Orders</td>\n",
       "      <td>0.030567</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.018816</td>\n",
       "      <td>0.032723</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                      1              2              3  \\\n",
       "Order-point M               2500.000000   23020.000000    6240.000000   \n",
       "Expected Profit            86659.288707  341387.265477  143267.169829   \n",
       "Profit Standard Deviation   4599.749013    1086.006075    9752.115263   \n",
       "Proportion of Lost Orders      0.030567       0.000000       0.018816   \n",
       "\n",
       "                                       4  \n",
       "Order-point M                2010.000000  \n",
       "Expected Profit            302223.308928  \n",
       "Profit Standard Deviation   35574.330385  \n",
       "Proportion of Lost Orders       0.032723  "
      ]
     },
     "execution_count": 245,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_product_review = df_product_review.set_index(pd.Index(idx))\n",
    "df_product_review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>1</th>\n",
       "      <th>2</th>\n",
       "      <th>3</th>\n",
       "      <th>4</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>Order-point M</td>\n",
       "      <td>2910.000000</td>\n",
       "      <td>23330.000000</td>\n",
       "      <td>6690.000000</td>\n",
       "      <td>2100.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Expected Profit</td>\n",
       "      <td>96042.499781</td>\n",
       "      <td>369499.571720</td>\n",
       "      <td>194457.728422</td>\n",
       "      <td>322838.215214</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Profit Standard Deviation</td>\n",
       "      <td>4674.771665</td>\n",
       "      <td>735.550308</td>\n",
       "      <td>8013.906551</td>\n",
       "      <td>25584.168878</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Proportion of Lost Orders</td>\n",
       "      <td>0.083989</td>\n",
       "      <td>0.003980</td>\n",
       "      <td>0.043220</td>\n",
       "      <td>0.070796</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                      1              2              3  \\\n",
       "Order-point M               2910.000000   23330.000000    6690.000000   \n",
       "Expected Profit            96042.499781  369499.571720  194457.728422   \n",
       "Profit Standard Deviation   4674.771665     735.550308    8013.906551   \n",
       "Proportion of Lost Orders      0.083989       0.003980       0.043220   \n",
       "\n",
       "                                       4  \n",
       "Order-point M                2100.000000  \n",
       "Expected Profit            322838.215214  \n",
       "Profit Standard Deviation   25584.168878  \n",
       "Proportion of Lost Orders       0.070796  "
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_product_review = df_product_review.set_index(pd.Index(idx))\n",
    "df_product_review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 270,
   "metadata": {},
   "outputs": [],
   "source": [
    "def p_review_optimum(product, M, sim=1000):\n",
    "    review_dict = {}\n",
    "    p_list, o_list = mc_simulation(product, M, sim)\n",
    "    review_dict[M] = (np.mean(p_list), np.quantile(p_list, 0.05), np.quantile(p_list, 0.95), np.std(p_list), np.mean(o_list))\n",
    "\n",
    "    return review_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 272,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>1</th>\n",
       "      <th>2</th>\n",
       "      <th>3</th>\n",
       "      <th>4</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>Order-point M</td>\n",
       "      <td>2701.000000</td>\n",
       "      <td>18424.000000</td>\n",
       "      <td>4154.000000</td>\n",
       "      <td>1305.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Expected Profit</td>\n",
       "      <td>83354.374623</td>\n",
       "      <td>372188.204545</td>\n",
       "      <td>165594.927642</td>\n",
       "      <td>320506.618858</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Profit Standard Deviation</td>\n",
       "      <td>5035.025681</td>\n",
       "      <td>1454.087292</td>\n",
       "      <td>6560.952235</td>\n",
       "      <td>29863.522266</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Proportion of Lost Orders</td>\n",
       "      <td>0.018057</td>\n",
       "      <td>0.010143</td>\n",
       "      <td>0.049532</td>\n",
       "      <td>0.073687</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                      1              2              3  \\\n",
       "Order-point M               2701.000000   18424.000000    4154.000000   \n",
       "Expected Profit            83354.374623  372188.204545  165594.927642   \n",
       "Profit Standard Deviation   5035.025681    1454.087292    6560.952235   \n",
       "Proportion of Lost Orders      0.018057       0.010143       0.049532   \n",
       "\n",
       "                                       4  \n",
       "Order-point M                1305.000000  \n",
       "Expected Profit            320506.618858  \n",
       "Profit Standard Deviation   29863.522266  \n",
       "Proportion of Lost Orders       0.073687  "
      ]
     },
     "execution_count": 272,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Product 1: 2071\n",
    "# Product 2: 18424\n",
    "# Product 3: 4154\n",
    "# Product 4: 1305\n",
    "# creating a dataframe with all the results\n",
    "idx = ['Order-point M', 'Expected Profit', 'Profit Standard Deviation', 'Proportion of Lost Orders']\n",
    "\n",
    "review = p_review_optimum(Product(1), 2701)\n",
    "prod_review_1 = max(review.items(), key=operator.itemgetter(1))\n",
    "review = p_review_optimum(Product(2), 18424)\n",
    "prod_review_2 = max(review.items(), key=operator.itemgetter(1))\n",
    "review = p_review_optimum(Product(3), 4154)\n",
    "prod_review_3 = max(review.items(), key=operator.itemgetter(1))\n",
    "review = p_review_optimum(Product(4), 1305)\n",
    "prod_review_4 = max(review.items(), key=operator.itemgetter(1))\n",
    "\n",
    "df_product_review = pd.DataFrame(\n",
    "    {'1': [prod_review_1[0], prod_review_1[1][0], prod_review_1[1][3], prod_review_1[1][4]],\n",
    "     '2': [prod_review_2[0], prod_review_2[1][0], prod_review_2[1][3], prod_review_2[1][4]],\n",
    "     '3': [prod_review_3[0], prod_review_3[1][0], prod_review_3[1][3], prod_review_3[1][4]],\n",
    "     '4': [prod_review_4[0], prod_review_4[1][0], prod_review_4[1][3], prod_review_4[1][4]]})\n",
    "\n",
    "df_product_review = df_product_review.set_index(pd.Index(idx))\n",
    "df_product_review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Continuous Review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 221,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cc_monte_carlo_ray(product, q, r):\n",
    "    inventory = product.starting_stock\n",
    "    mean = product.mean\n",
    "    sd = product.sd\n",
    "    lead_time = product.lead_time\n",
    "    probability = product.probability\n",
    "\n",
    "    order_placed = False\n",
    "    order_time = 0\n",
    "    stock_out = 0\n",
    "    # dictionary to store all the information\n",
    "    data = {'inv_level': [], 'daily_demand': [], 'units_sold': [], 'units_lost': [], 'orders': []}\n",
    "\n",
    "    \n",
    "    for day in range(1, 365):\n",
    "        day_demand = daily_demand(mean, sd, probability)\n",
    "        data['daily_demand'].append(day_demand)\n",
    "\n",
    "        if inventory <= r and not order_placed:\n",
    "            # Time to place an order\n",
    "            order_placed = True\n",
    "            order_time = day\n",
    "\n",
    "        if order_placed and (day-order_time) == lead_time:\n",
    "            data['orders'].append(q)\n",
    "            inventory += q\n",
    "            order_placed = False\n",
    "            order_time = 0\n",
    "            \n",
    "        if inventory - day_demand >= 0:\n",
    "            data['units_sold'].append(day_demand)\n",
    "            inventory -= day_demand\n",
    "        elif inventory - day_demand < 0:\n",
    "            data['units_sold'].append(inventory)\n",
    "            data['units_lost'].append(day_demand - inventory)\n",
    "            inventory = 0\n",
    "            stock_out += 1\n",
    "\n",
    "        data['inv_level'].append(inventory)\n",
    "\n",
    "    return data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 222,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 89816 characters]\n",
      "text/plain": [
       "<Figure size 1800x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "data = cc_monte_carlo_ray(Product(1), 2000, 700)\n",
    "plt.figure(figsize=(25,8))\n",
    "plt.plot(data['inv_level'])\n",
    "plt.axhline(3000, linewidth=1, color=\"grey\", linestyle=\":\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = cc_monte_carlo_ray(Product(1), 2000, 700)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(data['orders'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cc_mc_simulation(product, q, r, num_simulations=50):\n",
    "    profit_list = []\n",
    "    orders_lost_list = []\n",
    "    for sim in range(num_simulations):\n",
    "        data = cc_monte_carlo_ray(product, q, r)\n",
    "        # Calculating the profit\n",
    "        profit = calculate_profit(data, product)\n",
    "        profit_list.append(profit)\n",
    "        # Calculating the orders lost\n",
    "        total_demand = sum(data['daily_demand'])\n",
    "        unsold_orders = sum(data['units_lost'])\n",
    "        orders_lost_list.append(unsold_orders/total_demand)\n",
    "\n",
    "    return profit_list, orders_lost_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[108832.39646631153, 8592.610428950817]\n"
     ]
    }
   ],
   "source": [
    "p_list, o_list = cc_mc_simulation(Product(1), 2000, 700, 1000)\n",
    "print([np.mean(p_list), np.std(p_list)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 7252 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "sns.distplot(\n",
    "    p_list,\n",
    "    kde=False\n",
    ")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "metadata": {},
   "outputs": [],
   "source": [
    "def continous_review(product, q_guess, r_guess):\n",
    "    q_low = q_guess - 1000\n",
    "    q_high = q_guess + 1000\n",
    "    q_range = [i for i in range(int(q_low), int(q_high), 10)]\n",
    "\n",
    "    r_low = r_guess - 300\n",
    "    r_high = r_guess + 300\n",
    "    r_range = [i for i in range(int(r_low), int(r_high), 10)]\n",
    "\n",
    "    review_dict = {}\n",
    "\n",
    "    for q in q_range:\n",
    "        for r in r_range:\n",
    "            p_list, o_list = cc_mc_simulation(product, q, r)\n",
    "            review_dict[(q, r)] = (\n",
    "                np.mean(p_list), np.quantile(p_list, 0.05), np.quantile(p_list, 0.95), np.std(p_list), np.mean(o_list))\n",
    "\n",
    "    return review_dict\n",
    "\n",
    "#review = continous_review(Product(1), 2000, 700)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "cc_review = continous_review(Product(1), 2000, 700)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "from mpl_toolkits.mplot3d import Axes3D "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 161536 characters]\n",
      "text/plain": [
       "<Figure size 864x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(figsize=(12,8))\n",
    "ax = fig.add_subplot(111, projection='3d')\n",
    "\n",
    "for key, val in cc_review.items():\n",
    "    ax.scatter(key[0], key[1], val[0], marker = 'o')\n",
    "\n",
    "ax.set_xlabel('Order Quantity')\n",
    "ax.set_ylabel('Reorder Point')\n",
    "ax.set_zlabel('Profit')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 169,
   "metadata": {},
   "outputs": [],
   "source": [
    "cc_review = continous_review(Product(1), 2002, 812)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((1600, 825),\n",
       " (112665.05056769702,\n",
       "  101649.67352898626,\n",
       "  125685.94534605007,\n",
       "  7561.054946947037,\n",
       "  0.03177946885333878))"
      ]
     },
     "execution_count": 108,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max(review.items(), key=operator.itemgetter(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [],
   "source": [
    "# creating a dataframe with all the results\n",
    "idx = ['Order Quantity', 'Reorder Point', 'Expected Profit', 'Profit Standard Deviation', 'Proportion of Lost Orders']\n",
    "review = continous_review(Product(1), 2000, 700)\n",
    "prod_review_1 = max(review.items(), key=operator.itemgetter(1))\n",
    "\n",
    "review = continous_review(Product(2), 22500, 2600)\n",
    "prod_review_2 = max(review.items(), key=operator.itemgetter(1))\n",
    "\n",
    "review = continous_review(Product(3), 3500, 2500)\n",
    "prod_review_3 = max(review.items(), key=operator.itemgetter(1))\n",
    "\n",
    "review = continous_review(Product(4), 1150, 1150)\n",
    "prod_review_4 = max(review.items(), key=operator.itemgetter(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_cc_review = pd.DataFrame(\n",
    "        {'1': [prod_review_1[0][0], prod_review_1[0][1], prod_review_1[1][0], prod_review_1[1][3], prod_review_1[1][4]],\n",
    "         '2': [prod_review_2[0][0], prod_review_2[0][1], prod_review_2[1][0], prod_review_2[1][3], prod_review_2[1][4]],\n",
    "         '3': [prod_review_3[0][0], prod_review_3[0][1], prod_review_3[1][0], prod_review_3[1][3], prod_review_3[1][4]],\n",
    "         '4': [prod_review_4[0][0], prod_review_4[0][1], prod_review_4[1][0], prod_review_4[1][3], prod_review_4[1][4]]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>1</th>\n",
       "      <th>2</th>\n",
       "      <th>3</th>\n",
       "      <th>4</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>Order Quantity</td>\n",
       "      <td>1850.000000</td>\n",
       "      <td>22350.000000</td>\n",
       "      <td>3350.000000</td>\n",
       "      <td>1150.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Reorder Point</td>\n",
       "      <td>825.000000</td>\n",
       "      <td>2825.000000</td>\n",
       "      <td>2725.000000</td>\n",
       "      <td>1150.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Expected Profit</td>\n",
       "      <td>113305.635713</td>\n",
       "      <td>488147.417157</td>\n",
       "      <td>200275.005181</td>\n",
       "      <td>390542.242843</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Profit Standard Deviation</td>\n",
       "      <td>6486.337576</td>\n",
       "      <td>4862.295020</td>\n",
       "      <td>10126.328780</td>\n",
       "      <td>35229.299452</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>Proportion of Lost Orders</td>\n",
       "      <td>0.028578</td>\n",
       "      <td>0.055202</td>\n",
       "      <td>0.010148</td>\n",
       "      <td>0.022566</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                       1              2              3  \\\n",
       "Order Quantity               1850.000000   22350.000000    3350.000000   \n",
       "Reorder Point                 825.000000    2825.000000    2725.000000   \n",
       "Expected Profit            113305.635713  488147.417157  200275.005181   \n",
       "Profit Standard Deviation    6486.337576    4862.295020   10126.328780   \n",
       "Proportion of Lost Orders       0.028578       0.055202       0.010148   \n",
       "\n",
       "                                       4  \n",
       "Order Quantity               1150.000000  \n",
       "Reorder Point                1150.000000  \n",
       "Expected Profit            390542.242843  \n",
       "Profit Standard Deviation   35229.299452  \n",
       "Proportion of Lost Orders       0.022566  "
      ]
     },
     "execution_count": 123,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_cc_review = df_cc_review.set_index(pd.Index(idx))\n",
    "df_cc_review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[467790.62919030513, 5430.26335727599]\n"
     ]
    }
   ],
   "source": [
    "p_list, o_list = cc_mc_simulation(Product(2), 25513, 2638, 10000)\n",
    "print([np.mean(p_list), np.std(p_list)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 6912 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "sns.distplot(\n",
    "    p_list,\n",
    "    kde=False\n",
    ")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.055141650618583116"
      ]
     },
     "execution_count": 129,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(o_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Find the optimum value "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Periodic Review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 263,
   "metadata": {},
   "outputs": [],
   "source": [
    "product = Product(4)\n",
    "def fn(args):\n",
    "    M = args[0][0]\n",
    "    \n",
    "    p_list, o_list = mc_simulation(product, M, 500)\n",
    "    \n",
    "    print(f' M : {M}, Profit : ${np.mean(p_list):.2f}') \n",
    "    return -np.mean(p_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 264,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " M : 8564.0, Profit : $-62147.29\n",
      " M : 4575.0, Profit : $156476.17\n",
      " M : 2384.0, Profit : $279090.77\n",
      " M : 1240.0, Profit : $319304.16\n",
      " M : 2762.0, Profit : $254034.81\n",
      " M : 2379, Profit : $278839.29\n",
      " M : 2380, Profit : $281127.72\n",
      " M : 1297, Profit : $321277.65\n",
      " M : 1303, Profit : $322316.54\n",
      " M : 1309, Profit : $318693.07\n",
      " M : 1305, Profit : $323975.37\n",
      " M : 1311, Profit : $319215.17\n",
      " M : 1307, Profit : $320363.83\n",
      " M : 1307, Profit : $318037.02\n"
     ]
    }
   ],
   "source": [
    "bounds = [{'name':'M', 'type':'discrete', 'domain': range(10,10000)}]\n",
    "\n",
    "Op = BayesianOptimization(f = fn,\n",
    "                         domain = bounds,\n",
    "                         model_type = 'GP',\n",
    "                         acquisition_type = 'EI',\n",
    "                         exact_feval = False,\n",
    "                         maximize = False,\n",
    "                         normalize_Y = False)\n",
    "\n",
    "Op.run_optimization(max_iter=100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 265,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1305.])"
      ]
     },
     "execution_count": 265,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Op.x_opt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Continuous Review"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "metadata": {},
   "outputs": [],
   "source": [
    "def optimizer(args):\n",
    "    \n",
    "    q = args[0][0]\n",
    "    r = args[0][1]\n",
    "    \n",
    "    product = Product(4)\n",
    "    p_list, o_list = cc_mc_simulation(product, q, r, 500)\n",
    "    \n",
    "    print(f' q : {q}, r : {r}, Profit : ${np.mean(p_list):.2f}')\n",
    "    \n",
    "    return -np.mean(p_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " q : 2835.0, r : 4056.0, Profit : $186410.79\n",
      " q : 4288.0, r : 276.0, Profit : $265851.56\n",
      " q : 2719.0, r : 1400.0, Profit : $335889.17\n",
      " q : 3515.0, r : 4892.0, Profit : $115261.84\n",
      " q : 2896.0, r : 2893.0, Profit : $245381.43\n",
      " q : 294, r : 3206, Profit : $210068.66\n",
      " q : 3031, r : 3867, Profit : $189974.33\n",
      " q : 4520, r : 380, Profit : $262544.19\n",
      " q : 1119, r : 2578, Profit : $309741.69\n",
      " q : 776, r : 2286, Profit : $369692.76\n",
      " q : 4098, r : 2572, Profit : $219737.17\n",
      " q : 3789, r : 2246, Profit : $258899.44\n",
      " q : 1606, r : 1333, Profit : $366792.55\n",
      " q : 2375, r : 650, Profit : $342985.65\n",
      " q : 3624, r : 1445, Profit : $301727.82\n",
      " q : 687, r : 921, Profit : $342108.00\n",
      " q : 2377, r : 630, Profit : $339434.76\n",
      " q : 545, r : 1800, Profit : $318968.62\n",
      " q : 1372, r : 701, Profit : $354547.31\n",
      " q : 1068, r : 1952, Profit : $343620.37\n",
      " q : 1978, r : 1082, Profit : $361024.95\n",
      " q : 382, r : 10, Profit : $184590.19\n",
      " q : 1282, r : 1097, Profit : $375771.43\n",
      " q : 1455, r : 1079, Profit : $373767.38\n",
      " q : 1196, r : 1021, Profit : $377255.14\n",
      " q : 515, r : 2394, Profit : $305747.45\n",
      " q : 3044, r : 808, Profit : $339825.97\n",
      " q : 781, r : 4939, Profit : $367907.67\n",
      " q : 101, r : 4999, Profit : $122074.17\n",
      " q : 1253, r : 4849, Profit : $193111.32\n",
      " q : 689, r : 4553, Profit : $366319.78\n",
      " q : 4754, r : 1492, Profit : $284152.74\n",
      " q : 2527, r : 1009, Profit : $354410.13\n",
      " q : 2191, r : 1894, Profit : $326115.05\n",
      " q : 702, r : 4773, Profit : $367308.63\n",
      " q : 815, r : 3955, Profit : $362001.72\n",
      " q : 4079, r : 923, Profit : $328014.96\n",
      " q : 610, r : 4117, Profit : $342158.83\n",
      " q : 1003, r : 1180, Profit : $380922.56\n",
      " q : 1237, r : 3567, Profit : $256719.22\n",
      " q : 4999, r : 4116, Profit : $123139.59\n",
      " q : 856, r : 4263, Profit : $341450.20\n",
      " q : 3233, r : 10, Profit : $299067.66\n",
      " q : 3559, r : 577, Profit : $323966.83\n",
      " q : 2869, r : 2072, Profit : $294370.82\n",
      " q : 1028, r : 1096, Profit : $378536.73\n",
      " q : 1009, r : 1332, Profit : $379565.85\n",
      " q : 895, r : 1276, Profit : $379018.11\n",
      " q : 1008, r : 1235, Profit : $375126.21\n",
      " q : 920, r : 1463, Profit : $376838.19\n",
      " q : 2161, r : 1424, Profit : $344815.18\n",
      " q : 949, r : 1140, Profit : $377799.63\n",
      " q : 934, r : 1343, Profit : $376944.52\n",
      " q : 1717, r : 10, Profit : $283153.67\n",
      " q : 1063, r : 1107, Profit : $378320.50\n",
      " q : 760, r : 3636, Profit : $373500.82\n",
      " q : 4999, r : 2410, Profit : $232266.53\n",
      " q : 1009, r : 1136, Profit : $378645.19\n",
      " q : 1132, r : 1428, Profit : $372336.61\n",
      " q : 1006, r : 1142, Profit : $379957.20\n",
      " q : 1815, r : 762, Profit : $357564.16\n",
      " q : 1006, r : 1144, Profit : $379184.87\n",
      " q : 1005, r : 1144, Profit : $379907.99\n",
      " q : 1004, r : 1145, Profit : $376242.52\n",
      " q : 1021, r : 1130, Profit : $381056.11\n",
      " q : 1042, r : 1115, Profit : $380724.10\n",
      " q : 1061, r : 1104, Profit : $376940.06\n",
      " q : 1013, r : 1133, Profit : $379243.34\n",
      " q : 1013, r : 1132, Profit : $380030.91\n",
      " q : 1012, r : 1131, Profit : $380171.66\n",
      " q : 1011, r : 1129, Profit : $379649.24\n",
      " q : 1010, r : 1127, Profit : $378653.81\n",
      " q : 1011, r : 1130, Profit : $375229.44\n",
      " q : 1019, r : 1139, Profit : $379893.98\n",
      " q : 1021, r : 1141, Profit : $378950.91\n",
      " q : 1021, r : 1141, Profit : $380501.62\n"
     ]
    }
   ],
   "source": [
    "from GPyOpt.methods import BayesianOptimization\n",
    "\n",
    "bounds = [{'name':'Q', 'type':'discrete', 'domain': range(10,5000)},\n",
    "         {'name': 'r', 'type':'discrete', 'domain': range(10,5000)}]\n",
    "\n",
    "Op = BayesianOptimization(f = optimizer,\n",
    "                         domain = bounds,\n",
    "                         model_type = 'GP',\n",
    "                         acquisition_type = 'EI',\n",
    "                         exact_feval = False,\n",
    "                         maximize = False,\n",
    "                         normalize_Y = False)\n",
    "\n",
    "Op.run_optimization(max_iter=100)\n",
    "\n",
    "#Op.plot_acquisition()\n",
    "#Op.Y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1021., 1130.])"
      ]
     },
     "execution_count": 166,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Op.x_opt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Product 2: array([21150.,  4036.])\n",
    "# Product 1: array([2002.,  812.])\n",
    "# Product 3: array([3318., 2877.])\n",
    "# Product 4: array([1099., 1221.]) 383437.71"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 10092 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "p_list, o_list = cc_mc_simulation(Product(2), 21150, 4036, 10000)\n",
    "\n",
    "sns.distplot(\n",
    "    p_list,\n",
    "    kde=False\n",
    ")\n",
    "plt.title(f'Max Profit : {[np.mean(p_list), np.std(p_list)]}')\n",
    "                           \n",
    "plt.show()\n",
    "\n",
    "#print([np.mean(p_list), np.std(p_list)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1a1ca73450>"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 7432 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "sns.distplot(\n",
    "    o_list,\n",
    "    kde=False\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0019119662339925198"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.std(o_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Safety Stock"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Expected Demand Product 1 during lead time - 705.00 \n",
      "\n",
      "Expected Demand Product 2 during lead time - 3891.31 \n",
      "\n",
      "Expected Demand Product 3 during lead time - 2265.84 \n",
      "\n",
      "Expected Demand Product 4 during lead time - 784.79 \n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Drawing from the original distribution\n",
    "\n",
    "for i in range(4):\n",
    "    print(f\"Expected Demand Product {i+1} during lead time - {summary['Lead Time'].iloc[i] * np.mean(demand[i+1]):.2f} \\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "demand_lead_time = []\n",
    "for i in range(4):\n",
    "    demand_lead_time.append(summary['Lead Time'].iloc[i] * np.mean(demand[i+1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 187,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Safety Stock for Product 0 is : 107.0\n",
      "Safety Stock for Product 1 is : 145.0\n",
      "Safety Stock for Product 2 is : 611.0\n",
      "Safety Stock for Product 3 is : 436.0\n"
     ]
    }
   ],
   "source": [
    "r_star = [812, 4036, 2877, 1221]\n",
    "\n",
    "for i in range(4):\n",
    "    print(f'Safety Stock for Product {i} is : {round(r_star[i] - demand_lead_time[i], 0)}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotting function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 286,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_periodic_review(product, low, high, step=50):\n",
    "    \n",
    "    review = periodic_review(product, low, high, step)\n",
    "    optimum = max(review.items(), key=operator.itemgetter(1))\n",
    "    \n",
    "    xx = [i for i in review.keys()]\n",
    "    yy = []\n",
    "    y_sd_1 = []\n",
    "    y_sd_2 = []\n",
    "    y_sd = []\n",
    "    for val in review.values():\n",
    "        yy.append(val[0])\n",
    "        y_sd_1.append(val[1])\n",
    "        y_sd_2.append(val[2])\n",
    "        y_sd.append(val[3])\n",
    "\n",
    "    fig, ax = plt.subplots(nrows=1, ncols=1)\n",
    "    plt.plot(xx, yy, linewidth=2)\n",
    "    plt.fill_between(xx, y_sd_1, y_sd_2, alpha=.25)\n",
    "    #plt.axvline(x = optimum[0], color='k', alpha = .5, ls = '--')\n",
    "    plt.axvline(x = 2071, color='k', alpha = .5, ls = '--')\n",
    "\n",
    "    ax.set_ylabel('Profit')\n",
    "    ax.set_xlabel('M')\n",
    "    ax.set_title('Simulation of Profit')\n",
    "    plt.savefig('Pictures/sim_pr.png', dpi = 200)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 278,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_inventory(product, M):\n",
    "    data = monte_carlo_ray(M, product)\n",
    "    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(25,8))\n",
    "    plt.plot(data['inv_level'], linewidth = 1.5)\n",
    "    plt.axhline(M, linewidth=2, color=\"grey\", linestyle=\":\")\n",
    "    plt.axhline(0, linewidth=2, color=\"grey\", linestyle=\":\")\n",
    "    plt.xlim(0,365)\n",
    "    ax.set_ylabel('Inventory Level (units)', fontsize=18)\n",
    "    ax.set_xlabel('Day', fontsize=18)\n",
    "    ax.set_title(f\"Periodic Review Model : Number of Orders {len(data['orders'])}\", fontsize=18)\n",
    "    plt.savefig('Pictures/inv_level.png', dpi = 200)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 275,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_profit_orders(product, M):\n",
    "    p_list, o_list = mc_simulation(product, M, 10000)\n",
    "    f, ax = plt.subplots(1, 2, figsize=(10, 4))\n",
    "    sns.distplot(p_list, kde=False, ax=ax[0])\n",
    "    ax[0].set_title(f'Profit Distribution : Mean {np.mean(p_list):.2f}')\n",
    "    ax[0].axvline(x = np.mean(p_list), color='k', alpha = .5, ls = '--')\n",
    "    sns.distplot(o_list, kde=False, ax=ax[1])\n",
    "    ax[1].set_title(f'Orders Lost : Mean {np.mean(o_list):.3f}')\n",
    "    ax[1].axvline(x = np.mean(o_list), color='k', alpha = .5, ls = '--')\n",
    "    plt.tight_layout()\n",
    "    print(np.mean(p_list), np.std(p_list))\n",
    "    plt.savefig('Pictures/profit_dist.png', dpi = 200)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_cr_profit_orders(product, q, r):\n",
    "    p_list, o_list = cc_mc_simulation(product, q, r, 10000)\n",
    "    f, ax = plt.subplots(1, 2, figsize=(10, 4))\n",
    "    sns.distplot(p_list, kde=False, ax=ax[0])\n",
    "    ax[0].set_title(f'Profit Distribution : Mean {np.mean(p_list):.2f}')\n",
    "    ax[0].axvline(x = np.mean(p_list), color='k', alpha = .5, ls = '--')\n",
    "    sns.distplot(o_list, kde=False, ax=ax[1])\n",
    "    ax[1].set_title(f'Orders Lost : Mean {np.mean(o_list):.3f}')\n",
    "    ax[1].axvline(x = np.mean(o_list), color='k', alpha = .5, ls = '--')\n",
    "    plt.tight_layout()\n",
    "    print(np.mean(p_list), np.std(p_list))\n",
    "    plt.savefig('Pictures/cc_profit_dist.png', dpi = 200)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 195,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_cr_inventory(product, q, r):\n",
    "    data = cc_monte_carlo_ray(product, q, r)\n",
    "    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(25,8))\n",
    "    plt.plot(data['inv_level'], linewidth = 1.5)\n",
    "    #plt.axhline(M, linewidth=2, color=\"grey\", linestyle=\":\")\n",
    "    plt.axhline(0, linewidth=2, color=\"grey\", linestyle=\":\")\n",
    "    plt.xlim(0,365)\n",
    "    ax.set_ylabel('Inventory Level (units)', fontsize=18)\n",
    "    ax.set_xlabel('Day', fontsize=18)\n",
    "    ax.set_title(f\"Continuous Review Model : Number of Orders {len(data['orders'])}\", fontsize=18)\n",
    "    plt.savefig('Pictures/cc_inv_level.png', dpi = 200)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1a217b5cd0>"
      ]
     },
     "execution_count": 126,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ax"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(<Figure size 432x288 with 1 Axes>,\n",
       " <matplotlib.axes._subplots.AxesSubplot at 0x1a29b79cd0>)"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ax.annotate((3000, 90000))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 287,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 37188 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_periodic_review(Product(1), 1000, 3000, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 33068 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_periodic_review(Product(2), 22000, 25000, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 36132 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_periodic_review(Product(3), 5500, 7000, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 37824 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_periodic_review(Product(4), 1500, 3000, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 279,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 107388 characters]\n",
      "text/plain": [
       "<Figure size 1800x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_inventory(Product(1), 2071)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Product 1: 2071\n",
    "# Product 2: 18424\n",
    "# Product 3: 4154\n",
    "# Product 4: 1305"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 285,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "87863.3342331631 3667.1399014481394\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 20504 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_profit_orders(Product(1), 2000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 180,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 299744 characters]\n",
      "text/plain": [
       "<Figure size 864x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "cc_review = continous_review(Product(1), 2002, 812)\n",
    "fig = plt.figure(figsize=(12,8))\n",
    "ax = fig.add_subplot(111, projection='3d')\n",
    "\n",
    "for key, val in cc_review.items():\n",
    "    ax.scatter(key[0], key[1], val[0], marker = 'o')\n",
    "\n",
    "ax.set_xlabel('Order Quantity', fontsize=18)\n",
    "ax.set_ylabel('Reorder Point', fontsize=18)\n",
    "ax.set_zlabel('Profit', fontsize=18)\n",
    "ax.set_title('Product 1 Simulated Results', fontsize=18)\n",
    "\n",
    "plt.savefig('Pictures/3d_plot.png', dpi = 200)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Product 2: array([21150.,  4036.])\n",
    "# Product 1: array([2002.,  812.])\n",
    "# Product 3: array([3318., 2877.])\n",
    "# Product 4: array([1099., 1221.]) 383437.71"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 198,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "110280.47527902896 8871.41464096155\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 19880 characters]\n",
      "text/plain": [
       "<Figure size 720x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_cr_profit_orders(Product(1), 2002, 812)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 108832 characters]\n",
      "text/plain": [
       "<Figure size 1800x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_cr_inventory(Product(1), 2002, 812)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
