[ 0.987, 0.994, 1. , 1.006, 1.013],
[ 0.981, 0.987, 0.994, 1. , 1.006],
[ 0.975, 0.981, 0.987, 0.994, 1. ]])
In [ 73 ]: md = d ** md
md.round( 3 )
Out[73]: array([[ 1. , 1. , 1. , 1. , 1. ],
[ 0.994, 0.994, 0.994, 0.994, 0.994],
[ 0.987, 0.987, 0.987, 0.987, 0.987],
[ 0.981, 0.981, 0.981, 0.981, 0.981],
[ 0.975, 0.975, 0.975, 0.975, 0.975]])
Finally, bringing everything together:
In [ 74 ]: S = S0 * mu * md
S.round( 3 )
Out[74]: array([[ 100. , 100.634, 101.273, 101.915, 102.562],
[ 98.743, 99.37 , 100. , 100.634, 101.273],
[ 97.502, 98.121, 98.743, 99.37 , 100. ],
[ 96.276, 96.887, 97.502, 98.121, 98.743],
[ 95.066, 95.669, 96.276, 96.887, 97.502]])
From the ndarray object S, only the upper triangular matrix is of importance. Although
we do more calculations with this approach than are needed in principle, the approach is,
as expected, much faster than the first version, which relies heavily on nested loops on the
Python level:
In [ 75 ]: M = 1000 # reset number of time steps
%time round(binomial_np( 100 ), 3 )
Out[75]: CPU times: user 308 ms, sys: 6 ms, total: 314 ms
Wall time: 304 ms
10.449
Numba has proven a valuable performance enhancement tool for our sandbox example.
Here, it can prove its worth in the context of a very important financial algorithm:
In [ 76 ]: binomial_nb = nb.jit(binomial_py)
In [ 77 ]: %time round(binomial_nb( 100 ), 3 )
Out[77]: CPU times: user 1.71 s, sys: 137 ms, total: 1.84 s
Wall time: 1.59 s
10.449
We do not yet see a significant speedup over the NumPy vectorized version since the first
call of the compiled function involves some overhead. Therefore, using the
perf_comp_func function shall shed a more realistic light on how the three different
implementations compare with regard to performance. Obviously, the Numba compiled
version is indeed significantly faster than the NumPy version:
In [ 78 ]: func_list = [‘binomial_py’, ‘binomial_np’, ‘binomial_nb’]
K = 100.
data_list = 3 * [‘K’]
In [ 79 ]: perf_comp_data(func_list, data_list)
Out[79]: function: binomial_nb, av. time sec: 0.14800, relative: 1.0
function: binomial_np, av. time sec: 0.31770, relative: 2.1
function: binomial_py, av. time sec: 3.36707, relative: 22.8
In summary, we can state the following:
Efficiency: using Numba involves only a little additional effort. The original function
is often not changed at all; all you need to do is call the jit function.
Speed-up: Numba often leads to significant improvements in execution speed, not
only compared to pure Python but also to vectorized NumPy implementations.