Reflected Brownian Motion
Definición
Sea \(\{B(t), t\geq 0\}\) un movimiento Browniano estándar, \(\{X(t)= |B(t)|, t\geq 0\}\) es un movimiento Browniano Reflejado con valor esperado y varianza:
Valor esperado \[\mathbb{E}[X(t)] = \sqrt{\frac{2\pi}{t}}\]
Varianza \[\mathbb{V}ar(X(t)) = t\left(1- \frac{2}{\pi} \right)\]
Movimiento Browniano Reflejado en una dimensión
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.cm as cm
import matplotlib.patches as patches
sns.set_theme()# Definición de la clase
class StochasticSimulation():
def __init__(self, T=None, steps=100, n_times=1):
self.steps = steps
self.n_times = n_times
# Si T no se proporciona, usar T = steps
if T is None:
self.T = steps
else:
self.T = T
self.dt = self.T/ steps
self.t = np.linspace(0, self.T, steps+1)
# Moviento Browniano Reflejado
def ReflectedBrownianMotion(self):
# creamos arreglo
bm = np.zeros( (self.n_times, self.steps+1))
# inicializa simulación
for j in range(bm.shape[1]-1):
for i in range(bm.shape[0]):
bm[i][j+1] = abs(bm[i][j] + np.sqrt(self.dt) * np.random.normal(size=1))
df = pd.DataFrame(bm)
return df
En este ejemplo, primero se definen los parámetros de la simulación: se tomarán \(𝑁=10,000\) pasos, lo que permitirá generar una trayectoria muy detallada del proceso, y se simulará \(𝑀=1\) única trayectoria.
A continuación, se crea una instancia de la clase \(StochasticSimulation\), pasando como argumentos el número de pasos y el número de trayectorias a simular. Esta clase se encarga de manejar los cálculos necesarios para generar procesos estocásticos.
Finalmente, se llama al método \(ReflectedBrownianMotion()\) de la instancia, el cual genera la trayectoria simulada del movimiento browniano. El resultado se almacena en df, un DataFrame que contiene los valores de la trayectoria a lo largo de los pasos de tiempo definidos.
# Parámetros
N = 10000 # número de pasos
M = 1 # número de trayectorias simuladas
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.ReflectedBrownianMotion()
# Colormap
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim.t
std_teorica = np.sqrt(t*(1- (2/np.pi)))
# Media teórica
mean_B = np.sqrt(2*(np.pi/t))
# Intervalos de confianza 95%
conf_upper = mean_B + 1.96 * std_teorica
conf_lower = mean_B #- 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Movimiento Browniano Reflejado[{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("B(t)")
plt.grid(True)
plt.legend()
plt.show()Simulación de mútlples trajectorias
Ahora vamos a simular \(M=1000\) trayectorias
# Parámetros
N = 10000 # número de pasos
M = 1000 # número de trayectorias simuladas
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.ReflectedBrownianMotion()
# Colormap
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim.t
std_teorica = np.sqrt(t*(1- (2/np.pi)))
# Media teórica
mean_B = np.sqrt(2*(np.pi/t))
# Intervalos de confianza 95%
conf_upper = mean_B + 1.96 * std_teorica
conf_lower = mean_B #- 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Movimiento Browniano Reflejado[{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("B(t)")
plt.grid(True)
plt.legend()
plt.show()Distribución al tiempo \(t\)
Vamos a graficar muchas trajectorias, y hacer cortes verticala a distintos tiempos \(t\) y hechar un vistazo a la distribución:
# histogramas para distintos valores de t
valores_t = [10, 100, 1000, 5000]
# Crear figura con subplots 4x4
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes = axes.flatten()
for i, step in enumerate(valores_t):
data = df[step]
mean = data.mean()
var = data.var()
sns.histplot(data, bins=30, kde=True, ax=axes[i])
axes[i].set_title(f"Distribución al tiempo {step}")
axes[i].set_xlabel('Posición')
axes[i].set_ylabel('Frecuencia')
axes[i].legend([f'Media = {mean:.2f}, Varianza = {var:.2f}'])
# Eliminar los subplots vacíos
for j in range(len(valores_t), len(axes)):
fig.delaxes(axes[j])
plt.tight_layout()
plt.show()Simulación de un Movimiento Browniano en dos dimensiones
# Colormap (gradiente de gris)
cmap = cm.get_cmap("Greys", M*2)
centro = (0, 0)
circulo = patches.Circle(centro, 400, edgecolor='grey', facecolor='black', alpha=0.8, lw=1, ls='--')
# Crear la figura y los ejes
fig, ax = plt.subplots(figsize=(14, 10))
ax.add_patch(circulo)
# Brownian Motion
for i in range(df.shape[0] - 1):
if i < 800:
ax.plot(df.iloc[i][:], df.iloc[i + 1][:], linewidth=0.2, color=cmap(i)) # Normaliza el valor de i
else:
continue
plt.title('Simulación Movimiento Browniano Reflejado: {} steps and {} paths'.format(N, M))
plt.xlabel('X(t)')
plt.ylabel('X(t)')
plt.gca().set_xticklabels([]) # quitar etiquetas eje X
plt.gca().set_yticklabels([]) # quitar etiquetas eje Y
plt.grid(True)
plt.show()Referencias
- Ubbo F Wiersema (2008). Brownian Motion Calculus. John Wiley & Sons Ltd.
- Sheldon M. Ross(1995). Stochastic Processes. Wiley Series in Probability and Statistics: Probability and Statistics. John Wiley & Sons.
- LefebvreMario(2007). Applied Stochastics Processes. Springer, New York.