You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: book/book.tex
+33-17Lines changed: 33 additions & 17 deletions
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@
13
13
\newcommand{\thetitle}{Modeling and Simulation in Python}
14
14
\newcommand{\thesubtitle}{}
15
15
\newcommand{\theauthors}{Allen B. Downey}
16
-
\newcommand{\theversion}{1.0.1}
16
+
\newcommand{\theversion}{1.0.2}
17
17
18
18
19
19
%%%% Both LATEX and PLASTEX
@@ -2293,7 +2293,7 @@ \section{Disfunctions}
2293
2293
2294
2294
When people first learn about functions, there are a few things they often find confusing. In this section I present and explain some common problems with functions.
2295
2295
2296
-
As an example, suppose you want a function that takes a \py{System} object with variables \py{alpha} and \py{beta} as a parameter and computes the carrying capacity, \py{-alpha/beta}. Here's a good solution:
2296
+
As an example, suppose you want a function that takes a \py{System} object, with variables \py{alpha} and \py{beta}, as a parameter and computes the carrying capacity, \py{-alpha/beta}. Here's a good solution:
2297
2297
2298
2298
\begin{python}
2299
2299
def carrying_capacity(system):
@@ -2310,7 +2310,6 @@ \section{Disfunctions}
2310
2310
Disfunction \#1: Not using parameters. In the following version, the function doesn't take any parameters; when \py{system} appears inside the function, it refers to the object we created outside the function.
2311
2311
2312
2312
\begin{python}
2313
-
# WRONG
2314
2313
def carrying_capacity():
2315
2314
K = -system.alpha / system.beta
2316
2315
return K
@@ -2320,7 +2319,7 @@ \section{Disfunctions}
2320
2319
print(pop)
2321
2320
\end{python}
2322
2321
2323
-
This version actually works, but it is not very versatile. If there are several \py{System} object, this function can only work with one of them; that is, as long as one of them is named \py{System}.
2322
+
This version actually works, but it is not are versatile as it could be. If there are several \py{System} objects, this function can only work with one of them, and only if it is named \py{system}.
2324
2323
2325
2324
Disfunction \#2: Clobbering the parameters. When people first learn about parameters, they often write functions like this:
2326
2325
@@ -2331,12 +2330,12 @@ \section{Disfunctions}
2331
2330
K = -system.alpha / system.beta
2332
2331
return K
2333
2332
2334
-
sys1 = System(alpha=0.025, beta=-0.0018)
2333
+
sys1 = System(alpha=0.03, beta=-0.002)
2335
2334
pop = carrying_capacity(sys1)
2336
2335
print(pop)
2337
2336
\end{python}
2338
2337
2339
-
In this example, we have a \py{System} object named \py{sys1} that gets passed as an argument to \py{carrying_capacity}. But when the function run, it ignores the argument and immediately replaces it with a new \py{System} object. As a result, this function always returns the same value, no matter what argument is passed.
2338
+
In this example, we have a \py{System} object named \py{sys1} that gets passed as an argument to \py{carrying_capacity}. But when the function runs, it ignores the argument and immediately replaces it with a new \py{System} object. As a result, this function always returns the same value, no matter what argument is passed.
2340
2339
2341
2340
When you write a function, you generally don't know what the values of the parameters will be. Your job is to write a function that works for any valid values. If you assign your own values to the parameters, you defeat the whole purpose of functions.
2342
2341
@@ -2353,7 +2352,7 @@ \section{Disfunctions}
2353
2352
print(pop)
2354
2353
\end{python}
2355
2354
2356
-
A function that doesn't have a return statement always returns \py{None}, so in this example the value of \py{pop} is \py{None}.
2355
+
A function that doesn't have a return statement always returns \py{None}, so in this example the value of \py{pop} is \py{None}. If you are debugging a program and find that the value of a variable is \py{None}, when it shouldn't be, a function without a return statement is a likely cause.
2357
2356
2358
2357
2359
2358
Disfunction \#4: Ignoring the return value. Finally, here's a version where the function is correct, but the way it's used is not.
@@ -2369,7 +2368,9 @@ \section{Disfunctions}
2369
2368
print(K)
2370
2369
\end{python}
2371
2370
2372
-
If you call a function that returns a value, you should do something with the return value. Often you assign it to a variable, as in the previous examples, but you can also use it as part of an expression. For example, you could eliminate the temporary variable \py{pop} like this:
2371
+
In this example, \py{carrying_capacity} runs and returns \py{K}, but the return value is dropped.
2372
+
2373
+
When you call a function that returns a value, you should do something with the result. Often you assign it to a variable, as in the previous examples, but you can also use it as part of an expression. For example, you could eliminate the temporary variable \py{pop} like this:
I am using the word ``projection" deliberately, rather than ``prediction", with the following distinction: ``prediction" implies something like ``this is what we should reasonably expect to happen, at least approximately"; ``projection" implies something like ``if this model is actually a good description of what is happening in this system, and if nothing in the future causes the parameters of the model to change, this is what would happen."
2449
2450
2450
-
Using ``projection" leaves open the possibility that there are important things in the real world that are not captured in the model, and even if the model is good, that the parameters might change.
2451
+
Using ``projection" leaves open the possibility that there are important things in the real world that are not captured in the model. It also suggests that, even if the model is good, the parameters we estimate based on the past might be different in the future.
2451
2452
2452
2453
The quadratic model we've been working with is based on the assumption that population growth is limited by the availability of resources; in that scenario, as the population approaches carrying capacity, birth rates fall and death rates rise because resources become scarce.
2453
2454
\index{carrying capacity}
@@ -3003,7 +3004,7 @@ \section{Implementation}
3003
3004
gamma = 1 / tr # recovery rate in per day
3004
3005
\end{python}
3005
3006
3006
-
Now we need a \py{System} object to store the parameters and initial conditions. The following function takes the model parameters as function parameters and returns a new \py{System} object:
3007
+
Now we need a \py{System} object to store the parameters and initial conditions. The following function takes the system parameters as function parameters and returns a new \py{System} object:
3007
3008
\index{\py{make_system}}
3008
3009
3009
3010
\begin{python}
@@ -3080,7 +3081,7 @@ \section{Running the simulation}
3080
3081
return state
3081
3082
\end{python}
3082
3083
3083
-
The parameters of \py{run_simulation} are the \py{System} object and the update function. The \py{System} object contains the model parameters, initial conditions, and values of \py{t0} and \py{t_end}.
3084
+
The parameters of \py{run_simulation} are the \py{System} object and the update function. The \py{System} object contains the parameters, initial conditions, and values of \py{t0} and \py{t_end}.
3084
3085
\index{\py{run_simulation}}
3085
3086
3086
3087
The outline of this function should look familiar; it is similar to the function we used for the population model in Section~\ref{nowwithsystem}.
@@ -3124,7 +3125,7 @@ \section{Collecting the results}
3124
3125
S[t0], I[t0], R[t0] = state
3125
3126
3126
3127
for t in linrange(system.t0, system.t_end):
3127
-
state = update_func(system, state)
3128
+
state = update_func(state, system)
3128
3129
S[i+1], I[i+1], R[i+1] = state
3129
3130
3130
3131
system.S = S
@@ -3193,7 +3194,7 @@ \section{Now with a TimeFrame}
I think that makes the code easier to read. In the notebook for this chapter, you can use \py{unpack} to clean up \py{update1}.
3542
+
The variables you unpack should be treated as read-only. Modifying them is not an error, but it might not have the behavior you expect. In the notebook for this chapter, you can use \py{unpack} to clean up \py{update1}.
Describing physical systems using dimensionless parameters is often a useful move in the modeling and simulation game. It is so useful, in fact, that it has a name: nondimensionalization (see \url{http://modsimpy.com/nondim}).
3717
+
Describing physical systems using dimensionless parameters is often a useful move in the modeling and simulation game. It is so useful, in fact, that it has a name: {\bfnondimensionalization} (see \url{http://modsimpy.com/nondim}).
3717
3718
\index{nondimensionalization}
3718
3719
3719
3720
So we'll try the second option first. In the notebook for this chapter, you can explore the first option as an exercise.
@@ -4304,7 +4305,7 @@ \section{Analysis}
4304
4305
4305
4306
Now we can use the observed data to estimate the parameter $r$. If we observe $T(t_{end}) = T_{end}$, we can plug $t_{end}$ and $T_{end}$ into the particular solution and solve for $r$. The result is:
When $dt$ is very small, or more precisely {\bf infinitesimal}, this equation is exact. But in our simulations, $dt$ is \SI{2}{\minute}, which is small but not infinitesimal. In effect, the simulations assume that the derivatives $dG/dt$ and $dX/dt$ are constant during each \SI{2}{\minute} time step. That's not exactly true, but it can be a good enough approximation.
4640
+
When $dt$ is very small, or more precisely {\bf infinitesimal}, this equation is exact. But in our simulations, $dt$ is \SI{2}{\minute}, which is small but not infinitesimal. In effect, the simulations assume that the derivatives $dG/dt$ and $dX/dt$ are constant during each \SI{2}{\minute} time step. That's not exactly true, but it can be a good enough approximation.
4640
4641
\index{time step}
4641
4642
4642
4643
This method, evaluating derivatives at discrete time steps and assuming that they are constant in between, is called {\bf Euler's method} (see \url{http://modsimpy.com/euler}).
@@ -6347,6 +6348,21 @@ \section{Simulating a yo-yo}
6347
6348
At this point you have everything you need to simulate the descent of a yo-yo. In the notebook for this chapter you will have a chance to finish off the exercise.
0 commit comments