{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Modeling and Simulation in Python\n", "\n", "Chapter 2: Simulation\n", "\n", "Copyright 2017 Allen Downey\n", "\n", "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll start with the same code we saw last time: the magic command that tells Jupyter where to put the figures, and the import statement that gets the functions defined in the `modsim` module." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# If you want the figures to appear in the notebook, \n", "# and you want to interact with them, use\n", "# %matplotlib notebook\n", "\n", "# If you want the figures to appear in the notebook, \n", "# and you don't want to interact with them, use\n", "# %matplotlib inline\n", "\n", "# If you want the figures to appear in separate windows, use\n", "# %matplotlib qt5\n", "\n", "%matplotlib notebook\n", "\n", "from modsim import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More than one System object\n", "\n", "Here's the code from the previous chapter, with two changes:\n", "\n", "1. I've added DocStrings that explain what each function does, and what parameters it takes.\n", "\n", "2. I've added a parameter named `system` to the functions so they work with whatever `System` object we give them, instead of always using `bikeshare`. That makes it possible to work with more than one `System` object." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def run_steps(system, num_steps, p1, p2):\n", " \"\"\"Simulate the given number of time steps.\n", " \n", " system: bikeshare System object\n", " num_steps: number of time steps\n", " p1: probability of an Olin->Wellesley customer arrival\n", " p2: probability of a Wellesley->Olin customer arrival\n", " \"\"\"\n", " for i in range(num_steps):\n", " step(system, p1, p2)\n", " plot_system(system)\n", " \n", "def step(system, p1, p2):\n", " \"\"\"Simulate one minute of time.\n", " \n", " system: bikeshare System object\n", " p1: probability of an Olin->Wellesley customer arrival\n", " p2: probability of a Wellesley->Olin customer arrival\n", " \"\"\"\n", " if flip(p1):\n", " bike_to_wellesley(system)\n", " \n", " if flip(p2):\n", " bike_to_olin(system)\n", " \n", "def bike_to_wellesley(system):\n", " \"\"\"Move one bike from Olin to Wellesley.\n", " \n", " system: bikeshare System object\n", " \"\"\"\n", " move_bike(system, 1)\n", " \n", "def bike_to_olin(system):\n", " \"\"\"Move one bike from Wellesley to Olin.\n", " \n", " system: bikeshare System object\n", " \"\"\"\n", " move_bike(system, -1)\n", " \n", "def move_bike(system, n):\n", " \"\"\"Move a bike.\n", " \n", " system: bikeshare System object\n", " n: +1 to move from Olin to Wellesley or\n", " -1 to move from Wellesley to Olin\n", " \"\"\"\n", " system.olin -= n\n", " system.wellesley += n\n", " \n", "def plot_system(system):\n", " \"\"\"Plot the current system of the bikeshare system.\n", " \n", " system: bikeshare System object\n", " \"\"\"\n", " mark(system.olin, 'rs-', label='Olin')\n", " mark(system.wellesley, 'bo-', label='Wellesley')\n", " \n", "def decorate_bikeshare():\n", " \"\"\"Add a title and label the axes.\"\"\"\n", " decorate(title='Olin-Wellesley Bikeshare',\n", " xlabel='Time step (min)', \n", " ylabel='Number of bikes')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can create more than one `System` object:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare1 = System(olin=10, wellesley=2)\n", "bikeshare1" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare2 = System(olin=10, wellesley=2)\n", "bikeshare2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And whenever we call a function, we indicate which `System` object to work with:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "bike_to_olin(bikeshare1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "bike_to_wellesley(bikeshare2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And you can confirm that the different systems are getting updated independently:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare1" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Negative bikes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the code we have so far, the number of bikes at one of the locations can go negative, and the number of bikes at the other location can exceed the actual number of bikes in the system.\n", "\n", "If you run this simulation a few times, it happens quite often." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare = System(olin=10, wellesley=2)\n", "newfig()\n", "plot_system(bikeshare)\n", "decorate_bikeshare()\n", "run_steps(bikeshare, 60, 0.4, 0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But this is relatively easy to fix, using the `return` statement to exit the function early if the update would cause negative bikes.\n", "\n", "If the second `if` statement seems confusing, remember that `n` can be negative." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def move_bike(system, n):\n", " # make sure the number of bikes won't go negative\n", " olin_temp = system.olin - n\n", " if olin_temp < 0:\n", " return\n", " \n", " wellesley_temp = system.wellesley + n\n", " if wellesley_temp < 0:\n", " return\n", " \n", " # update the system\n", " system.olin = olin_temp\n", " system.wellesley = wellesley_temp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now if you run the simulation again, it should behave." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare = System(olin=10, wellesley=2)\n", "newfig()\n", "plot_system(bikeshare)\n", "decorate_bikeshare()\n", "run_steps(bikeshare, 60, 0.4, 0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The variables `olin` and `wellesley` are created inside `move_bike`, so they are local. When the function ends, they go away.\n", "\n", "If you try to access a local variable from outside its function, you get an error:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# If you remove the # from the last line in this cell and run it, you'll get\n", "# NameError: name 'olin' is not defined\n", "\n", "#olin" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Add print statements in `move_bike` so it prints a message each time a customer arrives and doesn't find a bike. Run the simulation again to confirm that it works as you expect. Then you might want to remove the print statements before you go on." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comparison operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `if` statements in the previous section used the comparison operator `<`. The other comparison operators are listed in the book.\n", "\n", "It is easy to confuse the comparison operator `==` with the assignment operator `=`.\n", "\n", "Remember that `=` creates a variable or gives an existing variable a new value." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "x = 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whereas `==` compared two values and returns `True` if they are equal." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x == 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can use `==` in an `if` statement." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if x == 5:\n", " print('yes, x is 5')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But if you use `=` in an `if` statement, you get an error." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# If you remove the # from the if statement and run it, you'll get\n", "# SyntaxError: invalid syntax\n", "\n", "#if x = 5:\n", "# print('yes, x is 5')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Add an `else` clause to the `if` statement above, and print an appropriate message.\n", "\n", "Replace the `==` operator with one or two of the other comparison operators, and confirm they do what you expect." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Metrics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have a working simulation, we'll use it to evaluate alternative designs and see how good or bad they are. The metric we'll use is the number of customers who arrive and find no bikes available, which might indicate a design problem." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we'll make a new `System` object that creates and initializes the system variables that will keep track of the metrics." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "bikeshare = System(olin=10, wellesley=2, \n", " olin_empty=0, wellesley_empty=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we need a version of `move_bike` that updates the metrics." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def move_bike(system, n):\n", " olin_temp = system.olin - n\n", " if olin_temp < 0:\n", " system.olin_empty += 1\n", " return\n", " \n", " wellesley_temp = system.wellesley + n\n", " if wellesley_temp < 0:\n", " system.wellesley_empty += 1\n", " return\n", " \n", " system.olin = olin_temp\n", " system.wellesley = wellesley_temp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now when we run a simulation, it keeps track of unhappy customers." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "newfig()\n", "plot_system(bikeshare)\n", "decorate_bikeshare()\n", "run_steps(bikeshare, 60, 0.4, 0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the simulation, we can print the number of unhappy customers at each location." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare.olin_empty" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare.wellesley_empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Let's add a \"clock\" to keep track of how many time steps have elapsed:\n", "\n", "1. Add a new system variable named `clock` to `bikeshare`, initialized to 0, and \n", "\n", "2. Modify `step` so it increments (adds one to) `clock` each time it is invoked.\n", "\n", "Test your code by adding a print statement that prints the value of `clock` at the beginning of each time step." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Here's a copy of step to get you started\n", "\n", "def step(system, p1, p2):\n", " \"\"\"Simulate one minute of time.\n", " \n", " system: bikeshare System object\n", " p1: probability of an Olin->Wellesley customer arrival\n", " p2: probability of a Wellesley->Olin customer arrival\n", " \"\"\"\n", " if flip(p1):\n", " bike_to_wellesley(system)\n", " \n", " if flip(p2):\n", " bike_to_olin(system)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Solution\n", "\n", "bikeshare = System(olin=10, wellesley=2, \n", " olin_empty=0, wellesley_empty=0,\n", " clock=0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Solution\n", "\n", "def step(system, p1, p2):\n", " \"\"\"Simulate one minute of time.\n", " \n", " system: bikeshare System object\n", " p1: probability of an Olin->Wellesley customer arrival\n", " p2: probability of a Wellesley->Olin customer arrival\n", " \"\"\"\n", " system.clock += 1\n", " # print(system.clock)\n", " \n", " if flip(p1):\n", " bike_to_wellesley(system)\n", " \n", " if flip(p2):\n", " bike_to_olin(system)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "newfig()\n", "plot_system(bikeshare)\n", "decorate_bikeshare()\n", "run_steps(bikeshare, 10, 0.4, 0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the simulation, check the final value of `clock`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# print(bikeshare.clock)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Now suppose we'd like to know how long it takes to run out of bikes at either location. Modify `move_bike` so the first time a student arrives at Olin and doesn't find a bike, it records the value of `clock` in a system variable.\n", "\n", "Hint: create a system variable named `t_first_empty` and initialize it to `-1` to indicate that it has not been set yet.\n", "\n", "Test your code by running a simulation for 60 minutes and checking the metrics." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "bikeshare = System(olin=10, wellesley=2, \n", " olin_empty=0, wellesley_empty=0,\n", " clock=0, t_first_empty=-1)\n", "\n", "bikeshare" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Solution\n", "\n", "def move_bike(system, n):\n", " olin_temp = system.olin - n\n", " if olin_temp < 0:\n", " system.olin_empty += 1\n", " if system.t_first_empty == -1:\n", " system.t_first_empty = system.clock\n", " return\n", " \n", " wellesley_temp = system.wellesley + n\n", " if wellesley_temp < 0:\n", " system.wellesley_empty += 1\n", " return\n", " \n", " system.olin = olin_temp\n", " system.wellesley = wellesley_temp" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "newfig()\n", "plot_system(bikeshare)\n", "decorate_bikeshare()\n", "run_steps(bikeshare, 60, 0.4, 0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the simulation, check the final value of `t_first_empty`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# print(bikeshare.t_first_empty)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Returning values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's a simple function that returns a value:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def add_five(x):\n", " return x + 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And here's how we call it." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y = add_five(3)\n", "y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you run a function on the last line of a cell, Jupyter displays the result:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "add_five(5)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "But that can be a bad habit, because usually if you call a function and don't assign the result in a variable, the result gets discarded.\n", "\n", "In the following example, Jupyter shows the second result, but the first result just disappears." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "add_five(3)\n", "add_five(5)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "When you call a function that returns a variable, it is generally a good idea to assign the result to a variable." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y1 = add_five(3)\n", "y2 = add_five(5)\n", "\n", "print(y1, y2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Write a function called `make_system` that creates a `System` object with the system variables `olin=10` and `wellesley=2`, and then returns the new `System` object.\n", "\n", "Write a line of code that calls `make_system` and assigns the result to a variable." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Solution\n", "\n", "def make_system():\n", " system = System(olin=10, wellesley=2)\n", " return system" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "system = make_system()\n", "system" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running simulations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we go on, let's put `step` and `move_bike` back the way we found them, so they don't break the examples below." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def step(system, p1, p2):\n", " if flip(p1):\n", " bike_to_wellesley(system)\n", " \n", " if flip(p2):\n", " bike_to_olin(system)\n", "\n", "def move_bike(system, n):\n", " olin_temp = system.olin - n\n", " if olin_temp < 0:\n", " system.olin_empty += 1\n", " return\n", " \n", " wellesley_temp = system.wellesley + n\n", " if wellesley_temp < 0:\n", " system.wellesley_empty += 1\n", " return\n", " \n", " system.olin = olin_temp\n", " system.wellesley = wellesley_temp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And I want to update `run_steps` so it doesn't plot the results." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def run_steps(system, num_steps, p1, p2):\n", " \"\"\"Simulate the given number of time steps.\n", " \n", " `num_steps` should be an integer; if not, it gets rounded down.\n", " \n", " system: bikeshare System object\n", " num_steps: number of time steps\n", " p1: probability of an Olin->Wellesley customer arrival\n", " p2: probability of a Wellesley->Olin customer arrival\n", " \"\"\"\n", " for i in range(num_steps):\n", " step(system, p1, p2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now when we run a simulation, we can choose not to plot the results:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "bikeshare = System(olin=10, wellesley=2, \n", " olin_empty=0, wellesley_empty=0)\n", "run_steps(bikeshare, 60, 0.4, 0.2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But after the simulation, we can still read the metrics." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bikeshare.olin_empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's wrap all that in a function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def run_simulation(p1, p2):\n", " bikeshare = System(olin=10, wellesley=2, \n", " olin_empty=0, wellesley_empty=0)\n", " run_steps(bikeshare, 60, p1, p2)\n", " return bikeshare" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And test it." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "system = run_simulation(0.4, 0.2)\n", "system" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can run simulations with a range of values for the parameters.\n", "\n", "When `p1` is small, we probably don't run out of bikes at Olin." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "system = run_simulation(0.2, 0.2)\n", "system.olin_empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When `p1` is large, we probably do." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "system = run_simulation(0.6, 0.2)\n", "system.olin_empty" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## More for loops" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`linspace` creates a NumPy array of equally spaced numbers." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p1_array = linspace(0, 1, 5)\n", "p1_array" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use an array in a `for` loop, like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for p1 in p1_array:\n", " print(p1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will come in handy in the next section.\n", "\n", "`linspace` is defined in `modsim.py`. You can read the documentation by running the following cell:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "help(linspace)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`linspace` is based on a NumPy function with the same name. Click on the link above to read more about how to use it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** \n", "Use `linspace` to make an array of 10 equally spaced numbers from 1 to 10 (including both)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "linspace(1, 10, 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** The `modsim` library provides a related function called `linrange`. You can view the documentation by running the following cell:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "help(linrange)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `linrange` to make an array of numbers from 1 to 11 with a step size of 2." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "linrange(1, 11, 2)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Sweeping parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example runs simulations with a range of values for `p1`; after each simulation, it prints the number of unhappy customers at the Olin station:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p1_array = linspace(0, 1, 11)\n", "p1_array" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for p1 in p1_array:\n", " system = run_simulation(p1, 0.2)\n", " print(p1, system.olin_empty)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can do the same thing, but plotting the results instead of printing them.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "newfig()\n", "for p1 in p1_array:\n", " system = run_simulation(p1, 0.2)\n", " mark(p1, system.olin_empty, 'rs', label='olin')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As always, we should label the axes and give the figure a title." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "decorate(title='Olin-Wellesley Bikeshare',\n", " xlabel='Arrival rate at Olin (p1 in customers/min)', \n", " ylabel='Number of unhappy customers')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Wrap this code in a function named `parameter_sweep` that takes an array called `p1_array` as a parameter. It should create a new figure, run a simulation for each value of `p1` in `p1_array`, and plot the results.\n", "\n", "Once you have the function working, modify it so it also plots the number of unhappy customers at Wellesley. Looking at the plot, can you estimate a range of values for `p1` that minimizes the total number of unhappy customers?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Solution\n", "\n", "def parameter_sweep(p1_array):\n", " for p1 in p1_array:\n", " system = run_simulation(p1, 0.2)\n", " mark(p1, system.olin_empty, 'rs', label='Olin')\n", " mark(p1, system.wellesley_empty, 'bo', label='Wellesley')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "newfig()\n", "p1_array = linspace(0, 1, 101)\n", "parameter_sweep(p1_array)\n", "decorate(title='Olin-Wellesley Bikeshare',\n", " xlabel='Arrival rate at Olin (p1 in customers/min)', \n", " ylabel='Number of unhappy customers')" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "**Exercise:** Write a function called `parameter_sweep2` that runs simulations with `p1=0.3` and a range of values for `p2`.\n", "\n", "Note: If you run `parameter_sweep2` a few times without calling `newfig`, you can plot multiple runs on the same axes, which will give you a sense of how much random variation there is from one run to the next. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Solution\n", "\n", "def parameter_sweep2(p2_array):\n", " for p2 in p2_array:\n", " system = run_simulation(0.3, p2)\n", " mark(p2, system.olin_empty, 'rs', label='Olin')\n", " mark(p2, system.wellesley_empty, 'bo', label='Wellesley')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "newfig()\n", "p2_array = linspace(0, 1, 101)\n", "parameter_sweep2(p2_array)\n", "decorate(title='Olin-Wellesley Bikeshare',\n", " xlabel='Arrival rate at Wellesley (p2 in customers/min)', \n", " ylabel='Number of unhappy customers')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "parameter_sweep2(p2_array)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "**Exercise:** The code below runs a simulation with the same parameters 10 times and computes the average number of unhappy customers.\n", "\n", "1. Wrap this code in a function called `run_simulations` that takes `p1` as a parameter.\n", "\n", "2. Run this function with a range of values of `p1` and `p2=0.2`, and make a plot that shows the average number of unhappy customers as a function `p1`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "num_runs = 100\n", "total = 0\n", "for i in range(num_runs):\n", " system = run_simulation(0.4, 0.2)\n", " total += system.olin_empty + system.wellesley_empty\n", "total / num_runs" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Solution\n", "\n", "def run_simulations(p1):\n", " num_runs = 100\n", " total = 0\n", " for i in range(num_runs):\n", " system = run_simulation(p1, 0.2)\n", " total += system.olin_empty + system.wellesley_empty\n", " return total / num_runs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "run_simulations(0.1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "run_simulations(0.9)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Solution\n", "\n", "p1_array = linspace(0, 1, 20)\n", "\n", "for p1 in p1_array:\n", " avg = run_simulations(p1)\n", " print(p1, avg)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "newfig()\n", "for p1 in p1_array:\n", " avg = run_simulations(p1)\n", " mark(p1, avg, 'bo-')\n", " \n", "decorate(title='Olin-Wellesley Bikeshare',\n", " xlabel='Arrival rate at Olin (p1 in customers/min)', \n", " ylabel='Average number of unhappy customers')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "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.6.1" } }, "nbformat": 4, "nbformat_minor": 1 }