@@ -29,18 +29,21 @@ premature optimization is the root of all evil." -- Donald Knuth
29293030Python is extremely popular for scientific computing, due to such factors as
313132-* the accessible and flexible nature of the language itself,
33-* the huge range of high quality scientific libraries now available,
32+* the accessible and expressive nature of the language itself,
33+* its vast range of high quality scientific libraries,
3434* the fact that the language and libraries are open source,
35-* the popular Anaconda Python distribution, which simplifies installation and
36- management of those libraries, and
37-* the recent surge of interest in using Python for machine learning and
38- artificial intelligence.
35+* the popular [Anaconda Python distribution](https://www.anaconda.com/download), which simplifies installation and management of scientific libraries, and
36+* the key role that Python plays in data science, machine learning and artificial intelligence.
393740-In this lecture we give a short overview of scientific computing in Python,
41-addressing the following questions:
38+In previous lectures, we looked at some scientific Python libaries such as NumPy and Matplotlib.
423943-* What are the relative strengths and weaknesses of Python for these tasks?
40+However, our main focus was the core Python language, rather than the libraries.
41+42+Now we turn to the scientific libraries and give them our full attention.
43+44+We'll also discuss the following topics:
45+46+* What are the relative strengths and weaknesses of Python for scientific work?
4447* What are the main elements of the scientific Python ecosystem?
4548* How is the situation changing over time?
4649@@ -53,62 +56,67 @@ tags: [hide-output]
5356!pip install quantecon
5457```
555859+60+5661## Scientific Libraries
576258-Let's briefly review Python's scientific libraries, starting with why we need
59-them.
63+Let's briefly review Python's scientific libraries, starting with why we need them.
60646165### The Role of Scientific Libraries
626663-One obvious reason we use scientific libraries is because they implement
64-routines we want to use.
67+One reason we use scientific libraries is because they implement routines we want to use.
68+69+* numerical integration, interpolation, linear algebra, root finding, etc.
657066-For example, it's almost always better to use an existing routine for root
67-finding than to write a new one from scratch.
71+For example, it's almost always better to use an existing routine for root finding than to write a new one from scratch.
687269-(For standard algorithms, efficiency is maximized if the community can coordinate on a
70-common set of implementations, written by experts and tuned by users to be as fast and robust as possible.)
73+(For standard algorithms, efficiency is maximized if the community can coordinate on a common set of implementations, written by experts and tuned by users to be as fast and robust as possible.)
71747275But this is not the only reason that we use Python's scientific libraries.
73767477Another is that pure Python, while flexible and elegant, is not fast.
75787679So we need libraries that are designed to accelerate execution of Python code.
778078-As we'll see below, there are now Python libraries that can do this extremely well.
81+They do this using two strategies:
798280-### Python's Scientific Ecosystem
83+1. using compilers that convert Python-like statements into fast machine code for individual threads of logic and
84+2. parallelizing tasks across multiple "workers" (e.g., CPUs, individual threads inside GPUs).
818582-In terms of popularity, the big four in the world of scientific Python
83-libraries are
86+There are several Python libraries that can do this extremely well.
848785-* NumPy
86-* SciPy
87-* Matplotlib
88-* Pandas
898890-For us, there's another (relatively new) library that will also be essential for
91-numerical computing:
89+### Python's Scientific Ecosystem
929093-* Numba
91+At QuantEcon, the scientific libraries we use most often are
949295-Over the next few lectures we'll see how to use these libraries.
93+* [NumPy](https://numpy.org/)
94+* [SciPy](https://scipy.org/)
95+* [Matplotlib](https://matplotlib.org/)
96+* [Pandas](https://pandas.pydata.org/)
97+* [Numba](https://numba.pydata.org/) and
98+* [JAX](https://github.com/jax-ml/jax)
969997-But first, let's quickly review how they fit together.
100+Here's how they fit together:
9810199-* NumPy forms the foundations by providing a basic array data type (think of
102+* NumPy forms foundations by providing a basic array data type (think of
100103 vectors and matrices) and functions for acting on these arrays (e.g., matrix
101104 multiplication).
102-* SciPy builds on NumPy by adding the kinds of numerical methods that are
103- routinely used in science (interpolation, optimization, root finding, etc.).
105+* SciPy builds on NumPy by adding numerical methods routinely used in science (interpolation, optimization, root finding, etc.).
104106* Matplotlib is used to generate figures, with a focus on plotting data stored in NumPy arrays.
105-* Pandas provides types and functions for empirical work (e.g., manipulating data).
106-* Numba accelerates execution via JIT compilation --- we'll learn about this
107- soon.
107+* Pandas provides types and functions for manipulating data.
108+* Numba provides a just-in-time compiler that integrates well with NumPy and
109+ helps accelerate Python code.
110+* JAX includes array processing operations similar to NumPy, automatic
111+ differentiation, a parallelization-centric just-in-time compiler, and automated integration with hardware accelerators such as
112+ GPUs.
113+114+115+108116109117## The Need for Speed
110118111-Now let's discuss execution speed.
119+Let's discuss execution speed and how scientific libraries can help us accelerate code.
112120113121Higher-level languages like Python are optimized for humans.
114122@@ -117,35 +125,38 @@ This means that the programmer can leave many details to the runtime environment
117125* specifying variable types
118126* memory allocation/deallocation, etc.
119127120-The upside is that, compared to low-level languages, Python is typically faster to write, less error-prone and easier to debug.
128+On one hand, compared to low-level languages, high-level languages are typically faster to write, less error-prone and easier to debug.
121129122-The downside is that Python is harder to optimize --- that is, turn into fast machine code --- than languages like C or Fortran.
130+On the other hand, high-level languages are harder to optimize --- that is, to turn into fast machine code --- than languages like C or Fortran.
123131124132Indeed, the standard implementation of Python (called CPython) cannot match the speed of compiled languages such as C or Fortran.
125133126134Does that mean that we should just switch to C or Fortran for everything?
127135128-The answer is: No, no and one hundred times no!
136+The answer is: No, no, and one hundred times no!
129137130-(This is what you should say to the senior professor insisting that the model
131-needs to be rewritten in Fortran or C++.)
138+(This is what you should say to your professor when they insist that your model needs to be rewritten in Fortran or C++.)
132139133140There are two reasons why:
134141135-First, for any given program, relatively few lines are ever going to
136-be time-critical.
142+First, for any given program, relatively few lines are ever going to be time-critical.
137143138144Hence it is far more efficient to write most of our code in a high productivity language like Python.
139145140146Second, even for those lines of code that *are* time-critical, we can now achieve the same speed as C or Fortran using Python's scientific libraries.
141147148+In fact we can often do better, because some scientific libraries are so
149+effective at accelerating and parallelizing our code.
150+151+142152### Where are the Bottlenecks?
143153144-Before we learn how to do this, let's try to understand why plain vanilla
145-Python is slower than C or Fortran.
154+Before we learn how to do this, let's try to understand why plain vanilla Python is slower than C or Fortran.
146155147156This will, in turn, help us figure out how to speed things up.
148157158+In reading the following, remember that the Python interpreter executes code line-by-line.
159+149160#### Dynamic Typing
150161151162```{index} single: Dynamic Typing
@@ -180,10 +191,11 @@ a + b
180191(We say that the operator `+` is *overloaded* --- its action depends on the
181192type of the objects on which it acts)
182193183-As a result, Python must check the type of the objects and then call the correct operation.
194+As a result, when executing `a + b`, Python must first check the type of the objects and then call the correct operation.
184195185196This involves substantial overheads.
186197198+187199#### Static Types
188200189201```{index} single: Static Types
@@ -255,6 +267,9 @@ In fact, it's generally true that memory traffic is a major culprit when it come
255267256268Let's look at some ways around these problems.
257269270+271+272+258273## {index}`Vectorization <single: Vectorization>`
259274260275```{index} single: Python; Vectorization
@@ -272,173 +287,12 @@ For example, when working in a high level language, the operation of inverting a
272287273288This clever idea dates back to MATLAB, which uses vectorization extensively.
274289275-Vectorization can greatly accelerate many numerical computations (but not all,
276-as we shall see).
277-278-Let's see how vectorization works in Python, using NumPy.
279-280-### Operations on Arrays
281-282-```{index} single: Vectorization; Operations on Arrays
283-```
284-285-First, let's run some imports
286-287-```{code-cell} python3
288-import random
289-import numpy as np
290-import quantecon as qe
291-```
292-293-Next let's try some non-vectorized code, which uses a native Python loop to generate,
294-square and then sum a large number of random variables:
295-296-```{code-cell} python3
297-n = 1_000_000
298-```
299-300-```{code-cell} python3
301-%%time
302-303-y = 0 # Will accumulate and store sum
304-for i in range(n):
305- x = random.uniform(0, 1)
306- y += x**2
307-```
308-309-The following vectorized code achieves the same thing.
310-311-```{code-cell} ipython
312-%%time
313-314-x = np.random.uniform(0, 1, n)
315-y = np.sum(x**2)
316-```
317-318-As you can see, the second code block runs much faster. Why?
319-320-The second code block breaks the loop down into three basic operations
321-322-1. draw `n` uniforms
323-1. square them
324-1. sum them
325-326-These are sent as batch operators to optimized machine code.
327290328-Apart from minor overheads associated with sending data back and forth, the result is C or Fortran-like speed.
329-330-When we run batch operations on arrays like this, we say that the code is *vectorized*.
331-332-Vectorized code is typically fast and efficient.
333-334-It is also surprisingly flexible, in the sense that many operations can be vectorized.
335-336-The next section illustrates this point.
337-338-(ufuncs)=
339-### Universal Functions
340-341-```{index} single: NumPy; Universal Functions
342-```
343-344-Many functions provided by NumPy are so-called *universal functions* --- also called [ufuncs](https://docs.scipy.org/doc/numpy/reference/ufuncs.html).
345-346-This means that they
347-348-* map scalars into scalars, as expected
349-* map arrays into arrays, acting element-wise
350-351-For example, `np.cos` is a ufunc:
352-353-```{code-cell} python3
354-np.cos(1.0)
355-```
356-357-```{code-cell} python3
358-np.cos(np.linspace(0, 1, 3))
291+```{figure} /_static/lecture_specific/need_for_speed/matlab.png
359292```
360293361-By exploiting ufuncs, many operations can be vectorized.
362-363-For example, consider the problem of maximizing a function $f$ of two
364-variables $(x,y)$ over the square $[-a, a] \times [-a, a]$.
365-366-For $f$ and $a$ let's choose
367-368-$$
369-f(x,y) = \frac{\cos(x^2 + y^2)}{1 + x^2 + y^2}
370-\quad \text{and} \quad
371-a = 3
372-$$
373-374-Here's a plot of $f$
375-376-```{code-cell} ipython
377-import matplotlib.pyplot as plt
378-from mpl_toolkits.mplot3d.axes3d import Axes3D
379-from matplotlib import cm
380-381-def f(x, y):
382- return np.cos(x**2 + y**2) / (1 + x**2 + y**2)
383-384-xgrid = np.linspace(-3, 3, 50)
385-ygrid = xgrid
386-x, y = np.meshgrid(xgrid, ygrid)
387-388-fig = plt.figure(figsize=(10, 8))
389-ax = fig.add_subplot(111, projection='3d')
390-ax.plot_surface(x,
391- y,
392- f(x, y),
393- rstride=2, cstride=2,
394- cmap=cm.jet,
395- alpha=0.7,
396- linewidth=0.25)
397-ax.set_zlim(-0.5, 1.0)
398-ax.set_xlabel('$x$', fontsize=14)
399-ax.set_ylabel('$y$', fontsize=14)
400-plt.show()
401-```
402-403-To maximize it, we're going to use a naive grid search:
404-405-1. Evaluate $f$ for all $(x,y)$ in a grid on the square.
406-1. Return the maximum of observed values.
407-408-The grid will be
409-410-```{code-cell} python3
411-grid = np.linspace(-3, 3, 1000)
412-```
413-414-Here's a non-vectorized version that uses Python loops.
415-416-```{code-cell} python3
417-%%time
418-419-m = -np.inf
420-421-for x in grid:
422- for y in grid:
423- z = f(x, y)
424- if z > m:
425- m = z
426-```
427-428-And here's a vectorized version
429-430-```{code-cell} python3
431-%%time
432-433-x, y = np.meshgrid(grid, grid)
434-np.max(f(x, y))
435-```
436-437-In the vectorized version, all the looping takes place in compiled code.
438-439-As you can see, the second version is **much** faster.
440-441-(We'll make it even faster again later on, using more scientific programming tricks.)
294+Vectorization can greatly accelerate many numerical computations, as we will see
295+in later lectures.
442296443297(numba-p_c_vectorization)=
444298## Beyond Vectorization
@@ -462,11 +316,11 @@ In these kinds of settings, we need to go back to loops.
462316Fortunately, there are alternative ways to speed up Python loops that work in
463317almost any setting.
464318465-For example, in the last few years, a new Python library called [Numba](http://numba.pydata.org/) has appeared that solves the main problems
466-with vectorization listed above.
319+For example, [Numba](http://numba.pydata.org/) solves the main problems with
320+vectorization listed above.
467321468322It does so through something called **just in time (JIT) compilation**,
469323which can generate extremely fast and efficient code.
470324471-We'll learn how to use Numba {doc}`soon <numba>`.
325+{doc}`Later <numba>` we'll learn how to use Numba to accelerate Python code.
472326