Brownian Bridge
Puente Browniano
El puente Browniano es útil para modelar un sistema que comienza en un nivel dado y se espera que regrese a ese mismo nivel en un momento futuro especificado.
Definición
Un procesos estocástico \(\{X(t)= B(t) - \frac{t}{T} B(T) , 0 \leq t \leq T \}\), es un puente Browniano si satisface las siguientes propiedades:
1.- \(X(0)=X(T)=0\)
2.- \(X(t)\) se distribuye como una normal con media cero y varianza \(t(1-t/T)\)
\[E(X(t)) = 0\], y \[Var(X(t)) = t(1-t/T)\]
3.- \(Cov(X(s), X(t)) = min(s, t) - \frac{st}{T}\)
Demostración:
Para \(0 \le s < t \le T\), la covarianza de \((X(t))\) y \((X(s))\) está dada por
\[\begin{aligned} \mathrm{Cov}\{X(s), X(t)\} &= E\Big[(X(s) - E[X(s)])(X(t) - E[X(t)])\Big] \\ &= E[X(s) X(t)] \\ &= E\Big[(W(s) - s W(T))(W(t) - t W(T))\Big] \\ &= E\Big[ W(s) W(t) - t W(s) W(T) - s W(t) W(T) + s t W^2(T) \Big] \\ &= E\Big[ W(s) W(t)\Big] - t\wedge E\Big[ W(s) W(T)\Big] - s \wedge E\Big[W(t) W(T)\Big] + st\wedge E\Big[W(T)W(T)\Big] \\ &= min(s,t) - t\wedge min(s,T) - s \wedge min(t,T) + st \wedge min(T,T) \\ &= s - s t - s t + s t T \\ &= s - \frac{s t}{T} \end{aligned}\]Para \(0 \le t <s \le T\) se sigue que $t - $, por lo que para cualesquiera \(s>0\), y \(t>0\)
\[Cov(X(s), X(t)) = min(s, t) - \frac{st}{T}\] y la correlación viene dada por
\[ \mathrm{Corr}(X(s), X(t)) = \frac{\mathrm{Cov}(X(s), X(t))}{\sqrt{\mathrm{Var}(X(s)) \mathrm{Var}(X(t))}} = \frac{min(s, t) - \frac{st}{T}}{\sqrt{st(1-t/T)(1-s/T))}}. \]
Simulación
Este código define una clase \(StochasticSimulation()\) para simular procesos estocásticos en tiempo discreto. El método \(\__init\__\) inicializa el número de pasos, repeticiones (\(n\_times\)), el tiempo total \(T\) y genera un vector de tiempos \(t\) con incremento \(dt\). El método \(BrownianBridge()\) simula puentes Brownianos con \(n_times\) simulaciones en paralelo, almacenando cada trayectoria en filas del arreglo. Finalmente, convierte el resultado en un DataFrame de pandas.
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()
# Function para simular una Movimiento Aleatorio
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)
# Brownian bridge method
def BrownianBridge(self):
# Brownian motion
bm = np.zeros( (self.n_times, self.steps+1))
# initiate the loop
for j in range(bm.shape[1]-1):
for i in range(bm.shape[0]):
bm[i][j+1] = bm[i][j] + + np.sqrt(self.dt)*np.random.normal(size=1)
# Brownian Bridge
bb = np.zeros( (self.n_times, self.steps+1))
# initiate the loop
for j in range(bb.shape[1]-1):
for i in range(bb.shape[0]):
bb[i][j+1] = bm[i][j] + -(j/self.steps)*bm[i][self.steps]
df = pd.DataFrame(bb)
return df
Puente Browniano en una dimensión
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 \(BrownianBridge()\) 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.BrownianBridge()
# Colormap
cmap = cm.get_cmap("Grays", M)
# lista de t
t = sim.t
T = sim.T
std_teorica = np.sqrt(t*(1- (t/T)))
# Media teórica
mean_B = [0]*len(t)
# Intervalos de confianza 95% t(1−t/T)
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, linestyle='--')
plt.plot(t, conf_lower, 'blue', linewidth=0.3, linestyle='--')
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Puente Browniano[{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("X(t)")
plt.grid(True)
plt.legend()
plt.show()
Aproximación teórica mediante simulación
Ahora vamos a simular \(M=1000\) trayectorias y vamos averificar algunas propuedades como el valor esperado, varianza y autocorrelación mediante simulación, para
1.- \(\mathbb{E}[X(15)]\), \(E[X(10)]\)
2.- \(\mathbb{V}ar(X(15))\), \(Var(X(10))\)
3.- \(\mathbb{C}or([X(15)], Var[X(10)])\)
# Parámetros
N = 10000 # número de pasos
M = 1000 # número de trayectorias simuladas
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.BrownianBridge()
# Colormap
cmap = cm.get_cmap("Grays", M)
# lista de t
t = sim.t
T = sim.T
std_teorica = np.sqrt(t*(1- (t/T)))
# Media teórica
mean_B = [0]*len(t)
# Intervalos de confianza 95% t(1−t/T)
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, linestyle='--')
plt.plot(t, conf_lower, 'blue', linewidth=0.3, linestyle='--')
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Puente Browniano[{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("X(t)")
plt.grid(True)
plt.legend()
plt.show()
Solución punto 1:
La solución mediante simulación consiste en generar muchas trajectorias, y hacer un corte vertical al tiempo \(t\) extraer los puntos y extraer medidas estadísticas como la media, la varianza, para la covarianza y correlación se seguiría la misma regla pero ahora dos cortes a distintos tiempo, y sobre ellos obtener las medidas. Veamos las distribuciones para los distintos tiempo selecionados en los ejemplos:
# Tiempos a graficar
steps_to_plot = [10, 15]
# Crear figura con subplots
fig, axes = plt.subplots(2, 1, figsize=(10, 6))
axes = axes.flatten()
for i, step in enumerate(steps_to_plot):
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}'])
plt.tight_layout()
plt.show()
# Movimiento Browniano B(15)
X_15 = np.round(df.mean(axis=0).to_list()[15],4)
print(f"Media del movimiento Browniano B(15): {X_15}")
## Media del movimiento Browniano B(15): -0.0611
# Movimiento Browniano B(10)
X_10 = np.round(df.mean(axis=0).to_list()[10],4)
print(f"Media del movimiento Browniano B(10): {X_10}")
## Media del movimiento Browniano B(10): 0.0213
Solución punto 2:
Sabemos que \(Var(X(t)) = t(1−t/T)\)
- \(\mathbb{V}ar(X(15)) = 15(1-15/10000)= 14.97\), y
- \(\mathbb{V}ar(X(10)) = 10(1-10/10000)= 9.99\)
# Puente Browniano X(15)
var_X15 = np.round(df.var(axis=0).to_list()[15],4)
print(f"Media del puente Browniano X(15): {var_X15}")
## Media del puente Browniano X(15): 13.3767
# Puente Browniano X(10)
var_X10= np.round(df.var(axis=0).to_list()[10],4)
print(f"Media del puente Browniano X(10): {var_X10}")
## Media del puente Browniano X(10): 8.4912
El valor simulado se acerca mucho al valor teórico, entre más trayectorias se generen el resultado se aproxima más al teórico.
Solución punto 3:
Recordando la formula de la autocorrelación, \(\mathbb{C}or(X(s), X(t)) = \frac{min(s, t) - \frac{st}{T}}{\sqrt{st(1-t/T)(1-s/T))}}\) y para \(s=15\), y \(t=10\), entonces, el valor teórico
- \(\mathbb{C}or(X(15), X(10)) = \frac{min(s, t) - \frac{st}{T}}{\sqrt{st(1-t/T)(1-s/T))}} \sim 0.82\)
# Movimiento Browniano Corr(B(15), B(10))
B_s = df.iloc[:, 15]
B_t = df.iloc[:, 10]
corr= np.round(np.corrcoef(B_s, B_t)[0,1],4)
print(f"Correlación del movimiento Browniano Cor(X(15),X(10)): {corr}")
## Correlación del movimiento Browniano Cor(X(15),X(10)): 0.7924
Observamos que la simulación aproxima bastente bien el valor teórico.
Simulación de un Puente Browniano en dos dimensiones
# Parámetros
N = 10000 # número de pasos
M = 1000 # número de trayectorias simuladas
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.BrownianBridge()
# Colormap (gradiente de gris)
cmap = cm.get_cmap("Greys", M*2)
centro = (0, 0)
circulo = patches.Circle(centro, 250, 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))
else:
continue
plt.title('Simulación de Puentes Brownianos: {} steps and {} paths'.format(N, M))
plt.xlabel('X(t)')
plt.ylabel('X(t)')
plt.xlim(-250, 250)
## (-250.0, 250.0)
## (-250.0, 250.0)
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.