Each of the eight v2.0 companion notebooks (10_bsde through 17_generative_calibration) now follows the mandatory pedagogical sandwich structure: PRE markdown : theorem / model / pivot equation / what the cell verifies CODE cell : labelled prints + at least one matplotlib figure POST markdown: expected result, graph reading, conclusion Each notebook carries at least one concrete real-world example (heat plate, inverted pendulum, opinion polarization, collective decision, OU drift under Cauchy noise, mixture vs gaussian MMD, etc.) Generator script: scripts/enrich_v2_notebooks.py Doc plots refreshed via scripts/inject_doc_plots.py.
177 KiB
177 KiB
In [1]:
import numpy as np
import matplotlib.pyplot as plt
from optimizr import _core as opt
plt.rcParams['figure.figsize'] = (8.5, 4.5)
plt.rcParams['figure.dpi'] = 110
plt.rcParams['axes.grid'] = True
plt.rcParams['grid.alpha'] = 0.3
In [2]:
rng = np.random.default_rng(0)
X = rng.standard_normal(200).tolist()
m = float(opt.mmd_gaussian(X, X, sigma=1.0))
print(f"MMD(X, X) = {m:.3e} (attendu : 0)")
fig, ax = plt.subplots()
ax.hist(X, bins=30, density=True, color='C0',
edgecolor='white', alpha=0.85)
ax.set_title(r'Échantillon $X = Y$')
ax.set_xlabel('x'); ax.set_ylabel('densité')
fig.tight_layout(); plt.show()
MMD(X, X) = 0.000e+00 (attendu : 0)
In [3]:
rng = np.random.default_rng(2)
n = 500
X = rng.standard_normal(n).tolist() # N(0, 1)
Y = rng.laplace(0.0, 1.0 / np.sqrt(2.0), n).tolist() # même variance
sigmas = np.geomspace(0.1, 10.0, 25)
mmds = [float(opt.mmd_gaussian(X, Y, sigma=s)) for s in sigmas]
print(f"MMD min : {min(mmds):.3e}, MMD max : {max(mmds):.3e}")
print(f"σ optimal : {sigmas[int(np.argmax(mmds))]:.2f}")
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
xs = np.linspace(-5, 5, 400)
axes[0].hist(X, bins=40, density=True, alpha=0.5,
label=r'$\mathcal{N}(0,1)$', color='C0')
axes[0].hist(Y, bins=40, density=True, alpha=0.5,
label='Laplace', color='C3')
axes[0].plot(xs, np.exp(-xs**2/2)/np.sqrt(2*np.pi),
'C0--', lw=1.5)
axes[0].plot(xs, np.exp(-np.abs(xs)*np.sqrt(2))*np.sqrt(2)/2,
'C3--', lw=1.5)
axes[0].set_xlabel('x'); axes[0].set_ylabel('densité')
axes[0].set_title("Densités comparées"); axes[0].legend()
axes[1].semilogx(sigmas, mmds, 'o-', lw=2, color='C2')
axes[1].set_xlabel(r'bandwidth $\sigma$')
axes[1].set_ylabel(r'$\widehat{\text{MMD}}^2$')
axes[1].set_title("Sensibilité à la bandwidth")
fig.tight_layout(); plt.show()
MMD min : 1.182e-02, MMD max : 1.603e-01
σ optimal : 0.38
In [4]:
rng = np.random.default_rng(11)
n = 400
P = rng.standard_normal(n).tolist()
mus = np.linspace(0.0, 2.5, 11)
mmd_vals = []
for mu in mus:
half = n // 2
Q = np.concatenate([
rng.standard_normal(half) - mu,
rng.standard_normal(n - half) + mu,
]).tolist()
mmd_vals.append(float(opt.mmd_gaussian(P, Q, sigma=1.0)))
for mu, m in zip(mus, mmd_vals):
print(f"μ = {mu:.2f} -> MMD² = {m:.3e}")
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].plot(mus, mmd_vals, 'o-', lw=2, color='C2')
axes[0].set_xlabel(r'séparation $\mu$')
axes[0].set_ylabel(r'$\widehat{\text{MMD}}^2$')
axes[0].set_title("MMD croissante avec la séparation")
mu_show = mus[-1]
half = n // 2
Q_show = np.concatenate([
rng.standard_normal(half) - mu_show,
rng.standard_normal(n - half) + mu_show,
])
axes[1].hist(P, bins=30, density=True, alpha=0.5,
label=r'P = $\mathcal{N}(0,1)$', color='C0')
axes[1].hist(Q_show, bins=30, density=True, alpha=0.5,
label=fr'Q (μ = {mu_show:.1f})', color='C3')
axes[1].set_xlabel('x'); axes[1].set_ylabel('densité')
axes[1].set_title("P vs Q (mélange séparé)")
axes[1].legend()
fig.tight_layout(); plt.show()
μ = 0.00 -> MMD² = 8.317e-02 μ = 0.25 -> MMD² = 3.606e-02 μ = 0.50 -> MMD² = 9.864e-02 μ = 0.75 -> MMD² = 1.881e-01 μ = 1.00 -> MMD² = 2.075e-01 μ = 1.25 -> MMD² = 3.452e-01 μ = 1.50 -> MMD² = 3.628e-01 μ = 1.75 -> MMD² = 4.763e-01 μ = 2.00 -> MMD² = 5.565e-01 μ = 2.25 -> MMD² = 6.350e-01 μ = 2.50 -> MMD² = 6.936e-01