Solutions for http://quant-econ.net/py/need_for_speed.html
Let's start with some imports
In [1]:
import matplotlib.pyplot as plt
import numpy as np
from numba import jit
We let
In [2]:
p, q = 0.1, 0.2 # Prob of leaving low and high state respectively
Here's a pure Python version of the function
In [3]:
def compute_series(n):
x = np.empty(n, dtype=int)
x[0] = 1 # Start in state 1
U = np.random.uniform(0, 1, size=n)
for t in range(1, n):
current_x = x[t-1]
if current_x == 0:
x[t] = U[t] < p
else:
x[t] = U[t] > q
return x
Let's run this code and check that the fraction of time spent in the low state is about 0.666
In [4]:
n = 100000
x = compute_series(n)
print(np.mean(x == 0)) # Fraction of time x is in state 0
Now let's time it
In [5]:
%timeit compute_series(n)
Next let's implement a Numba version, which is easy
In [6]:
compute_series_numba = jit(compute_series)
Let's check we still get the right numbers
In [7]:
x = compute_series_numba(n)
print(np.mean(x == 0))
Let's see the time
In [8]:
%timeit compute_series_numba(n)
This is a nice speed improvement for one line of code
Now let's implement a Cython version
In [9]:
%load_ext cythonmagic
In [10]:
%%cython
import numpy as np
from numpy cimport int_t, float_t
def compute_series_cy(int n):
# == Create NumPy arrays first == #
x_np = np.empty(n, dtype=int)
U_np = np.random.uniform(0, 1, size=n)
# == Now create memoryviews of the arrays == #
cdef int_t [:] x = x_np
cdef float_t [:] U = U_np
# == Other variable declarations == #
cdef float p = 0.1
cdef float q = 0.2
cdef int t
# == Main loop == #
x[0] = 1
for t in range(1, n):
current_x = x[t-1]
if current_x == 0:
x[t] = U[t] < p
else:
x[t] = U[t] > q
return np.asarray(x)
In [11]:
compute_series_cy(10)
Out[11]:
In [12]:
x = compute_series_cy(n)
print(np.mean(x == 0))
In [13]:
%timeit compute_series_cy(n)
The Cython implementation is fast, but not as fast as Numba