diff --git a/chapters/chap01.ipynb b/chapters/chap01.ipynb index c5c409ae..5183b65d 100644 --- a/chapters/chap01.ipynb +++ b/chapters/chap01.ipynb @@ -1,1351 +1,2141 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "a8b892c8", - "metadata": {}, - "source": [ - "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." - ] - }, - { - "cell_type": "markdown", - "id": "affecting-malta", - "metadata": {}, - "source": [ - "# Modeling" - ] - }, - { - "cell_type": "markdown", - "id": "pressed-palestinian", - "metadata": { - "tags": [] - }, - "source": [ - "*Modeling and Simulation in Python*\n", - "\n", - "Copyright 2021 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" - ] - }, - { - "cell_type": "markdown", - "id": "confirmed-budapest", - "metadata": { - "tags": [] - }, - "source": [ - "## Jupyter\n", - "\n", - "Welcome to *Modeling and Simulation in Python*, and welcome to Jupyter.\n", - "\n", - "This is a Jupyter notebook, which is a development environment where you can write and run Python code. Each notebook is divided into cells. Each cell contains either text (like this cell) or Python code." - ] - }, - { - "cell_type": "markdown", - "id": "danish-scope", - "metadata": { - "tags": [] - }, - "source": [ - "To run a cell, hold down SHIFT and press ENTER. \n", - "\n", - "* If you run a text cell, Jupyter formats the text and displays the result.\n", - "\n", - "* If you run a code cell, Jupyter runs the Python code in the cell and displays the result, if any.\n", - "\n", - "The following cells check whether the libraries we need are installed. If so, the cells produce no output. If not, you'll see updates from the installer." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "passive-dayton", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# install Pint if necessary\n", - "\n", - "try:\n", - " from pint import UnitRegistry\n", - "except ImportError:\n", - " !pip install pint" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "prompt-committee", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# download modsim.py if necessary\n", - "\n", - "from os.path import basename, exists\n", - "\n", - "def download(url):\n", - " filename = basename(url)\n", - " if not exists(filename):\n", - " from urllib.request import urlretrieve\n", - " local, _ = urlretrieve(url, filename)\n", - " print('Downloaded ' + local)\n", - " \n", - "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", - " 'modsim.py')" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "fifteen-train", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# import functions from modsim\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "id": "chronic-moore", - "metadata": {}, - "source": [ - "This chapter introduces the modeling framework we will use throughout the book, and works through our first example, using a simple model of physics to evaluate the claim that a penny falling from the height of the Empire State Building could kill someone if it hit them on the head. Also, you'll see how to do computation in Python with units like meters and seconds." - ] - }, - { - "cell_type": "markdown", - "id": "extensive-proposition", - "metadata": {}, - "source": [ - "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", - "Click here to access the notebooks: ." - ] - }, - { - "cell_type": "markdown", - "id": "hourly-financing", - "metadata": {}, - "source": [ - "## The Modeling Framework\n", - "\n", - "This book is about modeling and simulating physical systems. The\n", - "following diagram shows what I mean by *modeling*:\n", - "\n", - "![Diagram of the modeling framework.](https://github.com/AllenDowney/ModSim/raw/main/figs/modeling_framework.png)" - ] - }, - { - "cell_type": "markdown", - "id": "structured-receiver", - "metadata": {}, - "source": [ - "Starting in the lower left, the *system* is something in the real\n", - "world we are interested in. \n", - "To model the system, we have to decide which elements of the real world to include and which we can leave out.\n", - "This process is called *abstraction*.\n", - "\n", - "The result of abstraction is a *model*, which is a description of the system that includes only the features we think are essential. A model\n", - "can be represented in the form of diagrams and equations, which can be\n", - "used for mathematical *analysis*. It can also be implemented in the\n", - "form of a computer program, which can run *simulations*.\n", - "\n", - "The result of analysis and simulation might be a *prediction* about\n", - "what the system will do, an *explanation* of why it behaves the way it\n", - "does, or a *design* intended to achieve a purpose.\n", - "\n", - "We can *validate* predictions and test designs by taking\n", - "*measurements* from the real world and comparing the *data* we get\n", - "with the results from analysis and simulation." - ] - }, - { - "cell_type": "markdown", - "id": "center-accommodation", - "metadata": {}, - "source": [ - "For any physical system, there are many possible models, each one\n", - "including and excluding different features, or including different\n", - "levels of detail. The goal of the modeling process is to find the model\n", - "best suited to its purpose (prediction, explanation, or design).\n", - "\n", - "Sometimes the best model is the most detailed. If we include more\n", - "features, the model is more realistic, and we expect its predictions to\n", - "be more accurate.\n", - "But often a simpler model is better. If we include only the essential\n", - "features and leave out the rest, we get models that are easier to work\n", - "with, and the explanations they provide can be clearer and more\n", - "compelling." - ] - }, - { - "cell_type": "markdown", - "id": "confirmed-highlight", - "metadata": {}, - "source": [ - "As an example, suppose someone asks you why the orbit of the Earth is\n", - "elliptical. If you model the Earth and Sun as point masses (ignoring\n", - "their actual size), compute the gravitational force between them using\n", - "Newton's law of universal gravitation, and compute the resulting orbit\n", - "using Newton's laws of motion, you can show that the result is an\n", - "ellipse.\n", - "Of course, the actual orbit of Earth is not a perfect ellipse, because\n", - "of the gravitational forces of the Moon, Jupiter, and other objects in\n", - "the solar system, and because Newton's laws of motion are only\n", - "approximately true (they don't take into account relativistic effects).\n", - "But adding these features to the model would not improve the\n", - "explanation; more detail would only be a distraction from the\n", - "fundamental cause. However, if the goal is to predict the position of\n", - "the Earth with great precision, including more details might be\n", - "necessary." - ] - }, - { - "cell_type": "markdown", - "id": "stretch-geneva", - "metadata": {}, - "source": [ - "Choosing the best model depends on what the model is for. It is usually\n", - "a good idea to start with a simple model, even if it is likely to be too\n", - "simple, and test whether it is good enough for its purpose. Then you can\n", - "add features gradually, starting with the ones you expect to be most\n", - "essential. This process is called *iterative modeling*.\n", - "\n", - "Comparing results of successive models provides a form of *internal\n", - "validation*, so you can catch conceptual, mathematical, and software\n", - "errors. And by adding and removing features, you can tell which ones\n", - "have the biggest effect on the results, and which can be ignored.\n", - "\n", - "Comparing results to data from the real world provides *external\n", - "validation*, which is generally the strongest test.\n", - "\n", - "The modeling framework is pretty abstract; the following example might make it clearer." - ] - }, - { - "cell_type": "markdown", - "id": "criminal-lunch", - "metadata": {}, - "source": [ - "## The Falling Penny Myth\n", - "\n", - "You might have heard that a\n", - "penny dropped from the top of the Empire State Building would be going\n", - "so fast when it hit the pavement that it would be embedded in the\n", - "concrete; or if it hit a person, it would break their skull.\n", - "\n", - "We can test this myth by making and analyzing two models. For the first model,\n", - "we'll assume that the effect of air resistance is small. In that case, the primary force acting on the penny is gravity, which causes the penny to accelerate downward.\n", - "\n", - "If the initial velocity is 0 and the acceleration, $a$, is constant, the velocity after $t$ seconds is \n", - "\n", - "$$v = a t$$\n", - "\n", - "and the distance the penny has dropped is \n", - "\n", - "$$x = a t^2 / 2$$ \n", - "\n", - "To find the time until the penny reaches the sidewalk, we can solve for $t$:\n", - "\n", - "$$t = \\sqrt{ 2 x / a}$$ \n", - "\n", - "Plugging in the acceleration of gravity, $a = 9.8$ m/s$^2$, and the height of the Empire State Building, $x = 381$ m, we get $t = 8.8$ s. \n", - "\n", - "Then computing $v = a t$ we get a velocity on impact of $86$ m/s, which is about 190 miles per hour. That sounds like it could hurt." - ] - }, - { - "cell_type": "markdown", - "id": "documentary-diagnosis", - "metadata": {}, - "source": [ - "Of course, these results are not exact because the model is based on simplifications. For example, we assume that gravity is constant. \n", - "In fact, the force of gravity is different on different parts of the globe, and it gets weaker as you move away from the surface. \n", - "But these differences are small, so ignoring them is probably a good choice for this problem.\n", - "\n", - "On the other hand, ignoring air resistance is not a good choice, because in this scenario its effect is substantial.\n", - "Once the penny gets to about 29 m/s, the upward force of air resistance equals the downward force of gravity, so the penny stops accelerating.\n", - "This is the *terminal velocity* of the penny in air.\n", - "\n", - "And that suggests a second model, where the penny accelerates until it reaches terminal velocity; after that, acceleration is 0 and velocity is constant.\n", - "In this model, the penny hits the sidewalk at about 29 m/s.\n", - "That's much less than 86 m/s, which is what the first model predicts.\n", - "Getting hit with a penny at that speed might hurt, but it would be unlikely to cause real harm. And it would not damage concrete." - ] - }, - { - "cell_type": "markdown", - "id": "recent-appliance", - "metadata": {}, - "source": [ - "The statistician George Box famously said \"All models are wrong, but\n", - "some are useful.\" He was talking about statistical models, but his wise words apply to all kinds of models. Our first model, which ignores air resistance, is very wrong, and probably not useful. The second model, which takes air resistance into account, is still wrong, but it's better, and it's good enough to refute the myth.\n", - "\n", - "The television show *Mythbusters* has tested the myth of the falling\n", - "penny more carefully; you can view the results at\n", - ". Their work is based on a mathematical model of motion, measurements to determine the force of air resistance on a penny, and a physical model of a human head." - ] - }, - { - "cell_type": "markdown", - "id": "brief-zoning", - "metadata": {}, - "source": [ - "## Computation In Python\n", - "\n", - "Let me show you how I computed the results from the\n", - "previous section using Python.\n", - "First we'll create a variable to represent acceleration due to gravity in meters per second squared (m/s$^2$)." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "eleven-marine", - "metadata": {}, - "outputs": [], - "source": [ - "a = 9.8" - ] - }, - { - "cell_type": "markdown", - "id": "upset-myanmar", - "metadata": {}, - "source": [ - "A *variable* is a name that corresponds to a value. In this example, the name is `a` and the value is the number `9.8`.\n", - "\n", - "Suppose we let the penny drop for $3.4$ seconds (s). I'll create a variable to represent this time:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "following-launch", - "metadata": {}, - "outputs": [], - "source": [ - "t = 3.4" - ] - }, - { - "cell_type": "markdown", - "id": "greek-heritage", - "metadata": {}, - "source": [ - "Now we can compute the velocity of the penny after `t` seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "mature-duration", - "metadata": {}, - "outputs": [], - "source": [ - "v = a * t" - ] - }, - { - "cell_type": "markdown", - "id": "serial-pilot", - "metadata": {}, - "source": [ - "Python uses the symbol `*` for multiplication. The other arithmetic operators are `+` for addition, `-` for subtraction, `/` for division, and `**` for exponentiation." - ] - }, - { - "cell_type": "markdown", - "id": "qualified-diabetes", - "metadata": {}, - "source": [ - "After you assign a value to a variable, you can display the value like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "considered-inclusion", - "metadata": {}, - "outputs": [], - "source": [ - "v" - ] - }, - { - "cell_type": "markdown", - "id": "northern-saturday", - "metadata": {}, - "source": [ - "After $3.4$ s, the velocity of the penny is about $33$ m/s (ignoring air resistance). Now let's see how far it would travel during that time:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "valued-electricity", - "metadata": {}, - "outputs": [], - "source": [ - "x = a * t**2 / 2\n", - "x" - ] - }, - { - "cell_type": "markdown", - "id": "yellow-business", - "metadata": {}, - "source": [ - "It would travel about $56$ m. Now, going in the other direction, let's compute the time it takes to fall 381 m, the height of the Empire State Building." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "closed-month", - "metadata": {}, - "outputs": [], - "source": [ - "h = 381" - ] - }, - { - "cell_type": "markdown", - "id": "fuzzy-lease", - "metadata": {}, - "source": [ - "For this computation, we need the square root function, `sqrt`, which is provided by a library called NumPy.\n", - "Before we can use it, we have to import it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "nuclear-clone", - "metadata": {}, - "outputs": [], - "source": [ - "from numpy import sqrt" - ] - }, - { - "cell_type": "markdown", - "id": "unlimited-swiss", - "metadata": {}, - "source": [ - "Now we can use it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "quarterly-nightmare", - "metadata": {}, - "outputs": [], - "source": [ - "t = sqrt(2 * h / a)\n", - "t" - ] - }, - { - "cell_type": "markdown", - "id": "velvet-oklahoma", - "metadata": {}, - "source": [ - "With no air resistance, it would take about $8.8$ s for the penny to reach the sidewalk." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "quality-external", - "metadata": {}, - "outputs": [], - "source": [ - "v = a * t\n", - "v" - ] - }, - { - "cell_type": "markdown", - "id": "active-lobby", - "metadata": {}, - "source": [ - "And its velocity on impact would be about $86$ m/s." - ] - }, - { - "cell_type": "markdown", - "id": "human-phase", - "metadata": {}, - "source": [ - "### False Precision\n", - "\n", - "Python displays results with about 16 digits, which gives the impression that they are very precise, but don't be fooled.\n", - "The numbers we get out are only as good as the numbers we put in.\n", - "\n", - "For example, the \"roof height\" of the Empire State Building is $380$ m (according to Wikipedia: ).\n", - "I chose $h=381$ m for this example on the assumption that the observation deck is on the roof and you drop the penny from a 1 meter railing.\n", - "But that's probably not right, so we should treat this value as an approximation where only the first two digits are likely to be right.\n", - "\n", - "If the precision of the inputs is two digits, the precision of the outputs is two digits, *at most*.\n", - "That's why, if the output is `86.41527642726142`, I report it as \"about 86\"." - ] - }, - { - "cell_type": "markdown", - "id": "clinical-blackjack", - "metadata": {}, - "source": [ - "### Computation With Units\n", - "\n", - "The computations we just did use numbers without units.\n", - "When we set `h=381`, we left out the meters, and when we set `a=9.8`, we left out the meters per second squared.\n", - "And, when we got the result `v=86`, we added back the meters per second.\n", - "\n", - "Leaving units out of computation is a common practice, but it tends to cause errors, including the very expensive failure of the Mars Climate Orbiter in 1999 (see ).\n", - "When possible, it is better to include units in the computation.\n", - "\n", - "To represent units, we'll use a library called Pint ().\n", - "To use it, we have to import a function called `UnitRegistry` and call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "lovely-declaration", - "metadata": {}, - "outputs": [], - "source": [ - "from pint import UnitRegistry\n", - "\n", - "units = UnitRegistry()" - ] - }, - { - "cell_type": "markdown", - "id": "consecutive-sleeve", - "metadata": {}, - "source": [ - "The result is an object that contains variables representing pretty much every unit you've heard of.\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "cardiac-class", - "metadata": {}, - "outputs": [], - "source": [ - "units.league" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "phantom-copper", - "metadata": {}, - "outputs": [], - "source": [ - "units.fortnight" - ] - }, - { - "cell_type": "markdown", - "id": "unlike-opera", - "metadata": {}, - "source": [ - "But leagues and fortnights are not part of the International System of Units\n", - "(see ); in the jargon, they are not \"SI units\".\n", - "Instead, we will use `meter` and `second`." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "russian-popularity", - "metadata": {}, - "outputs": [], - "source": [ - "meter = units.meter\n", - "meter" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "endless-paint", - "metadata": {}, - "outputs": [], - "source": [ - "second = units.second\n", - "second" - ] - }, - { - "cell_type": "markdown", - "id": "spiritual-scenario", - "metadata": { - "tags": [] - }, - "source": [ - "To find out what other units are defined, type `units.` (including the period) in the next cell.\n", - "\n", - "If you are on Colab, a pop-up menu should appear with a list of units.\n", - "In other Jupyter environments, you might have to press `TAB` to get the menu." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "saving-suggestion", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "everyday-location", - "metadata": {}, - "source": [ - "We can use `meter` and `second` to create a variable named `a` and give it the value of acceleration due to gravity. " - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "earlier-bandwidth", - "metadata": {}, - "outputs": [], - "source": [ - "a = 9.8 * meter / second**2\n", - "a" - ] - }, - { - "cell_type": "markdown", - "id": "generic-bowling", - "metadata": {}, - "source": [ - "The result is a *quantity* with two parts, called `magnitude` and `units`, which we can access like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "missing-privilege", - "metadata": {}, - "outputs": [], - "source": [ - "a.magnitude" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "fourth-swedish", - "metadata": {}, - "outputs": [], - "source": [ - "a.units" - ] - }, - { - "cell_type": "markdown", - "id": "realistic-techno", - "metadata": {}, - "source": [ - "We can also create a quantity that represents $3.4$ s." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "apart-france", - "metadata": {}, - "outputs": [], - "source": [ - "t = 3.4 * second\n", - "t" - ] - }, - { - "cell_type": "markdown", - "id": "severe-share", - "metadata": {}, - "source": [ - "And use it to compute the distance a penny would fall after `t` seconds with constant acceleration `a`. " - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "alien-scout", - "metadata": {}, - "outputs": [], - "source": [ - "a * t**2 / 2" - ] - }, - { - "cell_type": "markdown", - "id": "wicked-indianapolis", - "metadata": {}, - "source": [ - "Notice that the units of the result are correct.\n", - "If we create a quantity to represent the height of the Empire State Building:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "confidential-costs", - "metadata": {}, - "outputs": [], - "source": [ - "h = 381 * meter" - ] - }, - { - "cell_type": "markdown", - "id": "hollow-programmer", - "metadata": {}, - "source": [ - "We can use it to compute the time the penny would take to reach the sidewalk." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "studied-opera", - "metadata": {}, - "outputs": [], - "source": [ - "t = sqrt(2 * h / a)\n", - "t" - ] - }, - { - "cell_type": "markdown", - "id": "seasonal-laser", - "metadata": {}, - "source": [ - "And the velocity of the penny on impact:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "exterior-greek", - "metadata": {}, - "outputs": [], - "source": [ - "v = a * t\n", - "v" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "impaired-puzzle", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "assert abs(v.magnitude - 86.41527642726142) < 1e-7" - ] - }, - { - "cell_type": "markdown", - "id": "superior-biography", - "metadata": {}, - "source": [ - "As in the previous section, the result is about $86$, but now it has the correct units, m/s.\n", - "\n", - "With Pint quantities, we can convert from one set of units to another like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "antique-landing", - "metadata": {}, - "outputs": [], - "source": [ - "mile = units.mile\n", - "hour = units.hour" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "included-failure", - "metadata": {}, - "outputs": [], - "source": [ - "v.to(mile/hour)" - ] - }, - { - "cell_type": "markdown", - "id": "progressive-charleston", - "metadata": {}, - "source": [ - "If you are more familiar with miles per hour, this result might be easier to interpret.\n", - "And it might give you a sense that this model is not realistic." - ] - }, - { - "cell_type": "markdown", - "id": "continuing-democrat", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "This chapter introduces a modeling framework that consists of three steps:\n", - "\n", - "* Abstraction is the process of defining a model by deciding which elements of the real world to include and which can be left out.\n", - "\n", - "* Analysis and simulation are ways to use a model to generate predictions, explain why things behave as they do, and design things that behave as we want.\n", - "\n", - "* Validation is how we test whether the model is right, often by comparing predictions with measurements from the real world.\n", - "\n", - "As a first example, we modeled a penny dropped from the Empire State building, including gravity but ignoring air resistance.\n", - "In the exercises, you'll have a chance to try a better model, including air resistance.\n", - "\n", - "This chapter also presents Pint, a library for doing computation with units, which is convenient for converting between different units and helpful for avoiding catastrophic errors." - ] - }, - { - "cell_type": "markdown", - "id": "thousand-equation", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "Before you go on, you might want to work on the following exercises." - ] - }, - { - "cell_type": "markdown", - "id": "periodic-objective", - "metadata": {}, - "source": [ - "### Exercise 1\n", - "\n", - "In mathematical notation, we can write an equation like $v = a t$ and it's understood that we are multiplying $a$ and $t$.\n", - "But that doesn't work in Python. If you put two variables side-by-side, like this:\n", - "\n", - "```\n", - "v = a t\n", - "```\n", - "\n", - "you'll get a *syntax error*, which means that something is wrong with the structure of the program. Try it out so you see what the error message looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "id": "exempt-delight", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "a = 9.8 * meter / second**2\n", - "t = 3.4 * second\n", - "\n", - "v = a * t\n", - "v" - ] - }, - { - "cell_type": "markdown", - "id": "pressing-sugar", - "metadata": {}, - "source": [ - "### Exercise 2\n", - "\n", - "In this chapter we used the `sqrt` function from the NumPy library. NumPy also provides a variable named `pi` that contains an approximation of the mathematical constant $\\pi$.\n", - "We can import it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "legal-observer", - "metadata": {}, - "outputs": [], - "source": [ - "from numpy import pi\n", - "pi" - ] - }, - { - "cell_type": "markdown", - "id": "aggregate-mambo", - "metadata": {}, - "source": [ - "NumPy provides other functions we'll use, including `log`, `exp`, `sin`, and `cos`.\n", - "Import `sin` and `cos` from NumPy and compute\n", - "\n", - "$$sin^2 (\\pi/4) + cos^2 (\\pi/4)$$\n", - "\n", - "Note: A mathematical identity tells us that the answer should be $1$." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "pressing-belgium", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "unlimited-delivery", - "metadata": {}, - "source": [ - "### Exercise 3\n", - "\n", - "Suppose you bring a 10 foot pole to the top of the Empire State Building and use it to drop the penny from `h` plus 10 feet.\n", - "\n", - "Define a variable named `foot` that contains the unit `foot` provided by the `UnitRegistry` we called `units`. Define a variable named `pole_height` and give it the value 10 feet.\n", - "\n", - "What happens if you add `h`, which is in units of meters, to `pole_height`, which is in units of feet? What happens if you write the addition the other way around?" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "inner-equivalent", - "metadata": {}, - "outputs": [], - "source": [ - "h = 381 * meter" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "nuclear-thirty", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "available-steering", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "aggressive-climate", - "metadata": {}, - "source": [ - "### Exercise 4\n", - "\n", - "Why would it be nonsensical to add `a` and `t`? What happens if you try?" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "documentary-doctrine", - "metadata": {}, - "outputs": [], - "source": [ - "a = 9.8 * meter / second**2\n", - "t = 3.4 * second" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "primary-partner", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "legal-audio", - "metadata": {}, - "source": [ - "In this example, you should get a `DimensionalityError`, which indicates that you have violated a rule of dimensional analysis: you cannot add quantities with different dimensions.\n", - "\n", - "The error messages you get from Python are big and scary, but if you read them carefully, they contain a lot of useful information.\n", - "\n", - "The last line usually tells you what type of error happened, and sometimes additional information, so you might want to start from the bottom and read up.\n", - "\n", - "The previous lines are a *traceback* of what was happening when the error occurred. The first section of the traceback shows the code you wrote. The following sections are often from Python libraries." - ] - }, - { - "cell_type": "markdown", - "id": "steady-chancellor", - "metadata": { - "tags": [] - }, - "source": [ - "Before you go on, you might want to delete the erroneous code so the notebook can run without errors." - ] - }, - { - "cell_type": "markdown", - "id": "valid-empire", - "metadata": {}, - "source": [ - "### Exercise 5\n", - "\n", - "Suppose instead of dropping the penny, you throw it downward at its terminal velocity, $29$ m/s. How long would it take to fall $381$ m?" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "greenhouse-reason", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "asian-murray", - "metadata": {}, - "source": [ - "### Exercise 6:\n", - "\n", - "So far we have considered two models of a falling penny:\n", - "\n", - "* If we ignore air resistance, the penny falls with constant acceleration, and we can compute the time to reach the sidewalk and the velocity of the penny when it gets there.\n", - "\n", - "* If we take air resistance into account, and drop the penny at its terminal velocity, it falls with constant velocity. \n", - "\n", - "Now let's consider a third model that includes elements of the first two: let's assume that the acceleration of the penny is `a` until the penny reaches $29$ m/s, and then $0$ m/s$^2$ afterwards. What is the total time for the penny to fall $381$ m?" - ] - }, - { - "cell_type": "markdown", - "id": "detailed-minnesota", - "metadata": {}, - "source": [ - "You can break this question into three parts:\n", - "\n", - "1. How long would the penny take to reach $29$ m/s with constant acceleration `a`.\n", - "2. How far would it fall during that time?\n", - "3. How long would it take to fall the remaining distance with constant velocity $29$ m/s?\n", - "\n", - "Suggestion: Assign each intermediate result to a variable with a meaningful name. And assign units to all quantities!" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "secure-crowd", - "metadata": {}, - "outputs": [], - "source": [ - "a = 9.8 * meter / second**2\n", - "h = 381 * meter" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "id": "thirty-minneapolis", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "premier-seeking", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "brave-laundry", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "adjusted-consultation", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "id": "understanding-consortium", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "composed-tunnel", - "metadata": {}, - "source": [ - "### Exercise 7\n", - "\n", - "When I was in high school, the pitcher on the baseball team claimed that when he threw a fastball he was throwing the ball down; that is, the ball left his hand at a downward angle.\n", - "I was skeptical; watching from the side, I thought the ball left his hand at an upward angle.\n", - "\n", - "Can you think of a simple model you could use to settle the argument? What factors would you include and what could you ignore? What quantities would you have to look up or estimate?\n", - "\n", - "I suggest you convert all quantities to SI units like meters and seconds (see )." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "about-complex", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "id": "unique-owner", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "minus-batman", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "id": "exact-vegetable", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "id": "neutral-lightning", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "jewish-secret", - "metadata": {}, - "source": [ - "### Exercise 8\n", - "\n", - "Suppose I run a 10K race in 44:52 (44 minutes and 52 seconds). What is my average pace in minutes per mile?" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "virgin-cambodia", - "metadata": {}, - "outputs": [], - "source": [ - "mile = units.mile\n", - "kilometer = units.kilometer\n", - "minute = units.minute" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "id": "right-intention", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "id": "mineral-sally", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "id": "coral-camel", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "id": "preceding-cricket", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "id": "effective-rendering", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "id": "printable-reply", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "environmental-wallet", - "metadata": { - "tags": [] - }, - "source": [ - "## More Jupyter\n", - "\n", - "You can add and remove cells from a notebook using the buttons in the toolbar and the items in the menu, both of which you should see at the top of this notebook.\n", - "\n", - "Try the following exercises:\n", - "\n", - "1. From the Insert menu select \"Insert cell below\" to add a cell below this one. By default, you get a code cell, as you can see in the pulldown menu that says \"Code\".\n", - "\n", - "2. In the new cell, add a print statement like `print('Hello')`, and run it.\n", - "\n", - "3. Add another cell, select the new cell, and then click on the pulldown menu that says \"Code\" and select \"Markdown\". This makes the new cell a text cell.\n", - "\n", - "4. In the new cell, type some text, and then run it.\n", - "\n", - "5. Use the arrow buttons in the toolbar to move cells up and down.\n", - "\n", - "6. Use the cut, copy, and paste buttons to delete, add, and move cells.\n", - "\n", - "7. As you make changes, Jupyter saves your notebook automatically, but if you want to make sure, you can press the save button, which looks like a floppy disk from the 1990s.\n", - "\n", - "8. Finally, when you are done with a notebook, select \"Close and Halt\" from the File menu." - ] - }, - { - "cell_type": "markdown", - "id": "successful-kentucky", - "metadata": { - "tags": [] - }, - "source": [ - "When you change the contents of a cell, you have to run it again for those changes to have an effect. If you forget to do that, the results can be confusing, because the code you are looking at is not the code you ran.\n", - "\n", - "If you ever lose track of which cells have run, and in what order, you should go to the Kernel menu and select \"Restart & Run All\". Restarting the kernel means that all of your variables get deleted, and running all the cells means all of your code will run again, in the right order.\n", - "\n", - "Select \"Restart & Run All\" now and confirm that it runs all of the cells." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "weighted-quebec", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.10.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": { + "id": "a8b892c8" + }, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "affecting-malta", + "metadata": { + "id": "affecting-malta" + }, + "source": [ + "# Modeling" + ] + }, + { + "cell_type": "markdown", + "id": "pressed-palestinian", + "metadata": { + "tags": [], + "id": "pressed-palestinian" + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "markdown", + "id": "confirmed-budapest", + "metadata": { + "tags": [], + "id": "confirmed-budapest" + }, + "source": [ + "## Jupyter\n", + "\n", + "Welcome to *Modeling and Simulation in Python*, and welcome to Jupyter.\n", + "\n", + "This is a Jupyter notebook, which is a development environment where you can write and run Python code. Each notebook is divided into cells. Each cell contains either text (like this cell) or Python code." + ] + }, + { + "cell_type": "markdown", + "id": "danish-scope", + "metadata": { + "tags": [], + "id": "danish-scope" + }, + "source": [ + "To run a cell, hold down SHIFT and press ENTER. \n", + "\n", + "* If you run a text cell, Jupyter formats the text and displays the result.\n", + "\n", + "* If you run a code cell, Jupyter runs the Python code in the cell and displays the result, if any.\n", + "\n", + "The following cells check whether the libraries we need are installed. If so, the cells produce no output. If not, you'll see updates from the installer." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "passive-dayton", + "metadata": { + "tags": [], + "id": "passive-dayton" + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " from pint import UnitRegistry\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "prompt-committee", + "metadata": { + "tags": [], + "id": "prompt-committee" + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + "\n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "fifteen-train", + "metadata": { + "tags": [], + "id": "fifteen-train" + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "chronic-moore", + "metadata": { + "id": "chronic-moore" + }, + "source": [ + "This chapter introduces the modeling framework we will use throughout the book, and works through our first example, using a simple model of physics to evaluate the claim that a penny falling from the height of the Empire State Building could kill someone if it hit them on the head. Also, you'll see how to do computation in Python with units like meters and seconds." + ] + }, + { + "cell_type": "markdown", + "id": "extensive-proposition", + "metadata": { + "id": "extensive-proposition" + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises.\n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "hourly-financing", + "metadata": { + "id": "hourly-financing" + }, + "source": [ + "## The Modeling Framework\n", + "\n", + "This book is about modeling and simulating physical systems. The\n", + "following diagram shows what I mean by *modeling*:\n", + "\n", + "![Diagram of the modeling framework.](https://github.com/AllenDowney/ModSim/raw/main/figs/modeling_framework.png)" + ] + }, + { + "cell_type": "markdown", + "id": "structured-receiver", + "metadata": { + "id": "structured-receiver" + }, + "source": [ + "Starting in the lower left, the *system* is something in the real\n", + "world we are interested in.\n", + "To model the system, we have to decide which elements of the real world to include and which we can leave out.\n", + "This process is called *abstraction*.\n", + "\n", + "The result of abstraction is a *model*, which is a description of the system that includes only the features we think are essential. A model\n", + "can be represented in the form of diagrams and equations, which can be\n", + "used for mathematical *analysis*. It can also be implemented in the\n", + "form of a computer program, which can run *simulations*.\n", + "\n", + "The result of analysis and simulation might be a *prediction* about\n", + "what the system will do, an *explanation* of why it behaves the way it\n", + "does, or a *design* intended to achieve a purpose.\n", + "\n", + "We can *validate* predictions and test designs by taking\n", + "*measurements* from the real world and comparing the *data* we get\n", + "with the results from analysis and simulation." + ] + }, + { + "cell_type": "markdown", + "id": "center-accommodation", + "metadata": { + "id": "center-accommodation" + }, + "source": [ + "For any physical system, there are many possible models, each one\n", + "including and excluding different features, or including different\n", + "levels of detail. The goal of the modeling process is to find the model\n", + "best suited to its purpose (prediction, explanation, or design).\n", + "\n", + "Sometimes the best model is the most detailed. If we include more\n", + "features, the model is more realistic, and we expect its predictions to\n", + "be more accurate.\n", + "But often a simpler model is better. If we include only the essential\n", + "features and leave out the rest, we get models that are easier to work\n", + "with, and the explanations they provide can be clearer and more\n", + "compelling." + ] + }, + { + "cell_type": "markdown", + "id": "confirmed-highlight", + "metadata": { + "id": "confirmed-highlight" + }, + "source": [ + "As an example, suppose someone asks you why the orbit of the Earth is\n", + "elliptical. If you model the Earth and Sun as point masses (ignoring\n", + "their actual size), compute the gravitational force between them using\n", + "Newton's law of universal gravitation, and compute the resulting orbit\n", + "using Newton's laws of motion, you can show that the result is an\n", + "ellipse.\n", + "Of course, the actual orbit of Earth is not a perfect ellipse, because\n", + "of the gravitational forces of the Moon, Jupiter, and other objects in\n", + "the solar system, and because Newton's laws of motion are only\n", + "approximately true (they don't take into account relativistic effects).\n", + "But adding these features to the model would not improve the\n", + "explanation; more detail would only be a distraction from the\n", + "fundamental cause. However, if the goal is to predict the position of\n", + "the Earth with great precision, including more details might be\n", + "necessary." + ] + }, + { + "cell_type": "markdown", + "id": "stretch-geneva", + "metadata": { + "id": "stretch-geneva" + }, + "source": [ + "Choosing the best model depends on what the model is for. It is usually\n", + "a good idea to start with a simple model, even if it is likely to be too\n", + "simple, and test whether it is good enough for its purpose. Then you can\n", + "add features gradually, starting with the ones you expect to be most\n", + "essential. This process is called *iterative modeling*.\n", + "\n", + "Comparing results of successive models provides a form of *internal\n", + "validation*, so you can catch conceptual, mathematical, and software\n", + "errors. And by adding and removing features, you can tell which ones\n", + "have the biggest effect on the results, and which can be ignored.\n", + "\n", + "Comparing results to data from the real world provides *external\n", + "validation*, which is generally the strongest test.\n", + "\n", + "The modeling framework is pretty abstract; the following example might make it clearer." + ] + }, + { + "cell_type": "markdown", + "id": "criminal-lunch", + "metadata": { + "id": "criminal-lunch" + }, + "source": [ + "## The Falling Penny Myth\n", + "\n", + "You might have heard that a\n", + "penny dropped from the top of the Empire State Building would be going\n", + "so fast when it hit the pavement that it would be embedded in the\n", + "concrete; or if it hit a person, it would break their skull.\n", + "\n", + "We can test this myth by making and analyzing two models. For the first model,\n", + "we'll assume that the effect of air resistance is small. In that case, the primary force acting on the penny is gravity, which causes the penny to accelerate downward.\n", + "\n", + "If the initial velocity is 0 and the acceleration, $a$, is constant, the velocity after $t$ seconds is\n", + "\n", + "$$v = a t$$\n", + "\n", + "and the distance the penny has dropped is\n", + "\n", + "$$x = a t^2 / 2$$\n", + "\n", + "To find the time until the penny reaches the sidewalk, we can solve for $t$:\n", + "\n", + "$$t = \\sqrt{ 2 x / a}$$\n", + "\n", + "Plugging in the acceleration of gravity, $a = 9.8$ m/s$^2$, and the height of the Empire State Building, $x = 381$ m, we get $t = 8.8$ s.\n", + "\n", + "Then computing $v = a t$ we get a velocity on impact of $86$ m/s, which is about 190 miles per hour. That sounds like it could hurt." + ] + }, + { + "cell_type": "markdown", + "id": "documentary-diagnosis", + "metadata": { + "id": "documentary-diagnosis" + }, + "source": [ + "Of course, these results are not exact because the model is based on simplifications. For example, we assume that gravity is constant.\n", + "In fact, the force of gravity is different on different parts of the globe, and it gets weaker as you move away from the surface.\n", + "But these differences are small, so ignoring them is probably a good choice for this problem.\n", + "\n", + "On the other hand, ignoring air resistance is not a good choice, because in this scenario its effect is substantial.\n", + "Once the penny gets to about 29 m/s, the upward force of air resistance equals the downward force of gravity, so the penny stops accelerating.\n", + "This is the *terminal velocity* of the penny in air.\n", + "\n", + "And that suggests a second model, where the penny accelerates until it reaches terminal velocity; after that, acceleration is 0 and velocity is constant.\n", + "In this model, the penny hits the sidewalk at about 29 m/s.\n", + "That's much less than 86 m/s, which is what the first model predicts.\n", + "Getting hit with a penny at that speed might hurt, but it would be unlikely to cause real harm. And it would not damage concrete." + ] + }, + { + "cell_type": "markdown", + "id": "recent-appliance", + "metadata": { + "id": "recent-appliance" + }, + "source": [ + "The statistician George Box famously said \"All models are wrong, but\n", + "some are useful.\" He was talking about statistical models, but his wise words apply to all kinds of models. Our first model, which ignores air resistance, is very wrong, and probably not useful. The second model, which takes air resistance into account, is still wrong, but it's better, and it's good enough to refute the myth.\n", + "\n", + "The television show *Mythbusters* has tested the myth of the falling\n", + "penny more carefully; you can view the results at\n", + ". Their work is based on a mathematical model of motion, measurements to determine the force of air resistance on a penny, and a physical model of a human head." + ] + }, + { + "cell_type": "markdown", + "id": "brief-zoning", + "metadata": { + "id": "brief-zoning" + }, + "source": [ + "## Computation In Python\n", + "\n", + "Let me show you how I computed the results from the\n", + "previous section using Python.\n", + "First we'll create a variable to represent acceleration due to gravity in meters per second squared (m/s$^2$)." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "eleven-marine", + "metadata": { + "id": "eleven-marine" + }, + "outputs": [], + "source": [ + "a = 9.8" + ] + }, + { + "cell_type": "markdown", + "id": "upset-myanmar", + "metadata": { + "id": "upset-myanmar" + }, + "source": [ + "A *variable* is a name that corresponds to a value. In this example, the name is `a` and the value is the number `9.8`.\n", + "\n", + "Suppose we let the penny drop for $3.4$ seconds (s). I'll create a variable to represent this time:" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "following-launch", + "metadata": { + "id": "following-launch" + }, + "outputs": [], + "source": [ + "t = 3.4" + ] + }, + { + "cell_type": "markdown", + "id": "greek-heritage", + "metadata": { + "id": "greek-heritage" + }, + "source": [ + "Now we can compute the velocity of the penny after `t` seconds." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "mature-duration", + "metadata": { + "id": "mature-duration" + }, + "outputs": [], + "source": [ + "v = a * t" + ] + }, + { + "cell_type": "markdown", + "id": "serial-pilot", + "metadata": { + "id": "serial-pilot" + }, + "source": [ + "Python uses the symbol `*` for multiplication. The other arithmetic operators are `+` for addition, `-` for subtraction, `/` for division, and `**` for exponentiation." + ] + }, + { + "cell_type": "markdown", + "id": "qualified-diabetes", + "metadata": { + "id": "qualified-diabetes" + }, + "source": [ + "After you assign a value to a variable, you can display the value like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "considered-inclusion", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "considered-inclusion", + "outputId": "a8b8064e-9d0b-4771-df12-17800069f744" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "33.32" + ] + }, + "metadata": {}, + "execution_count": 84 + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "markdown", + "id": "northern-saturday", + "metadata": { + "id": "northern-saturday" + }, + "source": [ + "After $3.4$ s, the velocity of the penny is about $33$ m/s (ignoring air resistance). Now let's see how far it would travel during that time:" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "valued-electricity", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "valued-electricity", + "outputId": "d962f0fe-a333-4037-c527-0de9139830e4" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "56.644" + ] + }, + "metadata": {}, + "execution_count": 85 + } + ], + "source": [ + "x = a * t**2 / 2\n", + "x" + ] + }, + { + "cell_type": "markdown", + "id": "yellow-business", + "metadata": { + "id": "yellow-business" + }, + "source": [ + "It would travel about $56$ m. Now, going in the other direction, let's compute the time it takes to fall 381 m, the height of the Empire State Building." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "closed-month", + "metadata": { + "id": "closed-month" + }, + "outputs": [], + "source": [ + "h = 381" + ] + }, + { + "cell_type": "markdown", + "id": "fuzzy-lease", + "metadata": { + "id": "fuzzy-lease" + }, + "source": [ + "For this computation, we need the square root function, `sqrt`, which is provided by a library called NumPy.\n", + "Before we can use it, we have to import it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "nuclear-clone", + "metadata": { + "id": "nuclear-clone" + }, + "outputs": [], + "source": [ + "from numpy import sqrt" + ] + }, + { + "cell_type": "markdown", + "id": "unlimited-swiss", + "metadata": { + "id": "unlimited-swiss" + }, + "source": [ + "Now we can use it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "quarterly-nightmare", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "quarterly-nightmare", + "outputId": "8acc6018-540b-4939-c457-ee759a3a81e3" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "8.817885349720552" + ] + }, + "metadata": {}, + "execution_count": 88 + } + ], + "source": [ + "t = sqrt(2 * h / a)\n", + "t" + ] + }, + { + "cell_type": "markdown", + "id": "velvet-oklahoma", + "metadata": { + "id": "velvet-oklahoma" + }, + "source": [ + "With no air resistance, it would take about $8.8$ s for the penny to reach the sidewalk." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "quality-external", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "quality-external", + "outputId": "126f33f7-6172-484f-de39-cc6f2fbafba5" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "86.41527642726142" + ] + }, + "metadata": {}, + "execution_count": 89 + } + ], + "source": [ + "v = a * t\n", + "v" + ] + }, + { + "cell_type": "markdown", + "id": "active-lobby", + "metadata": { + "id": "active-lobby" + }, + "source": [ + "And its velocity on impact would be about $86$ m/s." + ] + }, + { + "cell_type": "markdown", + "id": "human-phase", + "metadata": { + "id": "human-phase" + }, + "source": [ + "### False Precision\n", + "\n", + "Python displays results with about 16 digits, which gives the impression that they are very precise, but don't be fooled.\n", + "The numbers we get out are only as good as the numbers we put in.\n", + "\n", + "For example, the \"roof height\" of the Empire State Building is $380$ m (according to Wikipedia: ).\n", + "I chose $h=381$ m for this example on the assumption that the observation deck is on the roof and you drop the penny from a 1 meter railing.\n", + "But that's probably not right, so we should treat this value as an approximation where only the first two digits are likely to be right.\n", + "\n", + "If the precision of the inputs is two digits, the precision of the outputs is two digits, *at most*.\n", + "That's why, if the output is `86.41527642726142`, I report it as \"about 86\"." + ] + }, + { + "cell_type": "markdown", + "id": "clinical-blackjack", + "metadata": { + "id": "clinical-blackjack" + }, + "source": [ + "### Computation With Units\n", + "\n", + "The computations we just did use numbers without units.\n", + "When we set `h=381`, we left out the meters, and when we set `a=9.8`, we left out the meters per second squared.\n", + "And, when we got the result `v=86`, we added back the meters per second.\n", + "\n", + "Leaving units out of computation is a common practice, but it tends to cause errors, including the very expensive failure of the Mars Climate Orbiter in 1999 (see ).\n", + "When possible, it is better to include units in the computation.\n", + "\n", + "To represent units, we'll use a library called Pint ().\n", + "To use it, we have to import a function called `UnitRegistry` and call it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "id": "lovely-declaration", + "metadata": { + "id": "lovely-declaration" + }, + "outputs": [], + "source": [ + "from pint import UnitRegistry\n", + "\n", + "units = UnitRegistry()" + ] + }, + { + "cell_type": "markdown", + "id": "consecutive-sleeve", + "metadata": { + "id": "consecutive-sleeve" + }, + "source": [ + "The result is an object that contains variables representing pretty much every unit you've heard of.\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "id": "cardiac-class", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "cardiac-class", + "outputId": "ee432e3b-8da4-4763-9361-e2b4bcbc6e3c" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "league" + ], + "text/latex": "$\\mathrm{league}$" + }, + "metadata": {}, + "execution_count": 91 + } + ], + "source": [ + "units.league" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "phantom-copper", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "phantom-copper", + "outputId": "4939478e-ca7b-46d8-d1d9-199b16f85277" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "fortnight" + ], + "text/latex": "$\\mathrm{fortnight}$" + }, + "metadata": {}, + "execution_count": 92 + } + ], + "source": [ + "units.fortnight" + ] + }, + { + "cell_type": "markdown", + "id": "unlike-opera", + "metadata": { + "id": "unlike-opera" + }, + "source": [ + "But leagues and fortnights are not part of the International System of Units\n", + "(see ); in the jargon, they are not \"SI units\".\n", + "Instead, we will use `meter` and `second`." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "russian-popularity", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "russian-popularity", + "outputId": "028f58cd-4372-4656-ce9a-c306196d51f0" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "meter" + ], + "text/latex": "$\\mathrm{meter}$" + }, + "metadata": {}, + "execution_count": 93 + } + ], + "source": [ + "meter = units.meter\n", + "meter" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "id": "endless-paint", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "endless-paint", + "outputId": "30615da4-153c-49f1-b921-55305a53b07c" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "second" + ], + "text/latex": "$\\mathrm{second}$" + }, + "metadata": {}, + "execution_count": 94 + } + ], + "source": [ + "second = units.second\n", + "second" + ] + }, + { + "cell_type": "markdown", + "id": "spiritual-scenario", + "metadata": { + "tags": [], + "id": "spiritual-scenario" + }, + "source": [ + "To find out what other units are defined, type `units.` (including the period) in the next cell.\n", + "\n", + "If you are on Colab, a pop-up menu should appear with a list of units.\n", + "In other Jupyter environments, you might have to press `TAB` to get the menu." + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "id": "saving-suggestion", + "metadata": { + "tags": [], + "id": "saving-suggestion" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "everyday-location", + "metadata": { + "id": "everyday-location" + }, + "source": [ + "We can use `meter` and `second` to create a variable named `a` and give it the value of acceleration due to gravity." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "id": "earlier-bandwidth", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 38 + }, + "id": "earlier-bandwidth", + "outputId": "27be69e7-eadb-4589-b2e3-69eda08c48e1" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "9.8 meter/second2" + ], + "text/latex": "$9.8\\ \\frac{\\mathrm{meter}}{\\mathrm{second}^{2}}$" + }, + "metadata": {}, + "execution_count": 95 + } + ], + "source": [ + "a = 9.8 * meter / second**2\n", + "a" + ] + }, + { + "cell_type": "markdown", + "id": "generic-bowling", + "metadata": { + "id": "generic-bowling" + }, + "source": [ + "The result is a *quantity* with two parts, called `magnitude` and `units`, which we can access like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "id": "missing-privilege", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "missing-privilege", + "outputId": "67b598d1-0376-41b8-8222-d171f7509f64" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "9.8" + ] + }, + "metadata": {}, + "execution_count": 96 + } + ], + "source": [ + "a.magnitude" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "id": "fourth-swedish", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 38 + }, + "id": "fourth-swedish", + "outputId": "d4c07073-cc52-48d6-a83f-2440b5282cda" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "meter/second2" + ], + "text/latex": "$\\frac{\\mathrm{meter}}{\\mathrm{second}^{2}}$" + }, + "metadata": {}, + "execution_count": 97 + } + ], + "source": [ + "a.units" + ] + }, + { + "cell_type": "markdown", + "id": "realistic-techno", + "metadata": { + "id": "realistic-techno" + }, + "source": [ + "We can also create a quantity that represents $3.4$ s." + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "id": "apart-france", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "apart-france", + "outputId": "8338572e-45d7-4cf8-f0ee-5b5a299f32ea" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "3.4 second" + ], + "text/latex": "$3.4\\ \\mathrm{second}$" + }, + "metadata": {}, + "execution_count": 98 + } + ], + "source": [ + "t = 3.4 * second\n", + "t" + ] + }, + { + "cell_type": "markdown", + "id": "severe-share", + "metadata": { + "id": "severe-share" + }, + "source": [ + "And use it to compute the distance a penny would fall after `t` seconds with constant acceleration `a`. " + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "id": "alien-scout", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "alien-scout", + "outputId": "8af86047-32c3-4738-8829-274abc1748b3" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "56.644 meter" + ], + "text/latex": "$56.644\\ \\mathrm{meter}$" + }, + "metadata": {}, + "execution_count": 99 + } + ], + "source": [ + "a * t**2 / 2" + ] + }, + { + "cell_type": "markdown", + "id": "wicked-indianapolis", + "metadata": { + "id": "wicked-indianapolis" + }, + "source": [ + "Notice that the units of the result are correct.\n", + "If we create a quantity to represent the height of the Empire State Building:" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "id": "confidential-costs", + "metadata": { + "id": "confidential-costs" + }, + "outputs": [], + "source": [ + "h = 381 * meter" + ] + }, + { + "cell_type": "markdown", + "id": "hollow-programmer", + "metadata": { + "id": "hollow-programmer" + }, + "source": [ + "We can use it to compute the time the penny would take to reach the sidewalk." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "id": "studied-opera", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "studied-opera", + "outputId": "1f2a187a-d1d9-47b7-ea4c-bf3670cf1b95" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "8.817885349720552 second" + ], + "text/latex": "$8.817885349720552\\ \\mathrm{second}$" + }, + "metadata": {}, + "execution_count": 101 + } + ], + "source": [ + "t = sqrt(2 * h / a)\n", + "t" + ] + }, + { + "cell_type": "markdown", + "id": "seasonal-laser", + "metadata": { + "id": "seasonal-laser" + }, + "source": [ + "And the velocity of the penny on impact:" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "id": "exterior-greek", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "exterior-greek", + "outputId": "543a781d-5eec-4596-f17f-a665e86885a0" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "86.41527642726142 meter/second" + ], + "text/latex": "$86.41527642726142\\ \\frac{\\mathrm{meter}}{\\mathrm{second}}$" + }, + "metadata": {}, + "execution_count": 102 + } + ], + "source": [ + "v = a * t\n", + "v" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "id": "impaired-puzzle", + "metadata": { + "tags": [], + "id": "impaired-puzzle" + }, + "outputs": [], + "source": [ + "assert abs(v.magnitude - 86.41527642726142) < 1e-7" + ] + }, + { + "cell_type": "markdown", + "id": "superior-biography", + "metadata": { + "id": "superior-biography" + }, + "source": [ + "As in the previous section, the result is about $86$, but now it has the correct units, m/s.\n", + "\n", + "With Pint quantities, we can convert from one set of units to another like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "antique-landing", + "metadata": { + "id": "antique-landing" + }, + "outputs": [], + "source": [ + "mile = units.mile\n", + "hour = units.hour" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "included-failure", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "included-failure", + "outputId": "20c88680-3450-414f-edd7-f2afa04da817" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "193.30546802805432 mile/hour" + ], + "text/latex": "$193.30546802805432\\ \\frac{\\mathrm{mile}}{\\mathrm{hour}}$" + }, + "metadata": {}, + "execution_count": 105 + } + ], + "source": [ + "v.to(mile/hour)" + ] + }, + { + "cell_type": "markdown", + "id": "progressive-charleston", + "metadata": { + "id": "progressive-charleston" + }, + "source": [ + "If you are more familiar with miles per hour, this result might be easier to interpret.\n", + "And it might give you a sense that this model is not realistic." + ] + }, + { + "cell_type": "markdown", + "id": "continuing-democrat", + "metadata": { + "id": "continuing-democrat" + }, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces a modeling framework that consists of three steps:\n", + "\n", + "* Abstraction is the process of defining a model by deciding which elements of the real world to include and which can be left out.\n", + "\n", + "* Analysis and simulation are ways to use a model to generate predictions, explain why things behave as they do, and design things that behave as we want.\n", + "\n", + "* Validation is how we test whether the model is right, often by comparing predictions with measurements from the real world.\n", + "\n", + "As a first example, we modeled a penny dropped from the Empire State building, including gravity but ignoring air resistance.\n", + "In the exercises, you'll have a chance to try a better model, including air resistance.\n", + "\n", + "This chapter also presents Pint, a library for doing computation with units, which is convenient for converting between different units and helpful for avoiding catastrophic errors." + ] + }, + { + "cell_type": "markdown", + "id": "thousand-equation", + "metadata": { + "id": "thousand-equation" + }, + "source": [ + "## Exercises\n", + "\n", + "Before you go on, you might want to work on the following exercises." + ] + }, + { + "cell_type": "markdown", + "id": "periodic-objective", + "metadata": { + "id": "periodic-objective" + }, + "source": [ + "### Exercise 1\n", + "\n", + "In mathematical notation, we can write an equation like $v = a t$ and it's understood that we are multiplying $a$ and $t$.\n", + "But that doesn't work in Python. If you put two variables side-by-side, like this:\n", + "\n", + "```\n", + "v = a t\n", + "```\n", + "\n", + "you'll get a *syntax error*, which means that something is wrong with the structure of the program. Try it out so you see what the error message looks like." + ] + }, + { + "cell_type": "code", + "source": [ + "#I had a sybtax error that read \"Invalid sytax\"\n", + "v = a*t" + ], + "metadata": { + "id": "Z1pzSwNhqF6C" + }, + "id": "Z1pzSwNhqF6C", + "execution_count": 106, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": 107, + "id": "exempt-delight", + "metadata": { + "tags": [], + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "exempt-delight", + "outputId": "1eec5c81-84f9-499c-9c17-ee2788e45e1b" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "33.32 meter/second" + ], + "text/latex": "$33.32\\ \\frac{\\mathrm{meter}}{\\mathrm{second}}$" + }, + "metadata": {}, + "execution_count": 107 + } + ], + "source": [ + "a = 9.8 * meter / second**2\n", + "t = 3.4 * second\n", + "\n", + "v = a * t\n", + "v" + ] + }, + { + "cell_type": "markdown", + "id": "pressing-sugar", + "metadata": { + "id": "pressing-sugar" + }, + "source": [ + "### Exercise 2\n", + "\n", + "In this chapter we used the `sqrt` function from the NumPy library. NumPy also provides a variable named `pi` that contains an approximation of the mathematical constant $\\pi$.\n", + "We can import it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "id": "legal-observer", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "legal-observer", + "outputId": "10f0ada1-7be3-46a5-adc0-3a60590d1a1f" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "3.141592653589793" + ] + }, + "metadata": {}, + "execution_count": 108 + } + ], + "source": [ + "from numpy import pi\n", + "pi" + ] + }, + { + "cell_type": "markdown", + "id": "aggregate-mambo", + "metadata": { + "id": "aggregate-mambo" + }, + "source": [ + "NumPy provides other functions we'll use, including `log`, `exp`, `sin`, and `cos`.\n", + "Import `sin` and `cos` from NumPy and compute\n", + "\n", + "$$sin^2 (\\pi/4) + cos^2 (\\pi/4)$$\n", + "\n", + "Note: A mathematical identity tells us that the answer should be $1$." + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "id": "pressing-belgium", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pressing-belgium", + "outputId": "16868a77-7b20-4b3f-8f0e-4738eec2e80e" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "1.0" + ] + }, + "metadata": {}, + "execution_count": 109 + } + ], + "source": [ + "from numpy import sin, cos\n", + "sin(pi/4)**2 + cos(pi/4)**2" + ] + }, + { + "cell_type": "markdown", + "id": "unlimited-delivery", + "metadata": { + "id": "unlimited-delivery" + }, + "source": [ + "### Exercise 3\n", + "\n", + "Suppose you bring a 10 foot pole to the top of the Empire State Building and use it to drop the penny from `h` plus 10 feet.\n", + "\n", + "Define a variable named `foot` that contains the unit `foot` provided by the `UnitRegistry` we called `units`. Define a variable named `pole_height` and give it the value 10 feet.\n", + "\n", + "What happens if you add `h`, which is in units of meters, to `pole_height`, which is in units of feet? What happens if you write the addition the other way around?" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "id": "inner-equivalent", + "metadata": { + "id": "inner-equivalent" + }, + "outputs": [], + "source": [ + "h = 381 * meter" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "id": "nuclear-thirty", + "metadata": { + "id": "nuclear-thirty" + }, + "outputs": [], + "source": [ + "foot = units.foot\n", + "pole_height = 10 * foot" + ] + }, + { + "source": [ + "h + pole_height" + ], + "cell_type": "code", + "metadata": { + "id": "uY0enWCttOGU", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "outputId": "773a6a1f-fc9f-44a4-9bfd-bf46743921ca" + }, + "execution_count": 112, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "384.048 meter" + ], + "text/latex": "$384.048\\ \\mathrm{meter}$" + }, + "metadata": {}, + "execution_count": 112 + } + ], + "id": "uY0enWCttOGU" + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "DzqYRsectZ6z" + }, + "id": "DzqYRsectZ6z", + "execution_count": 112, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": 112, + "id": "available-steering", + "metadata": { + "id": "available-steering" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "aggressive-climate", + "metadata": { + "id": "aggressive-climate" + }, + "source": [ + "### Exercise 4\n", + "\n", + "Why would it be nonsensical to add `a` and `t`? What happens if you try?" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "id": "documentary-doctrine", + "metadata": { + "id": "documentary-doctrine" + }, + "outputs": [], + "source": [ + "a = 9.8 * meter / second**2\n", + "t = 3.4 * second" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "id": "primary-partner", + "metadata": { + "id": "primary-partner" + }, + "outputs": [], + "source": [ + "# a + t" + ] + }, + { + "cell_type": "markdown", + "id": "legal-audio", + "metadata": { + "id": "legal-audio" + }, + "source": [ + "In this example, you should get a `DimensionalityError`, which indicates that you have violated a rule of dimensional analysis: you cannot add quantities with different dimensions.\n", + "\n", + "The error messages you get from Python are big and scary, but if you read them carefully, they contain a lot of useful information.\n", + "\n", + "The last line usually tells you what type of error happened, and sometimes additional information, so you might want to start from the bottom and read up.\n", + "\n", + "The previous lines are a *traceback* of what was happening when the error occurred. The first section of the traceback shows the code you wrote. The following sections are often from Python libraries." + ] + }, + { + "cell_type": "markdown", + "id": "steady-chancellor", + "metadata": { + "tags": [], + "id": "steady-chancellor" + }, + "source": [ + "Before you go on, you might want to delete the erroneous code so the notebook can run without errors." + ] + }, + { + "cell_type": "markdown", + "id": "valid-empire", + "metadata": { + "id": "valid-empire" + }, + "source": [ + "### Exercise 5\n", + "\n", + "Suppose instead of dropping the penny, you throw it downward at its terminal velocity, $29$ m/s. How long would it take to fall $381$ m?" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "id": "greenhouse-reason", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "greenhouse-reason", + "outputId": "bfa27196-7082-49af-9ad5-513b11edf2e3" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "13.137931034482758 second\n" + ] + } + ], + "source": [ + "# Solution goes here\n", + "distance = 381 * units.meter\n", + "speed = 29 * units.meter / units.second # Terminal velocity of the penny\n", + "\n", + "\n", + "time = distance / speed\n", + "print(time)\n" + ] + }, + { + "cell_type": "markdown", + "id": "asian-murray", + "metadata": { + "id": "asian-murray" + }, + "source": [ + "### Exercise 6:\n", + "\n", + "So far we have considered two models of a falling penny:\n", + "\n", + "* If we ignore air resistance, the penny falls with constant acceleration, and we can compute the time to reach the sidewalk and the velocity of the penny when it gets there.\n", + "\n", + "* If we take air resistance into account, and drop the penny at its terminal velocity, it falls with constant velocity.\n", + "\n", + "Now let's consider a third model that includes elements of the first two: let's assume that the acceleration of the penny is `a` until the penny reaches $29$ m/s, and then $0$ m/s$^2$ afterwards. What is the total time for the penny to fall $381$ m?" + ] + }, + { + "cell_type": "markdown", + "id": "detailed-minnesota", + "metadata": { + "id": "detailed-minnesota" + }, + "source": [ + "You can break this question into three parts:\n", + "\n", + "1. How long would the penny take to reach $29$ m/s with constant acceleration `a`.\n", + "2. How far would it fall during that time?\n", + "3. How long would it take to fall the remaining distance with constant velocity $29$ m/s?\n", + "\n", + "Suggestion: Assign each intermediate result to a variable with a meaningful name. And assign units to all quantities!" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "id": "secure-crowd", + "metadata": { + "id": "secure-crowd" + }, + "outputs": [], + "source": [ + "a = 9.8 * meter / second**2\n", + "h = 381 * meter\n", + "v = 29 * meter / second" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "id": "thirty-minneapolis", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "thirty-minneapolis", + "outputId": "e7ac46c3-564f-4bae-bd88-07874360220c" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "2.9591836734693877 second" + ], + "text/latex": "$2.9591836734693877\\ \\mathrm{second}$" + }, + "metadata": {}, + "execution_count": 142 + } + ], + "source": [ + "# Solution goes here\n", + "time_to_29 = v/a\n", + "time_to_29" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "id": "premier-seeking", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "premier-seeking", + "outputId": "728e4705-2e7b-407e-9269-53581cfb8f87" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "42.90816326530612 meter" + ], + "text/latex": "$42.90816326530612\\ \\mathrm{meter}$" + }, + "metadata": {}, + "execution_count": 143 + } + ], + "source": [ + "# Solution goes here\n", + "distance_to_29 = a * time_to_29**2 / 2\n", + "distance_to_29" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "id": "brave-laundry", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "brave-laundry", + "outputId": "dab18710-a752-48d1-cf58-674fec50e572" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "11.658339197748065 second" + ], + "text/latex": "$11.658339197748065\\ \\mathrm{second}$" + }, + "metadata": {}, + "execution_count": 156 + } + ], + "source": [ + "# Solution goes here\n", + "new = h - distance_to_29\n", + "time_to_finish= new / v\n", + "time_to_finish" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "id": "adjusted-consultation", + "metadata": { + "id": "adjusted-consultation" + }, + "outputs": [], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "id": "understanding-consortium", + "metadata": { + "id": "understanding-consortium" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "composed-tunnel", + "metadata": { + "id": "composed-tunnel" + }, + "source": [ + "### Exercise 7\n", + "\n", + "When I was in high school, the pitcher on the baseball team claimed that when he threw a fastball he was throwing the ball down; that is, the ball left his hand at a downward angle.\n", + "I was skeptical; watching from the side, I thought the ball left his hand at an upward angle.\n", + "\n", + "Can you think of a simple model you could use to settle the argument? What factors would you include and what could you ignore? What quantities would you have to look up or estimate?\n", + "\n", + "I suggest you convert all quantities to SI units like meters and seconds (see )." + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "id": "about-complex", + "metadata": { + "id": "about-complex" + }, + "outputs": [], + "source": [ + "# Solution goes here\n", + "# I plan to ignore air resistance, and how the ball spins" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "unique-owner", + "metadata": { + "id": "unique-owner" + }, + "outputs": [], + "source": [ + "# Solution goes here\n", + "# Factors to Include:\n", + "# Initial Velocity (v₀): The speed at which the ball is thrown.\n", + "# Release Height (h): The height from which the ball is released.\n", + "# Distance to Home Plate (d): The horizontal distance from the pitcher to the catcher.\n", + "# Gravity (g): The acceleration due to gravity (9.81 m/s²).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "id": "minus-batman", + "metadata": { + "id": "minus-batman" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "id": "exact-vegetable", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "exact-vegetable", + "outputId": "7c0fe59e-301f-4260-fba7-b979128700c5" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The angle at which the ball is thrown: -3.64 degrees\n" + ] + } + ], + "source": [ + "# Solution goes here\n", + "import numpy as np\n", + "from scipy.optimize import fsolve #Use fsolve from scipy.optimize to solve for the\n", + "#angle θ that satisfies the vertical motion equation when the ball reaches home plate.\n", + "\n", + "# Given quantities i have to consider\n", + "v0 = 45 # initial velocity in m/s\n", + "h = 2 # release height in meters\n", + "d = 18.44 # distance to home plate in meters\n", + "g = 9.81 # acceleration due to gravity in m/s^2\n", + "\n", + "# Function to solve for the angle θ\n", + "def equations(theta):\n", + " theta_rad = np.radians(theta)\n", + " t = d / (v0 * np.cos(theta_rad))\n", + " y = h + v0 * np.sin(theta_rad) * t - 0.5 * g * t**2\n", + " return y\n", + "\n", + "# Initial guess for the angle\n", + "initial_guess = 0\n", + "\n", + "# Solve for the angle\n", + "angle_solution = fsolve(equations, initial_guess)\n", + "\n", + "print(f\"The angle at which the ball is thrown: {angle_solution[0]:.2f} degrees\")" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "id": "neutral-lightning", + "metadata": { + "id": "neutral-lightning" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "jewish-secret", + "metadata": { + "id": "jewish-secret" + }, + "source": [ + "### Exercise 8\n", + "\n", + "Suppose I run a 10K race in 44:52 (44 minutes and 52 seconds). What is my average pace in minutes per mile?" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "id": "virgin-cambodia", + "metadata": { + "id": "virgin-cambodia" + }, + "outputs": [], + "source": [ + "mile = units.mile\n", + "kilometer = units.kilometer\n", + "minute = units.minute" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "id": "right-intention", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "right-intention", + "outputId": "9ae4a782-2137-46f4-beb6-0a2e6d5b26dd" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "6.2137119223733395 mile" + ], + "text/latex": "$6.2137119223733395\\ \\mathrm{mile}$" + }, + "metadata": {}, + "execution_count": 154 + } + ], + "source": [ + "# Solution goes here\n", + "time = 44.875 * minute\n", + "distance = 10 * kilometer\n", + "distance = distance.to(mile)\n", + "distance" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "id": "mineral-sally", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "mineral-sally", + "outputId": "0c78c858-f36a-45a1-94ee-d88fd3326872" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "7.2219312 minute/mile" + ], + "text/latex": "$7.2219312\\ \\frac{\\mathrm{minute}}{\\mathrm{mile}}$" + }, + "metadata": {}, + "execution_count": 155 + } + ], + "source": [ + "# Solution goes here\n", + "pace = time / distance\n", + "pace" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "id": "coral-camel", + "metadata": { + "id": "coral-camel" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "preceding-cricket", + "metadata": { + "id": "preceding-cricket" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "id": "effective-rendering", + "metadata": { + "id": "effective-rendering" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "id": "printable-reply", + "metadata": { + "id": "printable-reply" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "environmental-wallet", + "metadata": { + "tags": [], + "id": "environmental-wallet" + }, + "source": [ + "## More Jupyter\n", + "\n", + "You can add and remove cells from a notebook using the buttons in the toolbar and the items in the menu, both of which you should see at the top of this notebook.\n", + "\n", + "Try the following exercises:\n", + "\n", + "1. From the Insert menu select \"Insert cell below\" to add a cell below this one. By default, you get a code cell, as you can see in the pulldown menu that says \"Code\".\n", + "\n", + "2. In the new cell, add a print statement like `print('Hello')`, and run it.\n", + "\n", + "3. Add another cell, select the new cell, and then click on the pulldown menu that says \"Code\" and select \"Markdown\". This makes the new cell a text cell.\n", + "\n", + "4. In the new cell, type some text, and then run it.\n", + "\n", + "5. Use the arrow buttons in the toolbar to move cells up and down.\n", + "\n", + "6. Use the cut, copy, and paste buttons to delete, add, and move cells.\n", + "\n", + "7. As you make changes, Jupyter saves your notebook automatically, but if you want to make sure, you can press the save button, which looks like a floppy disk from the 1990s.\n", + "\n", + "8. Finally, when you are done with a notebook, select \"Close and Halt\" from the File menu." + ] + }, + { + "cell_type": "markdown", + "id": "successful-kentucky", + "metadata": { + "tags": [], + "id": "successful-kentucky" + }, + "source": [ + "When you change the contents of a cell, you have to run it again for those changes to have an effect. If you forget to do that, the results can be confusing, because the code you are looking at is not the code you ran.\n", + "\n", + "If you ever lose track of which cells have run, and in what order, you should go to the Kernel menu and select \"Restart & Run All\". Restarting the kernel means that all of your variables get deleted, and running all the cells means all of your code will run again, in the right order.\n", + "\n", + "Select \"Restart & Run All\" now and confirm that it runs all of the cells." + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "id": "weighted-quebec", + "metadata": { + "id": "weighted-quebec" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.13" + }, + "colab": { + "provenance": [] + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/chapters/chap02.ipynb b/chapters/chap02.ipynb index b02e008c..e34c2510 100644 --- a/chapters/chap02.ipynb +++ b/chapters/chap02.ipynb @@ -1,1238 +1,7147 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "a8b892c8", - "metadata": {}, - "source": [ - "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." - ] + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": { + "id": "a8b892c8" + }, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "victorian-latitude", + "metadata": { + "id": "victorian-latitude" + }, + "source": [ + "# Bike Share System" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [], + "id": "imported-table" + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [], + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "electoral-turkey", + "outputId": "2e68f26c-038c-4dfa-f092-9c876226e111" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting pint\n", + " Downloading Pint-0.24.4-py3-none-any.whl.metadata (8.5 kB)\n", + "Requirement already satisfied: platformdirs>=2.1.0 in /usr/local/lib/python3.11/dist-packages (from pint) (4.3.6)\n", + "Requirement already satisfied: typing-extensions>=4.0.0 in /usr/local/lib/python3.11/dist-packages (from pint) (4.12.2)\n", + "Collecting flexcache>=0.3 (from pint)\n", + " Downloading flexcache-0.3-py3-none-any.whl.metadata (7.0 kB)\n", + "Collecting flexparser>=0.4 (from pint)\n", + " Downloading flexparser-0.4-py3-none-any.whl.metadata (18 kB)\n", + "Downloading Pint-0.24.4-py3-none-any.whl (302 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m302.0/302.0 kB\u001b[0m \u001b[31m21.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading flexcache-0.3-py3-none-any.whl (13 kB)\n", + "Downloading flexparser-0.4-py3-none-any.whl (27 kB)\n", + "Installing collected packages: flexparser, flexcache, pint\n", + "Successfully installed flexcache-0.3 flexparser-0.4 pint-0.24.4\n" + ] + } + ], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [], + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "formal-context", + "outputId": "bee4d725-0bd9-463b-f49e-cba51ba364c3" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloaded modsim.py\n" + ] + } + ], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + "\n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [], + "id": "progressive-typing" + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "unlimited-antenna", + "metadata": { + "id": "unlimited-antenna" + }, + "source": [ + "This chapter presents a simple model of a bike share system and\n", + "demonstrates the features of Python we'll use to develop simulations of real-world systems.\n", + "\n", + "Along the way, we'll make decisions about how to model the system. In\n", + "the next chapter we'll review these decisions and gradually improve the model." + ] + }, + { + "cell_type": "markdown", + "id": "electronic-radius", + "metadata": { + "id": "electronic-radius" + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises.\n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "above-denial", + "metadata": { + "id": "above-denial" + }, + "source": [ + "## Modeling a Bike Share System\n", + "\n", + "Imagine a bike share system for students traveling between Olin College and Wellesley College, which are about three miles apart in eastern Massachusetts.\n", + "\n", + "Suppose the system contains 12 bikes and two bike racks, one at Olin and one at Wellesley, each with the capacity to hold 12 bikes.\n", + "\n", + "As students arrive, check out a bike, and ride to the other campus, the number of bikes in each location changes. In the simulation, we'll need to keep track of where the bikes are. To do that, we'll use a function called `State`, which is defined in the ModSim library." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "incorrect-comparison", + "metadata": { + "id": "incorrect-comparison" + }, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2)" + ] + }, + { + "cell_type": "markdown", + "id": "living-wayne", + "metadata": { + "id": "living-wayne" + }, + "source": [ + "The equations in parentheses create two variables, `olin` and `wellesley`, and give them the values `10` and `2`.\n", + "The `State` function stores these variables and their values in a `State` object, which gets assigned to a new variable named `bikeshare`.\n", + "\n", + "Variables stored inside a `State` object are called *state variables*.\n", + "In this example, the state variables represent the number of\n", + "bikes at each location. Their values indicate that there are 10 bikes at Olin and 2 at Wellesley.\n", + "\n", + "The `State` object is assigned to a new variable named `bikeshare`.\n", + "We can get the value of a variable in a `State` object using the *dot operator*, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "brief-diversity", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "brief-diversity", + "outputId": "57fbd5b6-e083-496b-b495-94aa2ce222a8" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "10" + ] + }, + "metadata": {}, + "execution_count": 5 + } + ], + "source": [ + "bikeshare.olin" + ] + }, + { + "cell_type": "markdown", + "id": "intermediate-midwest", + "metadata": { + "id": "intermediate-midwest" + }, + "source": [ + "And this:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "designed-brazilian", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "designed-brazilian", + "outputId": "2542c31a-da76-4cf6-fb9e-89c41e55f460" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "2" + ] + }, + "metadata": {}, + "execution_count": 6 + } + ], + "source": [ + "bikeshare.wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "phantom-oklahoma", + "metadata": { + "id": "phantom-oklahoma" + }, + "source": [ + "Or, to display all of the state variables and their values, you can enter just the name of the object:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "impaired-potter", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 147 + }, + "id": "impaired-potter", + "outputId": "0712f059-7928-4381-bf4d-c7794c2b088d" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "olin 10\n", + "wellesley 2\n", + "Name: state, dtype: int64" + ], + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
state
olin10
wellesley2
\n", + "

" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ], + "source": [ + "bikeshare" + ] + }, + { + "cell_type": "markdown", + "id": "vital-journal", + "metadata": { + "id": "vital-journal" + }, + "source": [ + "These values make up the *state* of the system." + ] + }, + { + "cell_type": "markdown", + "id": "fleet-beijing", + "metadata": { + "tags": [], + "id": "fleet-beijing" + }, + "source": [ + "The ModSim library provides a function called `show` that displays a `State` object as a table." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "basic-fabric", + "metadata": { + "tags": [], + "colab": { + "base_uri": "https://localhost:8080/", + "height": 781 + }, + "id": "basic-fabric", + "outputId": "98157fcd-2d48-455e-9d38-848a1e207b63" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " state\n", + "olin 10\n", + "wellesley 2" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
state
olin10
wellesley2
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \"show(bikeshare)\",\n \"rows\": 2,\n \"fields\": [\n {\n \"column\": \"state\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 5,\n \"min\": 2,\n \"max\": 10,\n \"num_unique_values\": 2,\n \"samples\": [\n 2,\n 10\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 8 + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "

Distributions

\n", + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "from matplotlib import pyplot as plt\n", + "_df_0['state'].plot(kind='hist', bins=20, title='state')\n", + "plt.gca().spines[['top', 'right',]].set_visible(False)" + ], + "text/html": [ + "
\n", + " \n", + "
\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "

Categorical distributions

\n", + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "from matplotlib import pyplot as plt\n", + "import seaborn as sns\n", + "_df_1.groupby('index').size().plot(kind='barh', color=sns.palettes.mpl_palette('Dark2'))\n", + "plt.gca().spines[['top', 'right',]].set_visible(False)" + ], + "text/html": [ + "
\n", + " \n", + "
\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "

Values

\n", + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "from matplotlib import pyplot as plt\n", + "_df_2['state'].plot(kind='line', figsize=(8, 4), title='state')\n", + "plt.gca().spines[['top', 'right']].set_visible(False)" + ], + "text/html": [ + "
\n", + " \n", + "
\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "

Faceted distributions

\n", + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + ":5: FutureWarning: \n", + "\n", + "Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `y` variable to `hue` and set `legend=False` for the same effect.\n", + "\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "from matplotlib import pyplot as plt\n", + "import seaborn as sns\n", + "figsize = (12, 1.2 * len(_df_3['index'].unique()))\n", + "plt.figure(figsize=figsize)\n", + "sns.violinplot(_df_3, x='state', y='index', inner='stick', palette='Dark2')\n", + "sns.despine(top=True, right=True, bottom=True, left=True)" + ], + "text/html": [ + "
\n", + " \n", + "
\n", + " \n", + " " + ] + }, + "metadata": {} + } + ], + "source": [ + "show(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "specified-definition", + "metadata": { + "tags": [], + "id": "specified-definition" + }, + "source": [ + "You don't have to use `show`, but I think the results look better." + ] + }, + { + "cell_type": "markdown", + "id": "delayed-ocean", + "metadata": { + "id": "delayed-ocean" + }, + "source": [ + "We can update the state by assigning new values to the variables.\n", + "For example, if a student moves a bike from Olin to Wellesley, we can figure out the new values and assign them:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "floppy-trainer", + "metadata": { + "id": "floppy-trainer" + }, + "outputs": [], + "source": [ + "bikeshare.olin = 9\n", + "bikeshare.wellesley = 3" + ] + }, + { + "cell_type": "markdown", + "id": "natural-gossip", + "metadata": { + "id": "natural-gossip" + }, + "source": [ + "Or we can use *update operators*, `-=` and `+=`, to subtract 1 from\n", + "`olin` and add 1 to `wellesley`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "hungarian-bride", + "metadata": { + "id": "hungarian-bride" + }, + "outputs": [], + "source": [ + "bikeshare.olin -= 1\n", + "bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "radical-mills", + "metadata": { + "id": "radical-mills" + }, + "source": [ + "The result is the same either way." + ] + }, + { + "cell_type": "markdown", + "id": "controversial-opportunity", + "metadata": { + "id": "controversial-opportunity" + }, + "source": [ + "## Defining Functions\n", + "\n", + "So far we have used functions defined in NumPy and the ModSim library. Now we're going to define our own functions.\n", + "\n", + "When you are developing code in Jupyter, it is often efficient to write a few lines of code, test them to confirm they do what you intend, and then use them to define a new function. For example, these lines move a bike from Olin to Wellesley:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "vertical-drawing", + "metadata": { + "id": "vertical-drawing" + }, + "outputs": [], + "source": [ + "bikeshare.olin -= 1\n", + "bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "approximate-rolling", + "metadata": { + "id": "approximate-rolling" + }, + "source": [ + "Rather than repeat them every time a bike moves, we can define a new\n", + "function:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "significant-nutrition", + "metadata": { + "id": "significant-nutrition" + }, + "outputs": [], + "source": [ + "def bike_to_wellesley():\n", + " bikeshare.olin -= 1\n", + " bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "generous-tracker", + "metadata": { + "id": "generous-tracker" + }, + "source": [ + "`def` is a special word in Python that indicates we are defining a new\n", + "function. The name of the function is `bike_to_wellesley`. The empty\n", + "parentheses indicate that this function requires no additional\n", + "information when it runs. The colon indicates the beginning of an\n", + "indented *code block*.\n", + "\n", + "The next two lines are the *body* of the function. They have to be\n", + "indented; by convention, the indentation is four spaces.\n", + "\n", + "When you define a function, it has no immediate effect. The body of the\n", + "function doesn't run until you *call* the function. Here's how to call\n", + "this function:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "moving-jurisdiction", + "metadata": { + "id": "moving-jurisdiction" + }, + "outputs": [], + "source": [ + "bike_to_wellesley()" + ] + }, + { + "cell_type": "markdown", + "id": "meaningful-christmas", + "metadata": { + "id": "meaningful-christmas" + }, + "source": [ + "When you call the function, it runs the statements in the body, which\n", + "update the variables of the `bikeshare` object; you can check by\n", + "displaying the new state." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "proper-symposium", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 454 + }, + "id": "proper-symposium", + "outputId": "c3baa74c-4cd2-42f4-9b62-6282a47e890a" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " state\n", + "olin 6\n", + "wellesley 6" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
state
olin6
wellesley6
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \"show(bikeshare)\",\n \"rows\": 2,\n \"fields\": [\n {\n \"column\": \"state\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 6,\n \"max\": 6,\n \"num_unique_values\": 1,\n \"samples\": [\n 6\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 14 + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "

Categorical distributions

\n", + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "from matplotlib import pyplot as plt\n", + "import seaborn as sns\n", + "_df_4.groupby('index').size().plot(kind='barh', color=sns.palettes.mpl_palette('Dark2'))\n", + "plt.gca().spines[['top', 'right',]].set_visible(False)" + ], + "text/html": [ + "
\n", + " \n", + "
\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "

Time series

\n", + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "from matplotlib import pyplot as plt\n", + "import seaborn as sns\n", + "def _plot_series(series, series_name, series_index=0):\n", + " palette = list(sns.palettes.mpl_palette('Dark2'))\n", + " counted = (series['state']\n", + " .value_counts()\n", + " .reset_index(name='counts')\n", + " .rename({'index': 'state'}, axis=1)\n", + " .sort_values('state', ascending=True))\n", + " xs = counted['state']\n", + " ys = counted['counts']\n", + " plt.plot(xs, ys, label=series_name, color=palette[series_index % len(palette)])\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 5.2), layout='constrained')\n", + "df_sorted = _df_5.sort_values('state', ascending=True)\n", + "for i, (series_name, series) in enumerate(df_sorted.groupby('index')):\n", + " _plot_series(series, series_name, i)\n", + " fig.legend(title='index', bbox_to_anchor=(1, 1), loc='upper left')\n", + "sns.despine(fig=fig, ax=ax)\n", + "plt.xlabel('state')\n", + "_ = plt.ylabel('count()')" + ], + "text/html": [ + "
\n", + " \n", + "
\n", + " \n", + " " + ] + }, + "metadata": {} + } + ], + "source": [ + "show(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "eleven-brook", + "metadata": { + "id": "eleven-brook" + }, + "source": [ + "When you call a function, you have to include the parentheses. If you\n", + "leave them out, you get this:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "identical-yacht", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 104 + }, + "id": "identical-yacht", + "outputId": "20276d03-e1bb-44f0-835d-89f764b5e28e" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
\n", + "
bike_to_wellesley
def bike_to_wellesley()
/content/<ipython-input-12-0ba59ca0b597><no docstring>
" + ] + }, + "metadata": {}, + "execution_count": 15 + } + ], + "source": [ + "bike_to_wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "premier-youth", + "metadata": { + "id": "premier-youth" + }, + "source": [ + "This result indicates that `bike_to_wellesley` is a function. You don't have to know what `__main__` means, but if you see something like this, it probably means that you named a function but didn't actually call it.\n", + "So don't forget the parentheses." + ] + }, + { + "cell_type": "markdown", + "id": "brazilian-medicare", + "metadata": { + "id": "brazilian-medicare" + }, + "source": [ + "## Print Statements\n", + "\n", + "As you write more complicated programs, it is easy to lose track of what\n", + "is going on. One of the most useful tools for debugging is the *print statement*, which displays text in the Jupyter notebook.\n", + "\n", + "Normally when Jupyter runs the code in a cell, it displays the value of\n", + "the last line of code. For example, if you run:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "heavy-patrol", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "heavy-patrol", + "outputId": "907aae98-c5f0-4c72-bbf9-24bd40d7c8cb" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "6" + ] + }, + "metadata": {}, + "execution_count": 16 + } + ], + "source": [ + "bikeshare.olin\n", + "bikeshare.wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "ancient-projection", + "metadata": { + "id": "ancient-projection" + }, + "source": [ + "Jupyter runs both lines, but it only displays the value of the\n", + "second. If you want to display more than one value, you can use\n", + "print statements:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "french-preference", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "french-preference", + "outputId": "bc53827d-7a02-4265-f49f-4d2a5a25da12" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "6\n", + "6\n" + ] + } + ], + "source": [ + "print(bikeshare.olin)\n", + "print(bikeshare.wellesley)" + ] + }, + { + "cell_type": "markdown", + "id": "original-hollywood", + "metadata": { + "id": "original-hollywood" + }, + "source": [ + "When you call the `print` function, you can put a variable in\n", + "parentheses, as in the previous example, or you can provide a sequence\n", + "of variables separated by commas, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "alternative-keyboard", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "alternative-keyboard", + "outputId": "874024cc-4e5e-42c1-cadf-f8dae495a34a" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "6 6\n" + ] + } + ], + "source": [ + "print(bikeshare.olin, bikeshare.wellesley)" + ] + }, + { + "cell_type": "markdown", + "id": "described-produce", + "metadata": { + "id": "described-produce" + }, + "source": [ + "Python looks up the values of the variables and displays them; in this\n", + "example, it displays two values on the same line, with a space between\n", + "them.\n", + "\n", + "Print statements are useful for debugging functions. For example, we can\n", + "add a print statement to `bike_to_wellesley`, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "robust-holly", + "metadata": { + "id": "robust-holly" + }, + "outputs": [], + "source": [ + "def bike_to_wellesley():\n", + " print('Moving a bike to Wellesley')\n", + " bikeshare.olin -= 1\n", + " bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "vital-lender", + "metadata": { + "id": "vital-lender" + }, + "source": [ + "Each time we call this version of the function, it displays a message,\n", + "which can help us keep track of what the program is doing.\n", + "The message in this example is a *string*, which is a sequence of\n", + "letters and other symbols in quotes.\n", + "\n", + "Just like `bike_to_wellesley`, we can define a function that moves a\n", + "bike from Wellesley to Olin:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "fifteen-atmosphere", + "metadata": { + "id": "fifteen-atmosphere" + }, + "outputs": [], + "source": [ + "def bike_to_olin():\n", + " print('Moving a bike to Olin')\n", + " bikeshare.wellesley -= 1\n", + " bikeshare.olin += 1" + ] + }, + { + "cell_type": "markdown", + "id": "requested-glasgow", + "metadata": { + "id": "requested-glasgow" + }, + "source": [ + "And call it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "matched-narrow", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "matched-narrow", + "outputId": "83cc11f4-8064-4111-8e0a-507a03da2b91" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Moving a bike to Olin\n" + ] + } + ], + "source": [ + "bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "sitting-semiconductor", + "metadata": { + "id": "sitting-semiconductor" + }, + "source": [ + "One benefit of defining functions is that you avoid repeating chunks of\n", + "code, which makes programs smaller. Another benefit is that the name you\n", + "give the function documents what it does, which makes programs more\n", + "readable." + ] + }, + { + "cell_type": "markdown", + "id": "enhanced-maintenance", + "metadata": { + "id": "enhanced-maintenance" + }, + "source": [ + "## If Statements\n", + "\n", + "At this point we have functions that simulate moving bikes; now let's think about simulating customers. As a simple model of customer behavior, I will use a random number generator to determine when customers arrive at each station.\n", + "\n", + "The ModSim library provides a function called `flip` that generates random \"coin tosses\".\n", + "When you call it, you provide a probability between 0 and 1, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "29c1f41a", + "metadata": { + "tags": [], + "id": "29c1f41a" + }, + "outputs": [], + "source": [ + "# this line sets the random number generator so the results in\n", + "# the book are the same every time we run it\n", + "np.random.seed(17)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "illegal-metropolitan", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "illegal-metropolitan", + "outputId": "725eb24a-7c25-424d-9db1-027838b4b4db" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "True" + ] + }, + "metadata": {}, + "execution_count": 23 + } + ], + "source": [ + "flip(0.7)" + ] + }, + { + "cell_type": "markdown", + "id": "appropriate-funds", + "metadata": { + "id": "appropriate-funds" + }, + "source": [ + "The result is one of two values: `True` with probability 0.7 (in this example) or `False`\n", + "with probability 0.3. If you run `flip` like this 100 times, you should\n", + "get `True` about 70 times and `False` about 30 times. But the results\n", + "are random, so they might differ from these expectations.\n", + "\n", + "`True` and `False` are special values defined by Python.\n", + "They are called *boolean* values because they are\n", + "related to Boolean algebra ().\n", + "\n", + "Note that they are not strings. There is a difference between `True`, which is a boolean value, and `'True'`, which is a string.\n", + "\n", + "We can use boolean values to control the behavior of the program, using an *if statement*:" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "excessive-murder", + "metadata": { + "id": "excessive-murder" + }, + "outputs": [], + "source": [ + "if flip(0.5):\n", + " print('heads')" + ] + }, + { + "cell_type": "markdown", + "id": "seventh-profile", + "metadata": { + "id": "seventh-profile" + }, + "source": [ + "If the result from `flip` is `True`, the program displays the string\n", + "`'heads'`. Otherwise it does nothing.\n", + "\n", + "The syntax for `if` statements is similar to the syntax for\n", + "function definitions: the first line has to end with a colon, and the\n", + "lines inside the `if` statement have to be indented.\n", + "\n", + "Optionally, you can add an *else clause* to indicate what should\n", + "happen if the result is `False`:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "fundamental-nursing", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fundamental-nursing", + "outputId": "38617c4d-f694-419b-a179-490b5a9180b2" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "heads\n" + ] + } + ], + "source": [ + "if flip(0.5):\n", + " print('heads')\n", + "else:\n", + " print('tails')" + ] + }, + { + "cell_type": "markdown", + "id": "recovered-chemical", + "metadata": { + "id": "recovered-chemical" + }, + "source": [ + "If you run the previous cell a few times, it should print `heads` about half the time, and `tails` about half the time.\n", + "\n", + "Now we can use `flip` to simulate the arrival of customers who want to\n", + "borrow a bike. Suppose students arrive at the Olin station every two\n", + "minutes on average.\n", + "In that case, the chance of an arrival during any one-minute period is 50%, and we can simulate it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "twenty-health", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "twenty-health", + "outputId": "27fa500d-0fdc-4a98-9eda-dcc6eaa7a537" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Moving a bike to Wellesley\n" + ] + } + ], + "source": [ + "if flip(0.5):\n", + " bike_to_wellesley()" + ] + }, + { + "cell_type": "markdown", + "id": "difficult-construction", + "metadata": { + "id": "difficult-construction" + }, + "source": [ + "If students arrive at the Wellesley station every three minutes, on average,\n", + "the chance of an arrival during any one-minute period is 33%, and we can\n", + "simulate it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "played-character", + "metadata": { + "id": "played-character" + }, + "outputs": [], + "source": [ + "if flip(0.33):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "standard-party", + "metadata": { + "id": "standard-party" + }, + "source": [ + "We can combine these snippets into a function that simulates a *time step*, which is an interval of time, in this case one minute:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "ecological-colon", + "metadata": { + "id": "ecological-colon" + }, + "outputs": [], + "source": [ + "def step():\n", + " if flip(0.5):\n", + " bike_to_wellesley()\n", + "\n", + " if flip(0.33):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "amateur-exposure", + "metadata": { + "id": "amateur-exposure" + }, + "source": [ + "Then we can simulate a time step like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "mediterranean-german", + "metadata": { + "id": "mediterranean-german" + }, + "outputs": [], + "source": [ + "step()" + ] + }, + { + "cell_type": "markdown", + "id": "sought-mobile", + "metadata": { + "id": "sought-mobile" + }, + "source": [ + "Depending on the results from `flip`, this function might move a bike to Olin, or to Wellesley, or neither, or both." + ] + }, + { + "cell_type": "markdown", + "id": "organic-proportion", + "metadata": { + "id": "organic-proportion" + }, + "source": [ + "## Parameters\n", + "\n", + "The previous version of `step` is fine if the arrival probabilities\n", + "never change, but in reality they vary over time.\n", + "\n", + "So instead of putting the constant values 0.5 and 0.33 in `step`, we can replace them with *parameters*.\n", + "Parameters are variables whose values are set when a function is called.\n", + "\n", + "Here's a version of `step` that takes two parameters, `p1` and `p2`:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "hollywood-shopping", + "metadata": { + "id": "hollywood-shopping" + }, + "outputs": [], + "source": [ + "def step(p1, p2):\n", + " if flip(p1):\n", + " bike_to_wellesley()\n", + "\n", + " if flip(p2):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "encouraging-arkansas", + "metadata": { + "id": "encouraging-arkansas" + }, + "source": [ + "The values of `p1` and `p2` are not set inside this function; instead,\n", + "they are provided when the function is called, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "buried-alert", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "buried-alert", + "outputId": "22b795c3-ba95-4574-a5d6-182a0b7dc4e6" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Moving a bike to Olin\n" + ] + } + ], + "source": [ + "step(0.5, 0.33)" + ] + }, + { + "cell_type": "markdown", + "id": "aggregate-dynamics", + "metadata": { + "id": "aggregate-dynamics" + }, + "source": [ + "The values you provide when you call the function are called\n", + "*arguments*. The arguments, `0.5` and `0.33` in this example, get\n", + "assigned to the parameters, `p1` and `p2`, in order. So running this\n", + "function has the same effect as:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "recognized-denmark", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "recognized-denmark", + "outputId": "39fa742c-fc1b-4777-82f1-012fcb3e46f2" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Moving a bike to Wellesley\n" + ] + } + ], + "source": [ + "p1 = 0.5\n", + "p2 = 0.33\n", + "\n", + "if flip(p1):\n", + " bike_to_wellesley()\n", + "\n", + "if flip(p2):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "raised-museum", + "metadata": { + "id": "raised-museum" + }, + "source": [ + "The advantage of using parameters is that you can call the same function many times, providing different arguments each time.\n", + "\n", + "Adding parameters to a function is called *generalization*, because it makes the function more general; without parameters, the function always does the same thing; with parameters, it can do a range of things." + ] + }, + { + "cell_type": "markdown", + "id": "scenic-african", + "metadata": { + "id": "scenic-african" + }, + "source": [ + "## For Loops\n", + "\n", + "At some point you will get sick of running cells over and over.\n", + "Fortunately, there is an easy way to repeat a chunk of code, the *for loop*. Here's an example:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "polish-river", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "polish-river", + "outputId": "ee2f1d62-5e59-4aa3-c987-fdfc00d9f1e3" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0\n", + "Moving a bike to Wellesley\n", + "1\n", + "Moving a bike to Wellesley\n", + "2\n", + "Moving a bike to Wellesley\n" + ] + } + ], + "source": [ + "for i in range(3):\n", + " print(i)\n", + " bike_to_wellesley()" + ] + }, + { + "cell_type": "markdown", + "id": "compatible-conspiracy", + "metadata": { + "id": "compatible-conspiracy" + }, + "source": [ + "The syntax here should look familiar; the first line ends with a\n", + "colon, and the lines inside the `for` loop are indented. The other\n", + "elements of the loop are:\n", + "\n", + "- The words `for` and `in` are special words we have to use in a for\n", + " loop.\n", + "\n", + "- `range` is a Python function we use to control the number of times the loop runs.\n", + "\n", + "- `i` is a *loop variable* that gets created when the for loop runs.\n", + "\n", + "When this loop runs, it runs the statements inside the loop three times. The first time, the value of `i` is `0`; the second time, it is `1`; the third time, it is `2`.\n", + "\n", + "Each time through the loop, it prints the value of `i` and moves one bike to Wellesley." + ] + }, + { + "cell_type": "markdown", + "id": "breeding-groove", + "metadata": { + "id": "breeding-groove" + }, + "source": [ + "## TimeSeries\n", + "\n", + "When we run a simulation, we often want to save the results for later analysis. The ModSim library provides a `TimeSeries` object for this purpose. A `TimeSeries` contains a sequence of timestamps and a\n", + "corresponding sequence of quantities.\n", + "\n", + "In this example, the timestamps are integers representing minutes and the quantities are the number of bikes at one location.\n", + "\n", + "Since we have moved a number of bikes around, let's start again with a new `State` object." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "every-consultation", + "metadata": { + "id": "every-consultation" + }, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2)" + ] + }, + { + "cell_type": "markdown", + "id": "cross-sharp", + "metadata": { + "id": "cross-sharp" + }, + "source": [ + "We can create a new, empty `TimeSeries` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "changing-planet", + "metadata": { + "id": "changing-planet" + }, + "outputs": [], + "source": [ + "results = TimeSeries()" + ] + }, + { + "cell_type": "markdown", + "id": "attractive-revision", + "metadata": { + "id": "attractive-revision" + }, + "source": [ + "And we can add a quantity like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "aquatic-richardson", + "metadata": { + "id": "aquatic-richardson" + }, + "outputs": [], + "source": [ + "results[0] = bikeshare.olin" + ] + }, + { + "cell_type": "markdown", + "id": "searching-funeral", + "metadata": { + "id": "searching-funeral" + }, + "source": [ + "The number in brackets is the timestamp, also called a *label*.\n", + "\n", + "We can use a `TimeSeries` inside a for loop to store the results of the simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "english-titanium", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "english-titanium", + "outputId": "1808f0c0-2c60-4ac0-f6fc-452183330dc2" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0\n", + "Moving a bike to Wellesley\n", + "1\n", + "Moving a bike to Olin\n", + "2\n", + "Moving a bike to Olin\n" + ] + } + ], + "source": [ + "for i in range(3):\n", + " print(i)\n", + " step(0.6, 0.6)\n", + " results[i+1] = bikeshare.olin" + ] + }, + { + "cell_type": "markdown", + "id": "prospective-joining", + "metadata": { + "id": "prospective-joining" + }, + "source": [ + "Each time through the loop, we print the value of `i` and call `step`, which updates `bikeshare`.\n", + "Then we store the number of bikes at Olin in `results`.\n", + "We use the loop variable, `i`, to compute the timestamp, `i+1`.\n", + "\n", + "The first time through the loop, the value of `i` is `0`, so the timestamp is `1`.\n", + "The last time, the value of `i` is `2`, so the timestamp is `3`.\n", + "\n", + "When the loop exits, `results` contains 4 timestamps, from 0 through\n", + "3, and the number of bikes at Olin at the end of each time step.\n", + "\n", + "We can display the `TimeSeries` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "indonesian-singing", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 206 + }, + "id": "indonesian-singing", + "outputId": "9ed1e9ba-6696-44ce-ffb8-efe460203769" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " Quantity\n", + "Time \n", + "0 10\n", + "1 9\n", + "2 10\n", + "3 11" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Quantity
Time
010
19
210
311
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \"show(results)\",\n \"rows\": 4,\n \"fields\": [\n {\n \"column\": \"Time\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 1,\n \"min\": 0,\n \"max\": 3,\n \"num_unique_values\": 4,\n \"samples\": [\n 1,\n 3,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Quantity\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0,\n \"min\": 9,\n \"max\": 11,\n \"num_unique_values\": 3,\n \"samples\": [\n 10,\n 9,\n 11\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 38 + } + ], + "source": [ + "show(results)" + ] + }, + { + "cell_type": "markdown", + "id": "small-encoding", + "metadata": { + "id": "small-encoding" + }, + "source": [ + "The left column is the timestamps; the right column is the quantities." + ] + }, + { + "cell_type": "markdown", + "id": "following-contrary", + "metadata": { + "id": "following-contrary" + }, + "source": [ + "## Plotting\n", + "\n", + "`results` provides a function called `plot` we can use to plot\n", + "the results, and the ModSim library provides `decorate`, which we can use to label the axes and give the figure a title:" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "saved-hands", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 309 + }, + "id": "saved-hands", + "outputId": "cd4b4ce5-a299-42ef-eff6-87d4b6020d5c" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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\n" + }, + "metadata": {} + } + ], + "source": [ + "results.plot()\n", + "\n", + "decorate(title='Olin-Wellesley bikeshare',\n", + " xlabel='Time step (min)',\n", + " ylabel='Number of bikes')" + ] + }, + { + "cell_type": "markdown", + "id": "egyptian-korea", + "metadata": { + "id": "egyptian-korea" + }, + "source": [ + "The result should be a plot with time on the $x$-axis and the number of bikes on the $y$-axis.\n", + "Since we only ran three time steps, it might not be very interesting." + ] + }, + { + "cell_type": "markdown", + "id": "limited-interstate", + "metadata": { + "id": "limited-interstate" + }, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces the tools we need to run simulations, record the results, and plot them.\n", + "\n", + "We used a `State` object to represent the state of the system.\n", + "Then we used the `flip` function and an `if` statement to simulate a single time step.\n", + "We used a `for` loop to simulate a series of steps, and a `TimeSeries` to record the results.\n", + "Finally, we used `plot` and `decorate` to plot the results.\n", + "\n", + "In the next chapter, we will extend this simulation to make it a little more realistic." + ] + }, + { + "cell_type": "markdown", + "id": "fallen-surprise", + "metadata": { + "id": "fallen-surprise" + }, + "source": [ + "## Exercises\n", + "\n", + "Before you go on, you might want to work on the following exercises." + ] + }, + { + "cell_type": "markdown", + "id": "capital-internship", + "metadata": { + "id": "capital-internship" + }, + "source": [ + "### Exercise 1\n", + "\n", + "What happens if you spell the name of a state variable wrong? Edit the following cell, change the spelling of `wellesley`, and run it.\n", + "\n", + "The error message uses the word *attribute*, which is another name for what we are calling a state variable." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "helpful-zambia", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "helpful-zambia", + "outputId": "e6dc1b49-93a1-4902-f388-193ffa6f7c31" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "2" + ] + }, + "metadata": {}, + "execution_count": 40 + } + ], + "source": [ + "bikeshare = State(olin=10, wellesley=2)\n", + "\n", + "bikeshare.wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "dirty-multiple", + "metadata": { + "id": "dirty-multiple" + }, + "source": [ + "### Exercise 2\n", + "\n", + "Make a `State` object with a third state variable, called `downtown`, with initial value 0, and display the state of the system." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "beneficial-mainland", + "metadata": { + "id": "beneficial-mainland" + }, + "outputs": [], + "source": [ + "# Solution goes here\n", + "bikeshare = State(olin=10, wellesley=2, downtown=0)" + ] + }, + { + "cell_type": "code", + "source": [ + "show(bikeshare)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "584t4liVFA5W", + "outputId": "3fe20df9-eccb-4158-daf2-4134439bf4b4" + }, + "id": "584t4liVFA5W", + "execution_count": 49, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " state\n", + "olin 10\n", + "wellesley 2\n", + "downtown 0" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
state
olin10
wellesley2
downtown0
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \"show(bikeshare)\",\n \"rows\": 3,\n \"fields\": [\n {\n \"column\": \"state\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 5,\n \"min\": 0,\n \"max\": 10,\n \"num_unique_values\": 3,\n \"samples\": [\n 10,\n 2,\n 0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 49 + } + ] + }, + { + "cell_type": "markdown", + "id": "christian-madrid", + "metadata": { + "id": "christian-madrid" + }, + "source": [ + "### Exercise 3\n", + "\n", + "Wrap the code in the chapter in a function named `run_simulation` that takes three parameters, named `p1`, `p2`, and `num_steps`.\n", + "\n", + "It should:\n", + "\n", + "1. Create a `TimeSeries` object to hold the results.\n", + "\n", + "2. Use a for loop to run `step` the number of times specified by `num_steps`, passing along the specified values of `p1` and `p2`.\n", + "\n", + "3. After each step, it should save the number of bikes at Olin in the `TimeSeries`.\n", + "\n", + "4. After the for loop, it should plot the results and\n", + "\n", + "5. Decorate the axes.\n", + "\n", + "To test your function:\n", + "\n", + "1. Create a `State` object with the initial state of the system.\n", + "\n", + "2. Call `run_simulation` with parameters `p1=0.3`, `p2=0.2`, and `num_steps=60`." + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "rvPCwHe-FOAH" + }, + "id": "rvPCwHe-FOAH", + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "former-frost", + "metadata": { + "id": "former-frost" + }, + "outputs": [], + "source": [ + "# Solution goes here\n", + "def run_simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "spare-honduras", + "metadata": { + "id": "spare-honduras" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "instructional-finnish", + "metadata": { + "id": "instructional-finnish" + }, + "source": [ + "## Under the Hood\n", + "\n", + "This section contains additional information about the functions we've used and pointers to their documentation.\n", + "\n", + "You don't need to know anything in this section, so if you are already feeling overwhelmed, you might want to skip it.\n", + "But if you are curious, read on." + ] + }, + { + "cell_type": "markdown", + "id": "quick-citizen", + "metadata": { + "id": "quick-citizen" + }, + "source": [ + "`State` and `TimeSeries` objects are based on the `Series` object defined by the Pandas library.\n", + "The documentation is at .\n", + "\n", + "`Series` objects provide their own `plot` function, which is why we call it like this:\n", + "\n", + "```\n", + "results.plot()\n", + "```\n", + "\n", + "Instead of like this:\n", + "\n", + "```\n", + "plot(results)\n", + "```\n", + "\n", + "You can read the documentation of `Series.plot` at ." + ] + }, + { + "cell_type": "markdown", + "id": "digital-stretch", + "metadata": { + "id": "digital-stretch" + }, + "source": [ + "`decorate` is based on Matplotlib, which is a widely used plotting library for Python. Matplotlib provides separate functions for `title`, `xlabel`, and `ylabel`.\n", + "`decorate` makes them a little easier to use.\n", + "For the list of keyword arguments you can pass to `decorate`, see .\n", + "\n", + "The `flip` function uses NumPy's `random` function to generate a random number between 0 and 1, then returns `True` or `False` with the given probability.\n", + "\n", + "You can get the source code for `flip` (or any other function) by running the following cell." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "agricultural-midwest", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "agricultural-midwest", + "outputId": "42eb5674-385c-41dd-9c52-cb4097a235fe" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "def flip(p=0.5):\n", + " \"\"\"Flips a coin with the given probability.\n", + "\n", + " p: float 0-1\n", + "\n", + " returns: boolean (True or False)\n", + " \"\"\"\n", + " return np.random.random() < p\n", + "\n" + ] + } + ], + "source": [ + "source_code(flip)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "junior-lindsay", + "metadata": { + "id": "junior-lindsay" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.13" + }, + "colab": { + "provenance": [] + } }, - { - "cell_type": "markdown", - "id": "victorian-latitude", - "metadata": {}, - "source": [ - "# Bike Share System" - ] - }, - { - "cell_type": "markdown", - "id": "imported-table", - "metadata": { - "tags": [] - }, - "source": [ - "*Modeling and Simulation in Python*\n", - "\n", - "Copyright 2021 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "electoral-turkey", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# install Pint if necessary\n", - "\n", - "try:\n", - " import pint\n", - "except ImportError:\n", - " !pip install pint" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "formal-context", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# download modsim.py if necessary\n", - "\n", - "from os.path import basename, exists\n", - "\n", - "def download(url):\n", - " filename = basename(url)\n", - " if not exists(filename):\n", - " from urllib.request import urlretrieve\n", - " local, _ = urlretrieve(url, filename)\n", - " print('Downloaded ' + local)\n", - " \n", - "download('https://raw.githubusercontent.com/AllenDowney/' +\n", - " 'ModSimPy/master/modsim.py')" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "progressive-typing", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# import functions from modsim\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "id": "unlimited-antenna", - "metadata": {}, - "source": [ - "This chapter presents a simple model of a bike share system and\n", - "demonstrates the features of Python we'll use to develop simulations of real-world systems.\n", - "\n", - "Along the way, we'll make decisions about how to model the system. In\n", - "the next chapter we'll review these decisions and gradually improve the model." - ] - }, - { - "cell_type": "markdown", - "id": "electronic-radius", - "metadata": {}, - "source": [ - "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", - "Click here to access the notebooks: ." - ] - }, - { - "cell_type": "markdown", - "id": "above-denial", - "metadata": {}, - "source": [ - "## Modeling a Bike Share System\n", - "\n", - "Imagine a bike share system for students traveling between Olin College and Wellesley College, which are about three miles apart in eastern Massachusetts.\n", - "\n", - "Suppose the system contains 12 bikes and two bike racks, one at Olin and one at Wellesley, each with the capacity to hold 12 bikes.\n", - "\n", - "As students arrive, check out a bike, and ride to the other campus, the number of bikes in each location changes. In the simulation, we'll need to keep track of where the bikes are. To do that, we'll use a function called `State`, which is defined in the ModSim library." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "incorrect-comparison", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "id": "living-wayne", - "metadata": {}, - "source": [ - "The equations in parentheses create two variables, `olin` and `wellesley`, and give them the values `10` and `2`.\n", - "The `State` function stores these variables and their values in a `State` object, which gets assigned to a new variable named `bikeshare`.\n", - "\n", - "Variables stored inside a `State` object are called *state variables*.\n", - "In this example, the state variables represent the number of\n", - "bikes at each location. Their values indicate that there are 10 bikes at Olin and 2 at Wellesley. \n", - "\n", - "The `State` object is assigned to a new variable named `bikeshare`.\n", - "We can get the value of a variable in a `State` object using the *dot operator*, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "brief-diversity", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin" - ] - }, - { - "cell_type": "markdown", - "id": "intermediate-midwest", - "metadata": {}, - "source": [ - "And this:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "designed-brazilian", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.wellesley" - ] - }, - { - "cell_type": "markdown", - "id": "phantom-oklahoma", - "metadata": {}, - "source": [ - "Or, to display all of the state variables and their values, you can enter just the name of the object:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "impaired-potter", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "id": "vital-journal", - "metadata": {}, - "source": [ - "These values make up the *state* of the system." - ] - }, - { - "cell_type": "markdown", - "id": "fleet-beijing", - "metadata": { - "tags": [] - }, - "source": [ - "The ModSim library provides a function called `show` that displays a `State` object as a table." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "basic-fabric", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "show(bikeshare)" - ] - }, - { - "cell_type": "markdown", - "id": "specified-definition", - "metadata": { - "tags": [] - }, - "source": [ - "You don't have to use `show`, but I think the results look better." - ] - }, - { - "cell_type": "markdown", - "id": "delayed-ocean", - "metadata": {}, - "source": [ - "We can update the state by assigning new values to the variables. \n", - "For example, if a student moves a bike from Olin to Wellesley, we can figure out the new values and assign them:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "floppy-trainer", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin = 9\n", - "bikeshare.wellesley = 3" - ] - }, - { - "cell_type": "markdown", - "id": "natural-gossip", - "metadata": {}, - "source": [ - "Or we can use *update operators*, `-=` and `+=`, to subtract 1 from\n", - "`olin` and add 1 to `wellesley`:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "hungarian-bride", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin -= 1\n", - "bikeshare.wellesley += 1" - ] - }, - { - "cell_type": "markdown", - "id": "radical-mills", - "metadata": {}, - "source": [ - "The result is the same either way." - ] - }, - { - "cell_type": "markdown", - "id": "controversial-opportunity", - "metadata": {}, - "source": [ - "## Defining Functions\n", - "\n", - "So far we have used functions defined in NumPy and the ModSim library. Now we're going to define our own functions.\n", - "\n", - "When you are developing code in Jupyter, it is often efficient to write a few lines of code, test them to confirm they do what you intend, and then use them to define a new function. For example, these lines move a bike from Olin to Wellesley:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "vertical-drawing", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin -= 1\n", - "bikeshare.wellesley += 1" - ] - }, - { - "cell_type": "markdown", - "id": "approximate-rolling", - "metadata": {}, - "source": [ - "Rather than repeat them every time a bike moves, we can define a new\n", - "function:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "significant-nutrition", - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley():\n", - " bikeshare.olin -= 1\n", - " bikeshare.wellesley += 1" - ] - }, - { - "cell_type": "markdown", - "id": "generous-tracker", - "metadata": {}, - "source": [ - "`def` is a special word in Python that indicates we are defining a new\n", - "function. The name of the function is `bike_to_wellesley`. The empty\n", - "parentheses indicate that this function requires no additional\n", - "information when it runs. The colon indicates the beginning of an\n", - "indented *code block*.\n", - "\n", - "The next two lines are the *body* of the function. They have to be\n", - "indented; by convention, the indentation is four spaces.\n", - "\n", - "When you define a function, it has no immediate effect. The body of the\n", - "function doesn't run until you *call* the function. Here's how to call\n", - "this function:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "moving-jurisdiction", - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_wellesley()" - ] - }, - { - "cell_type": "markdown", - "id": "meaningful-christmas", - "metadata": {}, - "source": [ - "When you call the function, it runs the statements in the body, which\n", - "update the variables of the `bikeshare` object; you can check by\n", - "displaying the new state." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "proper-symposium", - "metadata": {}, - "outputs": [], - "source": [ - "show(bikeshare)" - ] - }, - { - "cell_type": "markdown", - "id": "eleven-brook", - "metadata": {}, - "source": [ - "When you call a function, you have to include the parentheses. If you\n", - "leave them out, you get this:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "identical-yacht", - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_wellesley" - ] - }, - { - "cell_type": "markdown", - "id": "premier-youth", - "metadata": {}, - "source": [ - "This result indicates that `bike_to_wellesley` is a function. You don't have to know what `__main__` means, but if you see something like this, it probably means that you named a function but didn't actually call it.\n", - "So don't forget the parentheses." - ] - }, - { - "cell_type": "markdown", - "id": "brazilian-medicare", - "metadata": {}, - "source": [ - "## Print Statements\n", - "\n", - "As you write more complicated programs, it is easy to lose track of what\n", - "is going on. One of the most useful tools for debugging is the *print statement*, which displays text in the Jupyter notebook.\n", - "\n", - "Normally when Jupyter runs the code in a cell, it displays the value of\n", - "the last line of code. For example, if you run:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "heavy-patrol", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin\n", - "bikeshare.wellesley" - ] - }, - { - "cell_type": "markdown", - "id": "ancient-projection", - "metadata": {}, - "source": [ - "Jupyter runs both lines, but it only displays the value of the\n", - "second. If you want to display more than one value, you can use\n", - "print statements:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "french-preference", - "metadata": {}, - "outputs": [], - "source": [ - "print(bikeshare.olin)\n", - "print(bikeshare.wellesley)" - ] - }, - { - "cell_type": "markdown", - "id": "original-hollywood", - "metadata": {}, - "source": [ - "When you call the `print` function, you can put a variable in\n", - "parentheses, as in the previous example, or you can provide a sequence\n", - "of variables separated by commas, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "alternative-keyboard", - "metadata": {}, - "outputs": [], - "source": [ - "print(bikeshare.olin, bikeshare.wellesley)" - ] - }, - { - "cell_type": "markdown", - "id": "described-produce", - "metadata": {}, - "source": [ - "Python looks up the values of the variables and displays them; in this\n", - "example, it displays two values on the same line, with a space between\n", - "them.\n", - "\n", - "Print statements are useful for debugging functions. For example, we can\n", - "add a print statement to `bike_to_wellesley`, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "robust-holly", - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley():\n", - " print('Moving a bike to Wellesley')\n", - " bikeshare.olin -= 1\n", - " bikeshare.wellesley += 1" - ] - }, - { - "cell_type": "markdown", - "id": "vital-lender", - "metadata": {}, - "source": [ - "Each time we call this version of the function, it displays a message,\n", - "which can help us keep track of what the program is doing.\n", - "The message in this example is a *string*, which is a sequence of\n", - "letters and other symbols in quotes.\n", - "\n", - "Just like `bike_to_wellesley`, we can define a function that moves a\n", - "bike from Wellesley to Olin:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "fifteen-atmosphere", - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_olin():\n", - " print('Moving a bike to Olin')\n", - " bikeshare.wellesley -= 1\n", - " bikeshare.olin += 1" - ] - }, - { - "cell_type": "markdown", - "id": "requested-glasgow", - "metadata": {}, - "source": [ - "And call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "matched-narrow", - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_olin()" - ] - }, - { - "cell_type": "markdown", - "id": "sitting-semiconductor", - "metadata": {}, - "source": [ - "One benefit of defining functions is that you avoid repeating chunks of\n", - "code, which makes programs smaller. Another benefit is that the name you\n", - "give the function documents what it does, which makes programs more\n", - "readable." - ] - }, - { - "cell_type": "markdown", - "id": "enhanced-maintenance", - "metadata": {}, - "source": [ - "## If Statements\n", - "\n", - "At this point we have functions that simulate moving bikes; now let's think about simulating customers. As a simple model of customer behavior, I will use a random number generator to determine when customers arrive at each station.\n", - "\n", - "The ModSim library provides a function called `flip` that generates random \"coin tosses\".\n", - "When you call it, you provide a probability between 0 and 1, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "29c1f41a", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# this line sets the random number generator so the results in\n", - "# the book are the same every time we run it\n", - "np.random.seed(17)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "illegal-metropolitan", - "metadata": {}, - "outputs": [], - "source": [ - "flip(0.7)" - ] - }, - { - "cell_type": "markdown", - "id": "appropriate-funds", - "metadata": {}, - "source": [ - "The result is one of two values: `True` with probability 0.7 (in this example) or `False`\n", - "with probability 0.3. If you run `flip` like this 100 times, you should\n", - "get `True` about 70 times and `False` about 30 times. But the results\n", - "are random, so they might differ from these expectations.\n", - "\n", - "`True` and `False` are special values defined by Python. \n", - "They are called *boolean* values because they are\n", - "related to Boolean algebra ().\n", - "\n", - "Note that they are not strings. There is a difference between `True`, which is a boolean value, and `'True'`, which is a string.\n", - "\n", - "We can use boolean values to control the behavior of the program, using an *if statement*:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "excessive-murder", - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.5):\n", - " print('heads')" - ] - }, - { - "cell_type": "markdown", - "id": "seventh-profile", - "metadata": {}, - "source": [ - "If the result from `flip` is `True`, the program displays the string\n", - "`'heads'`. Otherwise it does nothing.\n", - "\n", - "The syntax for `if` statements is similar to the syntax for\n", - "function definitions: the first line has to end with a colon, and the\n", - "lines inside the `if` statement have to be indented.\n", - "\n", - "Optionally, you can add an *else clause* to indicate what should\n", - "happen if the result is `False`:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "fundamental-nursing", - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.5):\n", - " print('heads')\n", - "else:\n", - " print('tails') " - ] - }, - { - "cell_type": "markdown", - "id": "recovered-chemical", - "metadata": {}, - "source": [ - "If you run the previous cell a few times, it should print `heads` about half the time, and `tails` about half the time.\n", - "\n", - "Now we can use `flip` to simulate the arrival of customers who want to\n", - "borrow a bike. Suppose students arrive at the Olin station every two\n", - "minutes on average.\n", - "In that case, the chance of an arrival during any one-minute period is 50%, and we can simulate it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "twenty-health", - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.5):\n", - " bike_to_wellesley()" - ] - }, - { - "cell_type": "markdown", - "id": "difficult-construction", - "metadata": {}, - "source": [ - "If students arrive at the Wellesley station every three minutes, on average,\n", - "the chance of an arrival during any one-minute period is 33%, and we can\n", - "simulate it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "played-character", - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.33):\n", - " bike_to_olin()" - ] - }, - { - "cell_type": "markdown", - "id": "standard-party", - "metadata": {}, - "source": [ - "We can combine these snippets into a function that simulates a *time step*, which is an interval of time, in this case one minute:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "ecological-colon", - "metadata": {}, - "outputs": [], - "source": [ - "def step():\n", - " if flip(0.5):\n", - " bike_to_wellesley()\n", - " \n", - " if flip(0.33):\n", - " bike_to_olin()" - ] - }, - { - "cell_type": "markdown", - "id": "amateur-exposure", - "metadata": {}, - "source": [ - "Then we can simulate a time step like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "id": "mediterranean-german", - "metadata": {}, - "outputs": [], - "source": [ - "step()" - ] - }, - { - "cell_type": "markdown", - "id": "sought-mobile", - "metadata": {}, - "source": [ - "Depending on the results from `flip`, this function might move a bike to Olin, or to Wellesley, or neither, or both." - ] - }, - { - "cell_type": "markdown", - "id": "organic-proportion", - "metadata": {}, - "source": [ - "## Parameters\n", - "\n", - "The previous version of `step` is fine if the arrival probabilities\n", - "never change, but in reality they vary over time.\n", - "\n", - "So instead of putting the constant values 0.5 and 0.33 in `step`, we can replace them with *parameters*.\n", - "Parameters are variables whose values are set when a function is called.\n", - "\n", - "Here's a version of `step` that takes two parameters, `p1` and `p2`:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "hollywood-shopping", - "metadata": {}, - "outputs": [], - "source": [ - "def step(p1, p2):\n", - " if flip(p1):\n", - " bike_to_wellesley()\n", - " \n", - " if flip(p2):\n", - " bike_to_olin()" - ] - }, - { - "cell_type": "markdown", - "id": "encouraging-arkansas", - "metadata": {}, - "source": [ - "The values of `p1` and `p2` are not set inside this function; instead,\n", - "they are provided when the function is called, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "buried-alert", - "metadata": {}, - "outputs": [], - "source": [ - "step(0.5, 0.33)" - ] - }, - { - "cell_type": "markdown", - "id": "aggregate-dynamics", - "metadata": {}, - "source": [ - "The values you provide when you call the function are called\n", - "*arguments*. The arguments, `0.5` and `0.33` in this example, get\n", - "assigned to the parameters, `p1` and `p2`, in order. So running this\n", - "function has the same effect as:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "recognized-denmark", - "metadata": {}, - "outputs": [], - "source": [ - "p1 = 0.5\n", - "p2 = 0.33\n", - "\n", - "if flip(p1):\n", - " bike_to_wellesley()\n", - " \n", - "if flip(p2):\n", - " bike_to_olin()" - ] - }, - { - "cell_type": "markdown", - "id": "raised-museum", - "metadata": {}, - "source": [ - "The advantage of using parameters is that you can call the same function many times, providing different arguments each time.\n", - "\n", - "Adding parameters to a function is called *generalization*, because it makes the function more general; without parameters, the function always does the same thing; with parameters, it can do a range of things." - ] - }, - { - "cell_type": "markdown", - "id": "scenic-african", - "metadata": {}, - "source": [ - "## For Loops\n", - "\n", - "At some point you will get sick of running cells over and over.\n", - "Fortunately, there is an easy way to repeat a chunk of code, the *for loop*. Here's an example:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "polish-river", - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(3):\n", - " print(i)\n", - " bike_to_wellesley()" - ] - }, - { - "cell_type": "markdown", - "id": "compatible-conspiracy", - "metadata": {}, - "source": [ - "The syntax here should look familiar; the first line ends with a\n", - "colon, and the lines inside the `for` loop are indented. The other\n", - "elements of the loop are:\n", - "\n", - "- The words `for` and `in` are special words we have to use in a for\n", - " loop.\n", - "\n", - "- `range` is a Python function we use to control the number of times the loop runs.\n", - "\n", - "- `i` is a *loop variable* that gets created when the for loop runs.\n", - "\n", - "When this loop runs, it runs the statements inside the loop three times. The first time, the value of `i` is `0`; the second time, it is `1`; the third time, it is `2`.\n", - "\n", - "Each time through the loop, it prints the value of `i` and moves one bike to Wellesley." - ] - }, - { - "cell_type": "markdown", - "id": "breeding-groove", - "metadata": {}, - "source": [ - "## TimeSeries\n", - "\n", - "When we run a simulation, we often want to save the results for later analysis. The ModSim library provides a `TimeSeries` object for this purpose. A `TimeSeries` contains a sequence of timestamps and a\n", - "corresponding sequence of quantities.\n", - "\n", - "In this example, the timestamps are integers representing minutes and the quantities are the number of bikes at one location.\n", - "\n", - "Since we have moved a number of bikes around, let's start again with a new `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "every-consultation", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "id": "cross-sharp", - "metadata": {}, - "source": [ - "We can create a new, empty `TimeSeries` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "changing-planet", - "metadata": {}, - "outputs": [], - "source": [ - "results = TimeSeries()" - ] - }, - { - "cell_type": "markdown", - "id": "attractive-revision", - "metadata": {}, - "source": [ - "And we can add a quantity like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "aquatic-richardson", - "metadata": {}, - "outputs": [], - "source": [ - "results[0] = bikeshare.olin" - ] - }, - { - "cell_type": "markdown", - "id": "searching-funeral", - "metadata": {}, - "source": [ - "The number in brackets is the timestamp, also called a *label*.\n", - "\n", - "We can use a `TimeSeries` inside a for loop to store the results of the simulation:" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "english-titanium", - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(3):\n", - " print(i)\n", - " step(0.6, 0.6)\n", - " results[i+1] = bikeshare.olin" - ] - }, - { - "cell_type": "markdown", - "id": "prospective-joining", - "metadata": {}, - "source": [ - "Each time through the loop, we print the value of `i` and call `step`, which updates `bikeshare`.\n", - "Then we store the number of bikes at Olin in `results`. \n", - "We use the loop variable, `i`, to compute the timestamp, `i+1`.\n", - "\n", - "The first time through the loop, the value of `i` is `0`, so the timestamp is `1`.\n", - "The last time, the value of `i` is `2`, so the timestamp is `3`.\n", - "\n", - "When the loop exits, `results` contains 4 timestamps, from 0 through\n", - "3, and the number of bikes at Olin at the end of each time step.\n", - "\n", - "We can display the `TimeSeries` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "id": "indonesian-singing", - "metadata": {}, - "outputs": [], - "source": [ - "show(results)" - ] - }, - { - "cell_type": "markdown", - "id": "small-encoding", - "metadata": {}, - "source": [ - "The left column is the timestamps; the right column is the quantities." - ] - }, - { - "cell_type": "markdown", - "id": "following-contrary", - "metadata": {}, - "source": [ - "## Plotting\n", - "\n", - "`results` provides a function called `plot` we can use to plot\n", - "the results, and the ModSim library provides `decorate`, which we can use to label the axes and give the figure a title:" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "saved-hands", - "metadata": {}, - "outputs": [], - "source": [ - "results.plot()\n", - "\n", - "decorate(title='Olin-Wellesley bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')" - ] - }, - { - "cell_type": "markdown", - "id": "egyptian-korea", - "metadata": {}, - "source": [ - "The result should be a plot with time on the $x$-axis and the number of bikes on the $y$-axis.\n", - "Since we only ran three time steps, it might not be very interesting." - ] - }, - { - "cell_type": "markdown", - "id": "limited-interstate", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "This chapter introduces the tools we need to run simulations, record the results, and plot them.\n", - "\n", - "We used a `State` object to represent the state of the system.\n", - "Then we used the `flip` function and an `if` statement to simulate a single time step.\n", - "We used a `for` loop to simulate a series of steps, and a `TimeSeries` to record the results.\n", - "Finally, we used `plot` and `decorate` to plot the results.\n", - "\n", - "In the next chapter, we will extend this simulation to make it a little more realistic." - ] - }, - { - "cell_type": "markdown", - "id": "fallen-surprise", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "Before you go on, you might want to work on the following exercises." - ] - }, - { - "cell_type": "markdown", - "id": "capital-internship", - "metadata": {}, - "source": [ - "### Exercise 1\n", - "\n", - "What happens if you spell the name of a state variable wrong? Edit the following cell, change the spelling of `wellesley`, and run it.\n", - "\n", - "The error message uses the word *attribute*, which is another name for what we are calling a state variable. " - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "helpful-zambia", - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)\n", - "\n", - "bikeshare.wellesley" - ] - }, - { - "cell_type": "markdown", - "id": "dirty-multiple", - "metadata": {}, - "source": [ - "### Exercise 2\n", - "\n", - "Make a `State` object with a third state variable, called `downtown`, with initial value 0, and display the state of the system." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "beneficial-mainland", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "christian-madrid", - "metadata": {}, - "source": [ - "### Exercise 3\n", - "\n", - "Wrap the code in the chapter in a function named `run_simulation` that takes three parameters, named `p1`, `p2`, and `num_steps`.\n", - "\n", - "It should:\n", - "\n", - "1. Create a `TimeSeries` object to hold the results.\n", - "\n", - "2. Use a for loop to run `step` the number of times specified by `num_steps`, passing along the specified values of `p1` and `p2`.\n", - "\n", - "3. After each step, it should save the number of bikes at Olin in the `TimeSeries`.\n", - "\n", - "4. After the for loop, it should plot the results and\n", - "\n", - "5. Decorate the axes.\n", - "\n", - "To test your function:\n", - "\n", - "1. Create a `State` object with the initial state of the system.\n", - "\n", - "2. Call `run_simulation` with parameters `p1=0.3`, `p2=0.2`, and `num_steps=60`." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "id": "former-frost", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "spare-honduras", - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "id": "instructional-finnish", - "metadata": {}, - "source": [ - "## Under the Hood\n", - "\n", - "This section contains additional information about the functions we've used and pointers to their documentation.\n", - "\n", - "You don't need to know anything in this section, so if you are already feeling overwhelmed, you might want to skip it.\n", - "But if you are curious, read on." - ] - }, - { - "cell_type": "markdown", - "id": "quick-citizen", - "metadata": {}, - "source": [ - "`State` and `TimeSeries` objects are based on the `Series` object defined by the Pandas library.\n", - "The documentation is at .\n", - "\n", - "`Series` objects provide their own `plot` function, which is why we call it like this:\n", - "\n", - "```\n", - "results.plot()\n", - "```\n", - "\n", - "Instead of like this:\n", - "\n", - "```\n", - "plot(results)\n", - "```\n", - "\n", - "You can read the documentation of `Series.plot` at ." - ] - }, - { - "cell_type": "markdown", - "id": "digital-stretch", - "metadata": {}, - "source": [ - "`decorate` is based on Matplotlib, which is a widely used plotting library for Python. Matplotlib provides separate functions for `title`, `xlabel`, and `ylabel`.\n", - "`decorate` makes them a little easier to use.\n", - "For the list of keyword arguments you can pass to `decorate`, see .\n", - "\n", - "The `flip` function uses NumPy's `random` function to generate a random number between 0 and 1, then returns `True` or `False` with the given probability.\n", - "\n", - "You can get the source code for `flip` (or any other function) by running the following cell." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "id": "agricultural-midwest", - "metadata": {}, - "outputs": [], - "source": [ - "source_code(flip)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "junior-lindsay", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.10.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file