Skip to content

Commit 8353cff

Browse files
Update rng usage in likelihood_ratio_process.md
1 parent 6f473fe commit 8353cff

1 file changed

Lines changed: 38 additions & 36 deletions

File tree

lectures/likelihood_ratio_process.md

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ from scipy.stats import beta as beta_dist
6363
import pandas as pd
6464
from IPython.display import display, Math
6565
import quantecon as qe
66+
67+
rng = np.random.default_rng()
6668
```
6769

6870
## Likelihood Ratio Process
@@ -154,18 +156,18 @@ def likelihood_ratio(w, f_func, g_func):
154156
return f_func(w) / g_func(w)
155157
156158
@jit
157-
def simulate_likelihood_ratios(a, b, f_func, g_func, T=50, N=500):
159+
def simulate_likelihood_ratios(a, b, f_func, g_func, rng, T=50, N=500):
158160
"""
159161
Generate N sets of T observations of the likelihood ratio.
160162
"""
161163
l_arr = np.empty((N, T))
162164
for i in range(N):
163165
for j in range(T):
164-
w = np.random.beta(a, b)
166+
w = rng.beta(a, b)
165167
l_arr[i, j] = f_func(w) / g_func(w)
166168
return l_arr
167169
168-
def simulate_sequences(distribution, f_func, g_func,
170+
def simulate_sequences(distribution, f_func, g_func, rng,
169171
F_params=(1, 1), G_params=(3, 1.2), T=50, N=500):
170172
"""
171173
Generate N sequences of T observations from specified distribution.
@@ -177,7 +179,7 @@ def simulate_sequences(distribution, f_func, g_func,
177179
else:
178180
raise ValueError("distribution must be 'f' or 'g'")
179181
180-
l_arr = simulate_likelihood_ratios(a, b, f_func, g_func, T, N)
182+
l_arr = simulate_likelihood_ratios(a, b, f_func, g_func, rng, T, N)
181183
l_seq = np.cumprod(l_arr, axis=1)
182184
return l_arr, l_seq
183185
@@ -207,7 +209,7 @@ draws from $g$.
207209

208210
```{code-cell} ipython3
209211
# Simulate when nature draws from g
210-
l_arr_g, l_seq_g = simulate_sequences('g', f, g, (F_a, F_b), (G_a, G_b))
212+
l_arr_g, l_seq_g = simulate_sequences('g', f, g, rng, (F_a, F_b), (G_a, G_b))
211213
plot_likelihood_paths(l_seq_g,
212214
title="$L(w^{t})$ paths when nature draws from g",
213215
ylim=[0, 3])
@@ -288,7 +290,7 @@ averaging across these many paths at each $t$.
288290

289291
```{code-cell} ipython3
290292
l_arr_g, l_seq_g = simulate_sequences('g',
291-
f, g, (F_a, F_b), (G_a, G_b), N=50000)
293+
f, g, rng, (F_a, F_b), (G_a, G_b), N=50000)
292294
```
293295

294296
It would be useful to use simulations to verify that unconditional means
@@ -335,7 +337,7 @@ Please note the scale of the $y$ axis.
335337

336338
```{code-cell} ipython3
337339
# Simulate when nature draws from f
338-
l_arr_f, l_seq_f = simulate_sequences('f', f, g,
340+
l_arr_f, l_seq_f = simulate_sequences('f', f, g, rng,
339341
(F_a, F_b), (G_a, G_b), N=50000)
340342
```
341343

@@ -787,7 +789,7 @@ for i, scenario in enumerate(scenarios):
787789
T = 150
788790
789791
# Generate data from h
790-
h_data = np.random.beta(scenario["h_params"][0],
792+
h_data = rng.beta(scenario["h_params"][0],
791793
scenario["h_params"][1], (N_paths, T))
792794
l_ratios, l_cumulative = compute_likelihood_ratios(h_data, f, g)
793795
log_l_cumulative = np.log(l_cumulative)
@@ -876,45 +878,45 @@ of IID draws from $g$.
876878
Here is Python code that we'll use to implement timing protocol 1 and 2
877879
878880
```{code-cell} ipython3
879-
def protocol_1(π_minus_1, T, N=1000, F_params=(1, 1), G_params=(3, 1.2)):
881+
def protocol_1(π_minus_1, T, rng, N=1000, F_params=(1, 1), G_params=(3, 1.2)):
880882
"""
881883
Simulate Protocol 1: Nature decides once at t=-1 which model to use.
882884
"""
883885
F_a, F_b = F_params
884886
G_a, G_b = G_params
885887
886888
# Single coin flip for the true model
887-
true_models_F = np.random.rand(N) < π_minus_1
889+
true_models_F = rng.random(N) < π_minus_1
888890
sequences = np.empty((N, T))
889891
890892
n_f = np.sum(true_models_F)
891893
n_g = N - n_f
892894
893895
if n_f > 0:
894-
sequences[true_models_F, :] = np.random.beta(F_a, F_b, (n_f, T))
896+
sequences[true_models_F, :] = rng.beta(F_a, F_b, (n_f, T))
895897
if n_g > 0:
896-
sequences[~true_models_F, :] = np.random.beta(G_a, G_b, (n_g, T))
898+
sequences[~true_models_F, :] = rng.beta(G_a, G_b, (n_g, T))
897899
898900
return sequences, true_models_F
899901
900-
def protocol_2(π_minus_1, T, N=1000, F_params=(1, 1), G_params=(3, 1.2)):
902+
def protocol_2(π_minus_1, T, rng, N=1000, F_params=(1, 1), G_params=(3, 1.2)):
901903
"""
902904
Simulate Protocol 2: Nature decides at each time step which model to use.
903905
"""
904906
F_a, F_b = F_params
905907
G_a, G_b = G_params
906908
907909
# Coin flips for each time step
908-
true_models_F = np.random.rand(N, T) < π_minus_1
910+
true_models_F = rng.random((N, T)) < π_minus_1
909911
sequences = np.empty((N, T))
910912
911913
n_f = np.sum(true_models_F)
912914
n_g = N * T - n_f
913915
914916
if n_f > 0:
915-
sequences[true_models_F] = np.random.beta(F_a, F_b, n_f)
917+
sequences[true_models_F] = rng.beta(F_a, F_b, n_f)
916918
if n_g > 0:
917-
sequences[~true_models_F] = np.random.beta(G_a, G_b, n_g)
919+
sequences[~true_models_F] = rng.beta(G_a, G_b, n_g)
918920
919921
return sequences, true_models_F
920922
```
@@ -970,12 +972,12 @@ Now let's simulate timing protocol 1 and compute the error probabilities
970972
```{code-cell} ipython3
971973
972974
def compute_protocol_1_errors(π_minus_1, T_max, N_simulations, f_func, g_func,
973-
F_params=(1, 1), G_params=(3, 1.2)):
975+
rng, F_params=(1, 1), G_params=(3, 1.2)):
974976
"""
975977
Compute error probabilities for Protocol 1.
976978
"""
977979
sequences, true_models = protocol_1(
978-
π_minus_1, T_max, N_simulations, F_params, G_params)
980+
π_minus_1, T_max, rng, N_simulations, F_params, G_params)
979981
l_ratios, L_cumulative = compute_likelihood_ratios(sequences,
980982
f_func, g_func)
981983
@@ -1007,10 +1009,10 @@ The following code visualizes the error probabilities for timing protocol 1
10071009
:tags: [hide-input]
10081010
10091011
def analyze_protocol_1(π_minus_1, T_max, N_simulations, f_func, g_func,
1010-
F_params=(1, 1), G_params=(3, 1.2)):
1012+
rng, F_params=(1, 1), G_params=(3, 1.2)):
10111013
"""Analyze Protocol 1"""
10121014
result = compute_protocol_1_errors(π_minus_1, T_max, N_simulations,
1013-
f_func, g_func, F_params, G_params)
1015+
f_func, g_func, rng, F_params, G_params)
10141016
10151017
# Plot results
10161018
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
@@ -1046,7 +1048,7 @@ T_max = 30
10461048
N_simulations = 10_000
10471049
10481050
result_p1 = analyze_protocol_1(π_minus_1, T_max, N_simulations,
1049-
f, g, (F_a, F_b), (G_a, G_b))
1051+
f, g, rng, (F_a, F_b), (G_a, G_b))
10501052
```
10511053
10521054
Notice how the model selection error probability approaches zero as $T$ grows.
@@ -1078,12 +1080,12 @@ Now let's write some code to simulate it
10781080
10791081
```{code-cell} ipython3
10801082
def compute_protocol_2_errors(π_minus_1, T_max, N_simulations, f_func, g_func,
1081-
F_params=(1, 1), G_params=(3, 1.2)):
1083+
rng, F_params=(1, 1), G_params=(3, 1.2)):
10821084
"""
10831085
Compute error probabilities for Protocol 2.
10841086
"""
10851087
sequences, true_models = protocol_2(π_minus_1,
1086-
T_max, N_simulations, F_params, G_params)
1088+
T_max, rng, N_simulations, F_params, G_params)
10871089
l_ratios, _ = compute_likelihood_ratios(sequences, f_func, g_func)
10881090
10891091
T_range = np.arange(1, T_max + 1)
@@ -1186,11 +1188,11 @@ Now we simulate timing protocol 2 and compute the classification error probabili
11861188
In the next cell, we also compare the theoretical classification accuracy to the empirical classification accuracy
11871189
11881190
```{code-cell} ipython3
1189-
def analyze_protocol_2(π_minus_1, T_max, N_simulations, f_func, g_func,
1191+
def analyze_protocol_2(π_minus_1, T_max, N_simulations, f_func, g_func, rng,
11901192
theory_error=None, F_params=(1, 1), G_params=(3, 1.2)):
11911193
"""Analyze Protocol 2."""
11921194
result = compute_protocol_2_errors(π_minus_1, T_max, N_simulations,
1193-
f_func, g_func, F_params, G_params)
1195+
f_func, g_func, rng, F_params, G_params)
11941196
11951197
# Plot results
11961198
plt.figure(figsize=(10, 6))
@@ -1210,7 +1212,7 @@ def analyze_protocol_2(π_minus_1, T_max, N_simulations, f_func, g_func,
12101212
return result
12111213
12121214
# Analyze Protocol 2
1213-
result_p2 = analyze_protocol_2(π_minus_1, T_max, N_simulations, f, g,
1215+
result_p2 = analyze_protocol_2(π_minus_1, T_max, N_simulations, f, g, rng,
12141216
theory_error, (F_a, F_b), (G_a, G_b))
12151217
```
12161218
@@ -1403,8 +1405,8 @@ for i, ((f_a, f_b), (g_a, g_b)) in enumerate(distribution_pairs):
14031405
chernoff_vals[i], _ = compute_chernoff_entropy(f, g)
14041406
14051407
# Generate samples
1406-
sequences_f = np.random.beta(f_a, f_b, (N_half, T_large))
1407-
sequences_g = np.random.beta(g_a, g_b, (N_half, T_large))
1408+
sequences_f = rng.beta(f_a, f_b, (N_half, T_large))
1409+
sequences_g = rng.beta(g_a, g_b, (N_half, T_large))
14081410
14091411
# Compute likelihood ratios and cumulative products
14101412
_, L_cumulative_f = compute_likelihood_ratios(sequences_f, f, g)
@@ -1585,12 +1587,12 @@ def markov_kl_divergence(P_f, P_g, pi_f):
15851587
kl_rate = np.sum(pi_f[:, np.newaxis] * P_f * log_ratios)
15861588
return kl_rate
15871589
1588-
def simulate_markov_chain(P, pi_0, T, N_paths=1000):
1590+
def simulate_markov_chain(P, pi_0, T, rng, N_paths=1000):
15891591
"""
15901592
Simulate N_paths sample paths from a Markov chain
15911593
"""
15921594
mc = qe.MarkovChain(P, state_values=None)
1593-
initial_states = np.random.choice(len(P), size=N_paths, p=pi_0)
1595+
initial_states = rng.choice(len(P), size=N_paths, p=pi_0)
15941596
paths = np.zeros((N_paths, T+1), dtype=int)
15951597
15961598
for i in range(N_paths):
@@ -1621,7 +1623,7 @@ def compute_likelihood_ratio_markov(paths, P_f, P_g, π_0_f, π_0_g):
16211623
16221624
return L_ratios
16231625
1624-
def analyze_markov_chains(P_f, P_g,
1626+
def analyze_markov_chains(P_f, P_g, rng,
16251627
T=500, N_paths=1000, plot_paths=True, n_show=50):
16261628
"""
16271629
Complete analysis of two Markov chains
@@ -1642,7 +1644,7 @@ def analyze_markov_chains(P_f, P_g,
16421644
16431645
if plot_paths:
16441646
# Simulate and plot paths
1645-
paths_from_f = simulate_markov_chain(P_f, π_f, T, N_paths)
1647+
paths_from_f = simulate_markov_chain(P_f, π_f, T, rng, N_paths)
16461648
L_ratios_f = compute_likelihood_ratio_markov(
16471649
paths_from_f, P_f, P_g, π_f, π_g)
16481650
@@ -1676,16 +1678,16 @@ def analyze_markov_chains(P_f, P_g,
16761678
'kl_rate_gf': kl_rate_gf
16771679
}
16781680
1679-
def compute_markov_selection_error(T_values, P_f, P_g, π_0_f, π_0_g, N_sim=1000):
1681+
def compute_markov_selection_error(T_values, P_f, P_g, π_0_f, π_0_g, rng, N_sim=1000):
16801682
"""
16811683
Compute model selection error probability for Markov chains
16821684
"""
16831685
errors = []
16841686
16851687
for T in T_values:
16861688
# Simulate from both models
1687-
paths_f = simulate_markov_chain(P_f, π_0_f, T, N_sim//2)
1688-
paths_g = simulate_markov_chain(P_g, π_0_g, T, N_sim//2)
1689+
paths_f = simulate_markov_chain(P_f, π_0_f, T, rng, N_sim//2)
1690+
paths_g = simulate_markov_chain(P_g, π_0_g, T, rng, N_sim//2)
16891691
16901692
# Compute likelihood ratios
16911693
L_f = compute_likelihood_ratio_markov(paths_f, P_f, P_g, π_0_f, π_0_g)
@@ -1717,7 +1719,7 @@ P_g = np.array([[0.5, 0.3, 0.2],
17171719
[0.2, 0.6, 0.2],
17181720
[0.2, 0.2, 0.6]])
17191721
1720-
markov_results = analyze_markov_chains(P_f, P_g)
1722+
markov_results = analyze_markov_chains(P_f, P_g, rng)
17211723
```
17221724
17231725
## Related lectures

0 commit comments

Comments
 (0)