aoml/pds/cases/case_2/Diemen_case_2_my_notebook.ipynb
2023-12-09 18:05:57 +01:00

686 lines
No EOL
28 KiB
Text

{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"source": [
"# Case 2 - Student Notebook"
],
"metadata": {
"id": "oHWpkTqMeBMq"
}
},
{
"cell_type": "markdown",
"source": [
"## Imports and data loading"
],
"metadata": {
"id": "BhR7Z_UqeEgm"
}
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "HQwlyagxfXRU"
},
"outputs": [],
"source": [
"import io\n",
"import pandas as pd\n",
"import numpy as np\n",
"import seaborn as sns\n",
"from google.colab import files\n",
"from datetime import datetime, timedelta"
]
},
{
"cell_type": "code",
"source": [
"# This avoids scientific notation on millions and larger\n",
"pd.set_option('display.float_format', lambda x: '%.2f' % x)"
],
"metadata": {
"id": "wsv3gEB9qmp6"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Upload files from your computer here\n",
"# Run the cell and click the \"Browse\" button to upload the provided CSV \n",
"# files\n",
"uploaded = files.upload()"
],
"metadata": {
"id": "4psao7htcAwr"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Read the files as pandas dataframes and print them so you can check that the\n",
"# process went fine\n",
"\n",
"served_orders = pd.read_csv(io.BytesIO(uploaded['served_orders.csv']))\n",
"sourcing_events = pd.read_csv(io.BytesIO(uploaded['sourcing_events.csv']))\n",
"\n",
"for table in (served_orders, sourcing_events):\n",
" print(table.head())"
],
"metadata": {
"id": "X8N0PZ4qcOls"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Analysis"
],
"metadata": {
"id": "GKty74ZfuBEG"
}
},
{
"cell_type": "code",
"source": [
"# You can use this space to analyse the provided data as you see fit."
],
"metadata": {
"id": "CuYSBC2auDIG"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Provided code\n"
],
"metadata": {
"id": "KM5HVJiIfYPC"
}
},
{
"cell_type": "code",
"source": [
"# This cell includes provided code to run simulations.\n",
"# You do not need to understand the internals of this code. Feel free to just\n",
"# run it and move forward.\n",
"# The next section explains how you can call this code to run your simulations.\n",
"\n",
"base = datetime(2022,1,1)\n",
"dates_in_2022 = [base + timedelta(days=x) for x in range(365)]\n",
"\n",
"class SimulationResult:\n",
"\n",
" def __init__(self, stock_states, demand_by_day, sourcing_events):\n",
" self.stock_states = stock_states\n",
" self.demand_by_day = demand_by_day\n",
" self.sourcing_events = sourcing_events\n",
"\n",
" def plot_stock_history(self):\n",
" sns.lineplot(x=dates_in_2022, y=self.stock_states)\n",
"\n",
" def plot_stock_distribution(self):\n",
" sns.histplot(x=self.stock_states, kde=True)\n",
"\n",
" def service_level(self):\n",
" return (self.stock_states > 0 ).astype(int).mean()\n",
"\n",
" def stock_level_summary(self):\n",
" print(\n",
" pd.DataFrame(self.stock_states).describe()\n",
" ) \n",
" \n",
" def mean_stock_level(self):\n",
" return self.stock_states.mean()\n",
"\n",
" def median_stock_level(self):\n",
" return np.median(self.stock_states)\n",
"\n",
" def stdev_stock_level(self):\n",
" return self.stock_states.std()\n",
"\n",
" def mean_demand(self):\n",
" return self.demand_by_day.mean()\n",
" \n",
" def number_of_purchase_orders_placed(self):\n",
" return len(self.sourcing_events)\n",
"\n",
"\n",
"class SimulationConfig:\n",
"\n",
" def __init__(\n",
" self, \n",
" starting_stock_raw_beans,\n",
" starting_stock_roasted_beans,\n",
" starting_stock_decaff_beans, \n",
" demand_generator_raw_beans,\n",
" demand_generator_roasted_beans,\n",
" demand_generator_decaff_beans,\n",
" lead_time_generator_raw_beans, \n",
" purchaser,\n",
" production_line_switcher,\n",
" roasted_beans_daily_production,\n",
" decaff_beans_daily_production\n",
" ):\n",
" self.starting_stock_raw_beans = starting_stock_raw_beans\n",
" self.starting_stock_roasted_beans = starting_stock_roasted_beans\n",
" self.starting_stock_decaff_beans = starting_stock_decaff_beans\n",
" self.demand_generator_raw_beans = demand_generator_raw_beans\n",
" self.demand_generator_roasted_beans = demand_generator_roasted_beans\n",
" self.demand_generator_decaff_beans = demand_generator_decaff_beans\n",
" self.lead_time_generator_raw_beans = lead_time_generator_raw_beans\n",
" self.purchaser = purchaser\n",
" self.production_line_switcher = production_line_switcher\n",
" self.roasted_beans_daily_production = roasted_beans_daily_production\n",
" self.decaff_beans_daily_production = decaff_beans_daily_production\n",
"\n",
"class PurchaseOrder:\n",
" \n",
" def __init__(self, amount, request_date, delivery_date):\n",
" self.amount = amount\n",
" self.request_date = request_date\n",
" self.delivery_date = delivery_date\n",
"\n",
" def __repr__(self):\n",
" return f\"Order of {self.amount:.0f}, requested on {self.request_date}, delivery on {self.delivery_date}.\"\n",
"\n",
"class ProductionLine:\n",
"\n",
" def __init__(self, products_and_rates, starting_product=None):\n",
" self.products_and_rates = products_and_rates\n",
" self.on_the_line = starting_product\n",
" self.next_on_the_line = None\n",
" self.days_on_current_batch = 0\n",
"\n",
" def tick(self):\n",
" if self.on_the_line in self.products_and_rates:\n",
" self.days_on_current_batch += 1\n",
" return self.products_and_rates[self.on_the_line]\n",
"\n",
" if self.on_the_line is None and self.next_on_the_line is None:\n",
" self.days_on_current_batch += 1\n",
" return 0\n",
" if self.on_the_line is None:\n",
" self.on_the_line = self.next_on_the_line\n",
" self.next_on_the_line = None\n",
" self.days_on_current_batch = 0\n",
" return 0\n",
" \n",
" def switch_to_product(self, next_product):\n",
" self.on_the_line = None\n",
" self.next_on_the_line = next_product\n",
" self.days_on_current_batch = -1\n",
"\n",
"\n",
"class Simulation:\n",
" \n",
" def __init__(self, config: SimulationConfig, verbose=False):\n",
" self._config = config\n",
" self.verbose = verbose\n",
"\n",
" def run(self):\n",
"\n",
" stock_raw_beans = np.array([self._config.starting_stock_raw_beans])\n",
" stock_roasted_beans = np.array([self._config.starting_stock_roasted_beans])\n",
" stock_decaff_beans = np.array([self._config.starting_stock_decaff_beans])\n",
"\n",
" opened_orders = []\n",
" ongoing_orders = {}\n",
"\n",
" demand_by_day_raw_beans = np.array(list())\n",
" demand_by_day_roasted_beans = np.array(list())\n",
" demand_by_day_decaff_beans = np.array(list())\n",
"\n",
" production_line = ProductionLine(\n",
" products_and_rates={\n",
" \"roasted_beans\": self._config.roasted_beans_daily_production,\n",
" \"decaff_beans\": self._config.decaff_beans_daily_production\n",
" },\n",
" starting_product=\"roasted_beans\"\n",
" )\n",
"\n",
" production_line_switcher = self._config.production_line_switcher\n",
" \n",
" for day in dates_in_2022:\n",
"\n",
" # General\n",
" if self.verbose:\n",
" print(\"-------------------------\")\n",
" print(f\"Simulating day: {day}\")\n",
" current_stock_raw_beans = stock_raw_beans[-1]\n",
" current_stock_roasted_beans = stock_roasted_beans[-1]\n",
" current_stock_decaff_beans = stock_decaff_beans[-1]\n",
"\n",
" # Generate demand\n",
" if self.verbose:\n",
" print(f\"Starting stock raw beans: {current_stock_raw_beans:.0f}\")\n",
" print(f\"Starting stock roasted beans: {current_stock_roasted_beans:.0f}\")\n",
" print(f\"Starting stock decaff beans: {current_stock_decaff_beans:.0f}\")\n",
" demand_for_this_day_raw_beans = self._config.demand_generator_raw_beans()\n",
" demand_for_this_day_roasted_beans = self._config.demand_generator_roasted_beans()\n",
" demand_for_this_day_decaff_beans = self._config.demand_generator_decaff_beans()\n",
" if self.verbose:\n",
" print(f\"Generated raw beans demand for today: {demand_for_this_day_raw_beans:.0f}\")\n",
" print(f\"Generated roasted beans demand for today: {demand_for_this_day_roasted_beans:.0f}\")\n",
" print(f\"Generated decaff beans demand for today: {demand_for_this_day_decaff_beans:.0f}\")\n",
" demand_by_day_raw_beans = np.append(demand_by_day_raw_beans, [demand_for_this_day_raw_beans])\n",
" demand_by_day_roasted_beans = np.append(demand_by_day_roasted_beans, [demand_for_this_day_roasted_beans])\n",
" demand_by_day_decaff_beans = np.append(demand_by_day_decaff_beans, [demand_for_this_day_decaff_beans])\n",
"\n",
"\n",
" # Receive orders and place orders\n",
" raw_beans_received_this_day = 0\n",
" if day in ongoing_orders:\n",
" order_delivered_today = ongoing_orders.pop(day)\n",
" raw_beans_received_this_day = order_delivered_today.amount\n",
" if self.verbose:\n",
" print(f\"Raw beans received today: {raw_beans_received_this_day:.0f}\")\n",
" \n",
" order_to_make = self._config.purchaser(\n",
" day, \n",
" current_stock_raw_beans, \n",
" ongoing_orders,\n",
" self._config.lead_time_generator_raw_beans\n",
" )\n",
" if order_to_make:\n",
" if self.verbose:\n",
" print(f\"Placing a new order: {order_to_make}\")\n",
" opened_orders.append(order_to_make)\n",
" ongoing_orders[order_to_make.delivery_date] = order_to_make\n",
"\n",
"\n",
" # Decide on production today and produce whatever gets produced or wait during the changeover\n",
"\n",
" print(f\"Product on the line: {production_line.on_the_line}\")\n",
"\n",
" production_line_switcher(\n",
" production_line,\n",
" {\n",
" \"raw_beans_stock\": current_stock_raw_beans,\n",
" \"roasted_beans_stock\": current_stock_roasted_beans,\n",
" \"decaff_beans_stock\": current_stock_decaff_beans\n",
" }\n",
" )\n",
"\n",
" if production_line.on_the_line == \"roasted_beans\":\n",
" roasted_beans_produced_this_day = production_line.tick()\n",
" decaff_beans_produced_this_day = 0\n",
" if production_line.on_the_line == \"decaff_beans\":\n",
" roasted_beans_produced_this_day = 0\n",
" decaff_beans_produced_this_day = production_line.tick()\n",
" if production_line.on_the_line not in (\"roasted_beans\", \"decaff_beans\"):\n",
" production_line.tick()\n",
" roasted_beans_produced_this_day = 0\n",
" decaff_beans_produced_this_day = 0\n",
"\n",
" raw_beans_consumed_in_production = roasted_beans_produced_this_day + decaff_beans_produced_this_day\n",
" \n",
" if self.verbose:\n",
" print(f\"Roasted beans produced today: {roasted_beans_produced_this_day}\")\n",
" print(f\"Decaff beans produced today: {decaff_beans_produced_this_day}\")\n",
" print(f\"Product {production_line.on_the_line} has been on the line for {production_line.days_on_current_batch} days.\")\n",
"\n",
" # Update stocks with the changes of the day\n",
"\n",
" current_stock_raw_beans = (\n",
" current_stock_raw_beans + \n",
" raw_beans_received_this_day - \n",
" demand_for_this_day_raw_beans -\n",
" raw_beans_consumed_in_production\n",
" )\n",
" stock_raw_beans = np.append(stock_raw_beans, [current_stock_raw_beans])\n",
" current_stock_roasted_beans = current_stock_roasted_beans + roasted_beans_produced_this_day - demand_for_this_day_roasted_beans\n",
" stock_roasted_beans = np.append(stock_roasted_beans, [current_stock_roasted_beans])\n",
" current_stock_decaff_beans = current_stock_decaff_beans + decaff_beans_produced_this_day - demand_for_this_day_decaff_beans\n",
" stock_decaff_beans = np.append(stock_decaff_beans, [current_stock_decaff_beans])\n",
" \n",
" # Remove starting stock\n",
" stock_raw_beans = np.delete(stock_raw_beans, 0) \n",
" stock_roasted_beans = np.delete(stock_roasted_beans, 0)\n",
" stock_decaff_beans = np.delete(stock_decaff_beans, 0)\n",
" \n",
" raw_beans_results = SimulationResult(\n",
" stock_states=stock_raw_beans, \n",
" demand_by_day=demand_by_day_raw_beans, \n",
" sourcing_events=opened_orders\n",
" )\n",
" roasted_beans_results = SimulationResult(\n",
" stock_states=stock_roasted_beans, \n",
" demand_by_day=demand_by_day_roasted_beans, \n",
" sourcing_events=None\n",
" )\n",
" decaff_beans_results = SimulationResult(\n",
" stock_states=stock_decaff_beans, \n",
" demand_by_day=demand_by_day_decaff_beans, \n",
" sourcing_events=opened_orders\n",
" )\n",
"\n",
"\n",
" return raw_beans_results, roasted_beans_results, decaff_beans_results\n",
" \n",
"\n",
"\n"
],
"metadata": {
"id": "cGcEzAIDfa8s"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Usage Example"
],
"metadata": {
"id": "Xul3y3LpYKiY"
}
},
{
"cell_type": "code",
"source": [
"# Read this block carefully to understand how to prepare parameters,\n",
"# run simulations and fetch the results.\n",
"\n",
"# These are the steps we will follow:\n",
"# 1. Prepare a purchaser function\n",
"# 2. Prepare a production line management function\n",
"# 3. Assemble a simulation configuration with your parameters and assumptions\n",
"# 4. Run a simulation\n",
"# 5. Fetch results\n",
"\n",
"\n",
"###\n",
"# 1. Prepare a purchaser function\n",
"###\n",
"\n",
"# The purchase function handles the decisions of whether to buy more raw coffee\n",
"# beans to send to Diemen, and how much to buy. It gets called once per simulated\n",
"# day, so you have an oportunity to place orders each day.\n",
"\n",
"\n",
"# You can name your function whatever you like, but the arguments should have\n",
"# the same names and order as show below.\n",
"def a_simple_purchaser(\n",
" day, # The current day\n",
" current_stock, # The level of raw beans stock on that day\n",
" ongoing_orders, # A dictionary with the open purchase orders\n",
" lead_time_generator # The same lead time generator you pass to the Simulation Config\n",
" ):\n",
" # Your code goes here. You can make any logic you want. Just make sure to return\n",
" # None if you don't want to place an order and to return a PurchaseOrder when\n",
" # you want to buy. The policies below are a simple example to inspire you: you\n",
" # definitely want to modify the numbers and/or followed logic.\n",
" \n",
" if ongoing_orders or current_stock > 15_000_000:\n",
" # If we are already waiting for an order to arrive or we have enough stock\n",
" # we don't request more goods.\n",
" return None\n",
"\n",
" if current_stock <= 15_000_000:\n",
" # If the stock is going low, we request more.\n",
" return PurchaseOrder(\n",
" amount=15_000_000, # The amount to order. This is the only bit you change.\n",
" request_date=day, # Always copy paste this.\n",
" delivery_date=day + timedelta(days=lead_time_generator()) # Always copy paste this.\n",
" )\n",
"\n",
"\n",
"###\n",
"# 2. Prepare a production line management function\n",
"### \n",
"\n",
"# The line manager function handles the decision of whether the production line\n",
"# should change to a different product (or no product at all). It gets called \n",
"# once per day.\n",
"\n",
"# You can name your function whatever you like, but the arguments should have\n",
"# the same names and order as show below.\n",
"def a_simple_line_manager(\n",
" production_line, # Details about the production line\n",
" stock_by_product # A summary of the stock that updates each day\n",
"):\n",
" # Your code goes here. You can make any logic you want. Just make sure to \n",
" # switch to None if you don't want to change the product on the line. If you want\n",
" # to switch the product on the line, call production_line.switch_to_product(\"product name\"). \n",
" # The policies below are a simple example to inspire you: you definitely want \n",
" # to modify the numbers and/or followed logic.\n",
" \n",
" # If the current product has been less than 21 days on the line, we don't \n",
" # change anything.\n",
" if production_line.days_on_current_batch < 14:\n",
" return\n",
" \n",
" if (\n",
" stock_by_product[\"roasted_beans_stock\"] > 2_000_000 and \n",
" stock_by_product[\"decaff_beans_stock\"] > 2_000_000 and\n",
" production_line.on_the_line is not None\n",
" ):\n",
" # If we have plenty of stock and we are still producing, we stop the line\n",
" # by switching to None.\n",
" production_line.switch_to_product(None)\n",
" print(\"Too much inventory. I'm switching to None!\")\n",
" return\n",
"\n",
" if (\n",
" stock_by_product[\"roasted_beans_stock\"] > 2_000_000 and \n",
" stock_by_product[\"decaff_beans_stock\"] > 2_000_000 and\n",
" production_line.on_the_line is None\n",
" ):\n",
" # If we have plenty of stock and we are stopped, we remain stopped.\n",
" print(\"Too much inventory. Staying in None!\")\n",
" return\n",
"\n",
" \n",
" if (\n",
" (\n",
" max(stock_by_product[\"roasted_beans_stock\"], 1) / max(stock_by_product[\"decaff_beans_stock\"], 1) < 2\n",
" ) and (\n",
" production_line.on_the_line != \"roasted_beans\"\n",
" )\n",
" ):\n",
" # If we are not producing roasted beans, and there is less than 2kg of roasted beans\n",
" # for each kg of decaff beans in stock, we switch to roasted beans.\n",
" production_line.switch_to_product(\"roasted_beans\")\n",
" print(\"I'm switching to roasted!\")\n",
" return\n",
"\n",
" if (\n",
" (\n",
" max(stock_by_product[\"roasted_beans_stock\"], 1) / max(stock_by_product[\"decaff_beans_stock\"], 1) > 2\n",
" ) and (\n",
" production_line.on_the_line != \"decaff_beans\"\n",
" )\n",
" ):\n",
" # If we are not producing decaff beans, and there is more than 2kg of roasted beans\n",
" # for each kg of decaff beans in stock, we switch to decaff beans.\n",
" production_line.switch_to_product(\"decaff_beans\")\n",
" print(\"I'm switching to decaff!\")\n",
" return\n",
"\n",
"\n",
"###\n",
"# 3. Assemble a simulation configuration\n",
"### \n",
"\n",
"# In order to run as Simulation, you must prepare a config. The config allows \n",
"# you to pass in your policies as well as to modify different parts of the \n",
"# simulation so you can recreate reality accurately. You can find each argument\n",
"# explained below.\n",
"\n",
"an_example_config = SimulationConfig(\n",
" starting_stock_raw_beans=20_000_000, \n",
" # ^ How many kgs of raw coffee beans does the warehouse start with.\n",
" starting_stock_roasted_beans=1_000_000,\n",
" # ^ How many kgs of roasted coffee beans does the warehouse start with. \n",
" starting_stock_decaff_beans=500_000,\n",
" # ^ How many kgs of decaff coffee beans does the warehouse start with.\n",
" demand_generator_raw_beans=lambda: np.random.poisson(5/7) * np.random.normal(300_000, 50_000),\n",
" # ^ A function that generates demand for raw beans. This gets called daily.\n",
" # The return units should be kilograms.\n",
" demand_generator_roasted_beans=lambda: np.random.poisson(4/7) * np.random.triangular(200_000, 250_000, 300_000),\n",
" # ^ Same as above but for roasted beans.\n",
" demand_generator_decaff_beans=lambda: np.random.poisson(1/7) * np.random.triangular(200_000, 250_000, 300_000),\n",
" # ^ Same as above but for decaff beans.\n",
" lead_time_generator_raw_beans=lambda: int(np.random.normal(30, 1)), \n",
" # ^ A function that generates the lead times for ships going from Latin America\n",
" # to Diemen. This gets called everytime you place an order to get more raw\n",
" # beans. Should return an integer number of days. \n",
" purchaser=a_simple_purchaser,\n",
" # ^ Here you pass your purchasing policy function.\n",
" production_line_switcher=a_simple_line_manager,\n",
" # ^ Here you pass your production line management policy.\n",
" roasted_beans_daily_production=250_000,\n",
" # ^ The capacity of normal bean roasting, in kgs per day.\n",
" decaff_beans_daily_production=150_000\n",
" # ^ The capacity of decaff bean roasting, in kgs per day.\n",
")\n",
"\n",
"###\n",
"# 4. Run a simulation\n",
"###\n",
"\n",
"# The Simulation class is the code that actually runs a simulation. It takes a \n",
"# SimulationConfig as an input, and returns a SimulationResult as an output.\n",
"\n",
"example_simulation = Simulation(\n",
" config=an_example_config, # The config you build goes here\n",
" verbose=True # This shows daily details. Turn to False if you don't want to see them.\n",
")\n",
"\n",
"# Let's run the simulation and store the results\n",
"raw_results, roasted_results, decaff_results = example_simulation.run()\n",
"\n",
"\n",
"###\n",
"# 5. Fetch results\n",
"###\n",
"\n",
"# The simulation will provide you back with three SimulationResult variables, \n",
"# one for each type of coffee bean. You can use this objects to make some quick\n",
"# plots, and also to access the raw data about the stock and demand throughout\n",
"# the simulation for each product.\n",
"\n",
"# In the next cells, you will find a few examples on how to explore these \n",
"# results\n"
],
"metadata": {
"id": "cYHFWRrm3-ns"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"raw_results.plot_stock_history()\n",
"roasted_results.plot_stock_history()\n",
"decaff_results.plot_stock_history()"
],
"metadata": {
"id": "iXeAFhIp7848"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"raw_results.plot_stock_distribution()\n",
"roasted_results.plot_stock_distribution()\n",
"decaff_results.plot_stock_distribution()"
],
"metadata": {
"id": "RLRHLyVOiysH"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"for product, result in ((\"raw\", raw_results), (\"roasted\", roasted_results), (\"decaff\", decaff_results)):\n",
" print(f\"{product} beans service level: {result.service_level()}\")\n",
" print(f\"{product} beans mean stock: {result.mean_stock_level()}\")\n",
" print(f\"{product} beans median stock: {result.median_stock_level()}\")\n",
" print(f\"{product} beans stdev stock: {result.stdev_stock_level()}\")\n",
" print(f\"{product} beans mean demand: {result.mean_demand()}\")"
],
"metadata": {
"id": "Cr-Txnk9jFJK"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"for product, result in ((\"raw\", raw_results), (\"roasted\", roasted_results), (\"decaff\", decaff_results)):\n",
" print(f\"Daily stock distribution summary for {product} beans:\")\n",
" print(result.stock_level_summary())"
],
"metadata": {
"id": "z1Ixv0bHqD65"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Finally, you can access the raw data with the following attributes\n",
"print(roasted_results.stock_states)\n",
"print(roasted_results.demand_by_day)"
],
"metadata": {
"id": "-pw38uGpm-Aa"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Your turn\n",
"\n",
"Run the previous cells in order to load the required packages and code. Once you\n",
"have done that, you can start building your own code below."
],
"metadata": {
"id": "qSOiFi9OmgUR"
}
},
{
"cell_type": "code",
"source": [],
"metadata": {
"id": "fOZ7KhC5rgYc"
},
"execution_count": null,
"outputs": []
}
]
}