Exercises NotebookMath for LLMs

Sampling Methods

ML Specific Math / Sampling Methods

Run notebook
Exercises Notebook

Exercises Notebook

Converted from exercises.ipynb for web reading.

Sampling Methods - Exercises

Ten graded exercises on direct sampling, estimators, MCMC, and ML sampling previews.

Code cell 2

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

try:
    import seaborn as sns
    sns.set_theme(style="whitegrid", palette="colorblind")
    HAS_SNS = True
except ImportError:
    plt.style.use("seaborn-v0_8-whitegrid")
    HAS_SNS = False

mpl.rcParams.update({
    "figure.figsize":    (10, 6),
    "figure.dpi":         120,
    "font.size":           13,
    "axes.titlesize":      15,
    "axes.labelsize":      13,
    "xtick.labelsize":     11,
    "ytick.labelsize":     11,
    "legend.fontsize":     11,
    "legend.framealpha":   0.85,
    "lines.linewidth":      2.0,
    "axes.spines.top":     False,
    "axes.spines.right":   False,
    "savefig.bbox":       "tight",
    "savefig.dpi":         150,
})
np.random.seed(42)
print("Plot setup complete.")

Code cell 3

import numpy as np

def header(title): print("\n"+"="*72+"\n"+title+"\n"+"="*72)
def check_close(name,value,expected,tol=1e-2):
    ok=np.allclose(value,expected,atol=tol,rtol=tol); print(f"{'PASS' if ok else 'FAIL'} - {name}: value={value}, expected={expected}"); return ok
def check_true(name,condition): ok=bool(condition); print(f"{'PASS' if ok else 'FAIL'} - {name}"); return ok
def softmax(z):
    z=np.asarray(z,dtype=float); e=np.exp(z-np.max(z)); return e/e.sum()
np.random.seed(42)
print("Exercise helpers ready.")

Exercise 1: Inverse exponential (*)

Sample exponential variables by inverse CDF.

Code cell 5

# Your Solution
u=np.random.rand(1000)
x=None
print(x)

Code cell 6

# Solution
header("Exercise 1: Inverse exponential")
u=np.random.rand(10000); lam=2.; x=-np.log(1-u)/lam
check_close("mean", x.mean(), 1/lam, tol=0.03)
print("\nTakeaway: inverse-transform sampling converts uniforms through an inverse CDF.")

Exercise 2: Categorical sampler (*)

Sample from cumulative probabilities.

Code cell 8

# Your Solution
p=np.array([0.2,0.8])
s=None
print(s)

Code cell 9

# Solution
header("Exercise 2: Categorical sampler")
p=np.array([0.2,0.8]); cdf=np.cumsum(p); u=np.random.rand(10000); s=np.searchsorted(cdf,u); freq=np.bincount(s,minlength=2)/len(s)
check_close("frequencies", freq, p, tol=0.02)
print("\nTakeaway: categorical sampling is inverse CDF on a discrete CDF.")

Exercise 3: Monte Carlo E[X^2] (*)

Estimate E[X^2] for standard normal.

Code cell 11

# Your Solution
x=np.random.normal(size=1000)
est=None
print(est)

Code cell 12

# Solution
header("Exercise 3: Monte Carlo E[X^2]")
x=np.random.normal(size=20000); est=np.mean(x**2)
check_close("E[X^2]", est, 1.0, tol=0.03)
print("\nTakeaway: Monte Carlo turns expectations into sample means.")

Exercise 4: Confidence interval (**)

Build a normal-approximation CI.

Code cell 14

# Your Solution
x=np.random.normal(size=100)
ci=None
print(ci)

Code cell 15

# Solution
header("Exercise 4: Confidence interval")
x=np.random.normal(loc=1.,scale=2.,size=500); mean=x.mean(); se=x.std(ddof=1)/np.sqrt(len(x)); ci=(mean-1.96*se, mean+1.96*se)
check_true("CI contains true mean", ci[0]<1<ci[1])
print("\nTakeaway: estimator uncertainty shrinks as standard error falls.")

Exercise 5: Rejection sampling (**)

Sample from density p(x)=2x on [0,1].

Code cell 17

# Your Solution
accepted=[]
print(accepted)

Code cell 18

# Solution
header("Exercise 5: Rejection sampling")
accepted=[]; trials=0
while len(accepted)<3000:
    x=np.random.rand(); u=np.random.rand(); trials+=1
    if u <= (2*x)/2: accepted.append(x)
accepted=np.array(accepted)
check_close("mean", accepted.mean(), 2/3, tol=0.03)
print("\nTakeaway: rejection sampling trades proposal simplicity for rejected draws.")

Exercise 6: Importance ESS (**)

Compute ESS for weighted samples.

Code cell 20

# Your Solution
w=np.array([10.,1.,1.])
ess=None
print(ess)

Code cell 21

# Solution
header("Exercise 6: Importance ESS")
w=np.array([10.,1.,1.]); ess=(w.sum()**2)/np.sum(w**2)
check_true("ESS less than sample count", ess<3)
print("\nTakeaway: high weight concentration reduces effective sample size.")

Exercise 7: Metropolis sampler (***)

Run a short random-walk Metropolis chain.

Code cell 23

# Your Solution
chain=[]
print(chain)

Code cell 24

# Solution
header("Exercise 7: Metropolis sampler")
def logp(x): return -0.5*x*x
cur=0.; chain=[]; acc=0
for t in range(6000):
    prop=cur+np.random.normal()
    if np.log(np.random.rand()) < logp(prop)-logp(cur): cur=prop; acc+=1
    if t>500: chain.append(cur)
chain=np.array(chain)
check_close("variance", chain.var(), 1.0, tol=0.15)
print("\nTakeaway: MCMC samples are dependent but can approximate the target distribution.")

Exercise 8: Reparameterization (***)

Sample z=mu+sigma eps.

Code cell 26

# Your Solution
mu=1.; sigma=2.; z=None
print(z)

Code cell 27

# Solution
header("Exercise 8: Reparameterization")
mu=1.; sigma=2.; eps=np.random.normal(size=10000); z=mu+sigma*eps
check_close("mean", z.mean(), mu, tol=0.06)
check_close("std", z.std(), sigma, tol=0.06)
print("\nTakeaway: reparameterization separates randomness from differentiable parameters.")

Exercise 9: Gumbel-Max (***)

Sample categorical values with Gumbel-Max.

Code cell 29

# Your Solution
p=np.array([0.25,0.75])
freq=None
print(freq)

Code cell 30

# Solution
header("Exercise 9: Gumbel-Max")
p=np.array([0.25,0.75]); logits=np.log(p); u=np.random.uniform(1e-12,1-1e-12,size=(10000,2)); g=-np.log(-np.log(u)); s=np.argmax(logits+g,axis=1); freq=np.bincount(s,minlength=2)/len(s)
check_close("frequencies", freq, p, tol=0.02)
print("\nTakeaway: Gumbel-Max converts logits plus Gumbel noise into categorical samples.")

Exercise 10: Top-k top-p (***)

Filter a categorical distribution.

Code cell 32

# Your Solution
logits=np.array([5.,4.,3.,2.])
keep=None
print(keep)

Code cell 33

# Solution
header("Exercise 10: Top-k top-p")
logits=np.array([5.,4.,3.,2.]); probs=softmax(logits); topk=np.argsort(probs)[-2:][::-1]
order=np.argsort(probs)[::-1]; cum=np.cumsum(probs[order]); topp=order[cum<=0.8]; topp=np.r_[topp, order[len(topp)]] if len(topp)<len(order) else topp
check_true("top-k keeps two", len(topk)==2)
check_true("top-p keeps at least one", len(topp)>=1)
print("\nTakeaway: top-k fixes set size; top-p fixes cumulative probability mass.")