MTH3007b Weekly Problems 7

Original Documents: Problem Sheet / Provided Solutions

Vibes: …

Used Techniques:


7.1. Monte Carlo Integration

Question

Use Monte Carlo integration to estimate with random samples. Report the estimate and its error.

Monte Carlo integration estimates where is the sample mean of evaluated at uniformly random points in .

The estimate and its standard error are:

where and .

The exact value is .

import numpy as np
 
number_of_samples = 1000
np.random.seed(0)
x_samples = np.random.uniform(0, 1, number_of_samples)
f_values = x_samples ** 2
 
integral_estimate = (1 - 0) * np.mean(f_values)
error_estimate = (1 - 0) * np.sqrt(
    (np.mean(f_values ** 2) - np.mean(f_values) ** 2) / number_of_samples
)
print(f"F = {integral_estimate:.4f}, error = {error_estimate:.4f}, exact = 0.3333")

Expected output: with error (varies with seed). The error is of order ; the prefactor depends on the variance of .


7.2. Error Scaling for Monte Carlo:

Question

Verify numerically that the Monte Carlo error scales as by running the estimator for several values of .

The Monte Carlo error scaling is:

This is a consequence of the central limit theorem. Doubling reduces the error by , regardless of the dimension of the integral.

import numpy as np
 
np.random.seed(42)
exact_value = 1.0 / 3.0
 
print(f"{'N':>8}  {'Estimate':>10}  {'Error (SE)':>12}  {'|estimate - exact|':>20}")
for number_of_samples in [100, 1000, 10000, 100000]:
    x_samples = np.random.uniform(0, 1, number_of_samples)
    f_values = x_samples ** 2
    estimate = np.mean(f_values)
    standard_error = np.sqrt(
        (np.mean(f_values ** 2) - np.mean(f_values) ** 2) / number_of_samples
    )
    print(f"{number_of_samples:>8}  {estimate:>10.5f}  {standard_error:>12.5f}  {abs(estimate - exact_value):>20.5f}")

The standard error column should decrease by a factor of each time increases by a factor of 10, confirming convergence.


7.3. Wiener Process Simulation

Question

Implement a Wiener process simulation and plot 100 independent realisations.

A Wiener process (standard Brownian motion) satisfies:

  • Increments are independent and normally distributed with mean 0 and variance .

The discrete update rule is:

The Euler-Maruyama method for a Wiener process is exact (not an approximation) because the process itself has independent increments.

import numpy as np
import matplotlib.pyplot as plt
 
time_step = 0.01
time_end = 10.0
number_of_steps = int(round(time_end / time_step))
number_of_walkers = 100
np.random.seed(0)
 
W_paths = np.zeros((number_of_walkers, number_of_steps + 1))
t_values = np.linspace(0, time_end, number_of_steps + 1)
 
for walker_index in range(number_of_walkers):
    for step_index in range(number_of_steps):
        W_paths[walker_index, step_index + 1] = (
            W_paths[walker_index, step_index]
            + np.sqrt(time_step) * np.random.normal()
        )
 
# Vectorised equivalent (more efficient):
# increments = np.sqrt(time_step) * np.random.normal(size=(number_of_walkers, number_of_steps))
# W_paths = np.concatenate([np.zeros((number_of_walkers, 1)), np.cumsum(increments, axis=1)], axis=1)
 
plt.figure(figsize=(10, 5))
for walker_index in range(number_of_walkers):
    plt.plot(t_values, W_paths[walker_index, :], alpha=0.3, linewidth=0.5)
plt.xlabel('t')
plt.ylabel('W(t)')
plt.title('100 Wiener process realisations')
plt.tight_layout()
plt.show()
 
variance_over_time = np.var(W_paths, axis=0)
print(f"Var[W(t=5)] = {variance_over_time[500]:.3f}, expected 5.000")
print(f"Var[W(t=10)] = {variance_over_time[-1]:.3f}, expected 10.000")

Key statistical properties visible from the plot:

  • All paths start at .
  • The spread (standard deviation) of the ensemble grows as (variance grows linearly).
  • Individual paths are continuous but nowhere differentiable.