-
-
Notifications
You must be signed in to change notification settings - Fork 30
[numba.md] Update np.random → Generator API #550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
397a891
aef82bd
f8f02b7
ff43ce6
f2c6503
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -454,11 +454,13 @@ Compare speed with and without Numba when the sample size is large. | |
| Here is one solution: | ||
|
|
||
| ```{code-cell} ipython3 | ||
| rng = np.random.default_rng() | ||
|
|
||
| @jit | ||
| def calculate_pi(n=1_000_000): | ||
| def calculate_pi(rng, n=1_000_000): | ||
| count = 0 | ||
| for i in range(n): | ||
| u, v = np.random.uniform(0, 1), np.random.uniform(0, 1) | ||
| u, v = rng.uniform(0, 1), rng.uniform(0, 1) | ||
| d = np.sqrt((u - 0.5)**2 + (v - 0.5)**2) | ||
| if d < 0.5: | ||
| count += 1 | ||
|
|
@@ -471,12 +473,12 @@ Now let's see how fast it runs: | |
|
|
||
| ```{code-cell} ipython3 | ||
| with qe.Timer(): | ||
| calculate_pi() | ||
| calculate_pi(rng) | ||
| ``` | ||
|
|
||
| ```{code-cell} ipython3 | ||
| with qe.Timer(): | ||
| calculate_pi() | ||
| calculate_pi(rng) | ||
| ``` | ||
|
|
||
| If we switch off JIT compilation by removing `@jit`, the code takes around | ||
|
|
@@ -550,10 +552,12 @@ p, q = 0.1, 0.2 # Prob of leaving low and high state respectively | |
| Here's a pure Python version of the function | ||
|
|
||
| ```{code-cell} ipython3 | ||
| def compute_series(n): | ||
| rng = np.random.default_rng() | ||
|
|
||
| def compute_series(n, rng): | ||
| x = np.empty(n, dtype=np.int64) | ||
| x[0] = 1 # Start in state 1 | ||
| U = np.random.uniform(0, 1, size=n) | ||
| U = rng.uniform(0, 1, size=n) | ||
| for t in range(1, n): | ||
| current_x = x[t-1] | ||
| if current_x == 0: | ||
|
|
@@ -568,7 +572,7 @@ state is about 0.666 | |
|
|
||
| ```{code-cell} ipython3 | ||
| n = 1_000_000 | ||
| x = compute_series(n) | ||
| x = compute_series(n, rng) | ||
| print(np.mean(x == 0)) # Fraction of time x is in state 0 | ||
| ``` | ||
|
|
||
|
|
@@ -578,7 +582,7 @@ Now let's time it: | |
|
|
||
| ```{code-cell} ipython3 | ||
| with qe.Timer(): | ||
| compute_series(n) | ||
| compute_series(n, rng) | ||
| ``` | ||
|
|
||
| Next let's implement a Numba version, which is easy | ||
|
|
@@ -590,15 +594,15 @@ compute_series_numba = jit(compute_series) | |
| Let's check we still get the right numbers | ||
|
|
||
| ```{code-cell} ipython3 | ||
| x = compute_series_numba(n) | ||
| x = compute_series_numba(n, rng) | ||
| print(np.mean(x == 0)) | ||
| ``` | ||
|
|
||
| Let's see the time | ||
|
|
||
| ```{code-cell} ipython3 | ||
| with qe.Timer(): | ||
| compute_series_numba(n) | ||
| compute_series_numba(n, rng) | ||
| ``` | ||
|
|
||
| This is a nice speed improvement for one line of code! | ||
|
|
@@ -636,11 +640,17 @@ For the size of the Monte Carlo simulation, use something substantial, such as | |
| Here is one solution: | ||
|
|
||
| ```{code-cell} ipython3 | ||
| n = 1_000_000 | ||
| rng = np.random.default_rng() | ||
| u_draws = rng.uniform(size=n) | ||
| v_draws = rng.uniform(size=n) | ||
|
HumphreyYang marked this conversation as resolved.
Outdated
|
||
|
|
||
| @jit(parallel=True) | ||
| def calculate_pi(n=1_000_000): | ||
| def calculate_pi(u_draws, v_draws): | ||
| n = len(u_draws) | ||
| count = 0 | ||
| for i in prange(n): | ||
| u, v = np.random.uniform(0, 1), np.random.uniform(0, 1) | ||
| u, v = u_draws[i], v_draws[i] | ||
| d = np.sqrt((u - 0.5)**2 + (v - 0.5)**2) | ||
| if d < 0.5: | ||
| count += 1 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this avoids the thread-safety issue, but the timing comparison is no longer quite apples-to-apples because the random draws have been moved outside the timed function. Personally, I think we can generate the draws in the first solution by moving n = 1_000_000
rng = np.random.default_rng()
u_draws = rng.uniform(size=n)
v_draws = rng.uniform(size=n)to line 457, and modifying the function in the code block starting at line 457 (solution to exercise 1) to take In this case, we would have a fair timing comparison. Please let me know what you think!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @mmcky, I think this is an example where we need to make a tough choice between keeping the legacy API for simplicity and using the new
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @HumphreyYang I agree with you. I have added some additional notes below regarding double checking |
||
|
|
@@ -653,12 +663,12 @@ Now let's see how fast it runs: | |
|
|
||
| ```{code-cell} ipython3 | ||
| with qe.Timer(): | ||
| calculate_pi() | ||
| calculate_pi(u_draws, v_draws) | ||
| ``` | ||
|
|
||
| ```{code-cell} ipython3 | ||
| with qe.Timer(): | ||
| calculate_pi() | ||
| calculate_pi(u_draws, v_draws) | ||
| ``` | ||
|
|
||
| By switching parallelization on and off (selecting `True` or | ||
|
|
@@ -773,6 +783,7 @@ def compute_call_price_parallel(β=β, | |
| s = np.log(S0) | ||
| h = h0 | ||
| # Simulate forward in time | ||
| # Draws are kept inside the loop to avoid pre-allocating large shock arrays. | ||
| for t in range(n): | ||
| s = s + μ + np.exp(h) * np.random.randn() | ||
| h = ρ * h + ν * np.random.randn() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.