@@ -332,13 +332,12 @@ We simulate inventory dynamics under the optimal policy for the baseline $\gamma
332332333333```{code-cell} ipython3
334334@numba.jit(nopython=True)
335-def sim_inventories(ts_length, σ, p, X_init=0, seed=0):
335+def sim_inventories(ts_length, σ, p, rng, X_init=0):
336336 """Simulate inventory dynamics under policy σ."""
337- np.random.seed(seed)
338337 X = np.zeros(ts_length, dtype=np.int32)
339338 X[0] = X_init
340339 for t in range(ts_length - 1):
341- d = np.random.geometric(p) - 1
340+ d = rng.geometric(p) - 1
342341 X[t+1] = max(X[t] - d, 0) + σ[X[t]]
343342 return X
344343```
@@ -354,7 +353,8 @@ K = len(x_values) - 1
354353355354for i, γ in enumerate(γ_values):
356355 v, σ = results[γ]
357- X = sim_inventories(ts_length, σ, model.p, X_init=K // 2, seed=sim_seed)
356+ X = sim_inventories(ts_length, σ, model.p,
357+ np.random.default_rng(sim_seed), X_init=K // 2)
358358 axes[i].plot(X, alpha=0.7)
359359 axes[i].set_ylabel("inventory")
360360 axes[i].set_title(f"$\\gamma = {γ}$")
@@ -588,8 +588,7 @@ the update target uses $\exp(-\gamma R_{t+1})
588588```{code-cell} ipython3
589589@numba.jit(nopython=True)
590590def q_learning_rs_kernel(K, p, c, κ, β, γ, n_steps, X_init,
591- ε_init, ε_min, ε_decay, q_init, snapshot_steps, seed):
592- np.random.seed(seed)
591+ ε_init, ε_min, ε_decay, q_init, snapshot_steps, rng):
593592 q = np.full((K + 1, K + 1), q_init) # optimistic initialization
594593 n = np.zeros((K + 1, K + 1)) # visit counts for learning rate
595594 ε = ε_init
@@ -600,7 +599,7 @@ def q_learning_rs_kernel(K, p, c, κ, β, γ, n_steps, X_init,
600599601600 # Initialize state and action
602601 x = X_init
603- a = np.random.randint(0, K - x + 1)
602+ a = rng.integers(0, K - x + 1)
604603605604 for t in range(n_steps):
606605 # Record policy snapshot if needed
@@ -609,7 +608,7 @@ def q_learning_rs_kernel(K, p, c, κ, β, γ, n_steps, X_init,
609608 snap_idx += 1
610609611610 # === Draw D_{t+1} and observe outcome ===
612- d = np.random.geometric(p) - 1
611+ d = rng.geometric(p) - 1
613612 reward = min(x, d) - c * a - κ * (a > 0)
614613 x_next = max(x - d, 0) + a
615614@@ -630,8 +629,8 @@ def q_learning_rs_kernel(K, p, c, κ, β, γ, n_steps, X_init,
630629631630 # === Behavior policy: ε-greedy (uses a_next, the argmin action) ===
632631 x = x_next
633- if np.random.random() < ε:
634- a = np.random.randint(0, K - x + 1)
632+ if rng.random() < ε:
633+ a = rng.integers(0, K - x + 1)
635634 else:
636635 a = a_next
637636 ε = max(ε_min, ε * ε_decay)
@@ -649,8 +648,9 @@ def q_learning_rs(model, n_steps=20_000_000, X_init=0,
649648 K = len(x_values) - 1
650649 if snapshot_steps is None:
651650 snapshot_steps = np.array([], dtype=np.int64)
651+ rng = np.random.default_rng(seed)
652652 return q_learning_rs_kernel(K, p, c, κ, β, γ, n_steps, X_init,
653- ε_init, ε_min, ε_decay, q_init, snapshot_steps, seed)
653+ ε_init, ε_min, ε_decay, q_init, snapshot_steps, rng)
654654```
655655656656### Running Q-learning
@@ -721,7 +721,8 @@ X_init = K // 2
721721sim_seed = 5678
722722723723# Optimal policy
724-X_opt = sim_inventories(ts_length, σ_star, model.p, X_init, seed=sim_seed)
724+X_opt = sim_inventories(ts_length, σ_star, model.p,
725+ np.random.default_rng(sim_seed), X_init)
725726axes[0].plot(X_opt, alpha=0.7)
726727axes[0].set_ylabel("inventory")
727728axes[0].set_title("Optimal (VFI)")
@@ -730,7 +731,8 @@ axes[0].set_ylim(0, K + 2)
730731# Q-learning snapshots
731732for i in range(n_snaps):
732733 σ_snap = snapshots[i]
733- X = sim_inventories(ts_length, σ_snap, model.p, X_init, seed=sim_seed)
734+ X = sim_inventories(ts_length, σ_snap, model.p,
735+ np.random.default_rng(sim_seed), X_init)
734736 axes[i + 1].plot(X, alpha=0.7)
735737 axes[i + 1].set_ylabel("inventory")
736738 axes[i + 1].set_title(f"Step {snap_steps[i]:,}")