GitHub

@@ -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

354353355354

for 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)

590590

def 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

721721

sim_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)

725726

axes[0].plot(X_opt, alpha=0.7)

726727

axes[0].set_ylabel("inventory")

727728

axes[0].set_title("Optimal (VFI)")

@@ -730,7 +731,8 @@ axes[0].set_ylim(0, K + 2)

730731

# Q-learning snapshots

731732

for 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]:,}")

Read the original on github.com ↗