Lasry–Lions Envelope¶
Definition/Proposition (Lasry–Lions Envelope).
Let h > 0. For a locally bounded positive function $F:[0,\infty) \to \mathbb{R}_{>0}$ we define its one-sided envelope $\mathcal{E}_h(F):[0,\infty) \to \mathbb{R}_{>0}$ by
$$ \mathcal{E}_h(F)(t) := \sup_{0 \le s \le t}\; F(s)\ e^{-(t-s)^2/h}. $$
In [234]:
Copied!
# Example monotonically decaying function derived from Poisson Porcess
import numpy as np
from matplotlib import pyplot as plt
f = (np.random.poisson(0.2, 500)**5).cumsum()[::-1]
plt.figure(figsize=(25,3))
plt.plot(f, 'k-', linewidth=2)
plt.title('Monotonically Decreasing Step Function')
plt.grid(True, alpha=0.3)
# Example monotonically decaying function derived from Poisson Porcess
import numpy as np
from matplotlib import pyplot as plt
f = (np.random.poisson(0.2, 500)**5).cumsum()[::-1]
plt.figure(figsize=(25,3))
plt.plot(f, 'k-', linewidth=2)
plt.title('Monotonically Decreasing Step Function')
plt.grid(True, alpha=0.3)
In [235]:
Copied!
def LL(f, h=1.0):
"""
Compute Lasry-Lions envelope of function f.
Parameters:
f: array-like, function values
h: float, envelope parameter (default 1.0)
Returns:
numpy array of envelope values
"""
f = np.array(f)
n = len(f)
envelope = np.zeros(n)
for t in range(n):
# Compute sup over s in [0, t] of F(s) * exp(-(t-s)^2/h)
s_vals = np.arange(t + 1)
weights = np.exp(-(t - s_vals)**2 / h**2)
envelope[t] = np.max(f[s_vals] * weights)
return envelope
def LL(f, h=1.0):
"""
Compute Lasry-Lions envelope of function f.
Parameters:
f: array-like, function values
h: float, envelope parameter (default 1.0)
Returns:
numpy array of envelope values
"""
f = np.array(f)
n = len(f)
envelope = np.zeros(n)
for t in range(n):
# Compute sup over s in [0, t] of F(s) * exp(-(t-s)^2/h)
s_vals = np.arange(t + 1)
weights = np.exp(-(t - s_vals)**2 / h**2)
envelope[t] = np.max(f[s_vals] * weights)
return envelope
In [236]:
Copied!
# Static plot showing original function f and smoothed envelope e
h = 40.0 # Fixed parameter value
e = LL(f, h=h) # e = smoothed envelope
plt.figure(figsize=(12, 6))
plt.plot(f, 'k-', label='F (Original Function)', linewidth=2)
plt.plot(e, 'g-', label='E (Lasry-Lions Envelope)', linewidth=2)
plt.title(f'Lasry-Lions Envelope with h = {h}')
plt.xlabel('Index')
plt.ylabel('Value')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# Static plot showing original function f and smoothed envelope e
h = 40.0 # Fixed parameter value
e = LL(f, h=h) # e = smoothed envelope
plt.figure(figsize=(12, 6))
plt.plot(f, 'k-', label='F (Original Function)', linewidth=2)
plt.plot(e, 'g-', label='E (Lasry-Lions Envelope)', linewidth=2)
plt.title(f'Lasry-Lions Envelope with h = {h}')
plt.xlabel('Index')
plt.ylabel('Value')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
In [237]:
Copied!
#
# Scale Bound inequality E(t-s)/exp(2s/h·t) ≤ E(t)
#
def ScaleBound(E, tau):
head = E[tau:] / np.exp(2 * tau / h**2 * np.arange(tau, len(E)))
tail = [0] * tau
return np.concatenate((head, tail))
h = 50.0
tau = 1
e = LL(f, h=h)
scale_bound = ScaleBound(e, tau)
plt.figure(figsize=(12, 6))
plt.plot(f, 'k-', label='f (original function)', linewidth=2, alpha=0.5)
plt.plot(e, 'g-', label='e (envelope)', linewidth=2)
plt.plot(scale_bound, 'b--', label=f'E(t-{tau})/exp(2τ/h·t)', linewidth=1, alpha=0.5)
plt.title(f'Inequality Visualization: E(t-τ)/exp(2τ/h·t) ≤ E(t) with τ={tau}, h={h}')
plt.xlabel('Index t')
plt.ylabel('Value')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# Verify the inequality holds
inequality_holds = np.all(scale_bound <= e)
print(f"Inequality E(t-{tau})/exp(2·{tau}/{h}·t) ≤ E(t) holds: {inequality_holds}")
#
# Scale Bound inequality E(t-s)/exp(2s/h·t) ≤ E(t)
#
def ScaleBound(E, tau):
head = E[tau:] / np.exp(2 * tau / h**2 * np.arange(tau, len(E)))
tail = [0] * tau
return np.concatenate((head, tail))
h = 50.0
tau = 1
e = LL(f, h=h)
scale_bound = ScaleBound(e, tau)
plt.figure(figsize=(12, 6))
plt.plot(f, 'k-', label='f (original function)', linewidth=2, alpha=0.5)
plt.plot(e, 'g-', label='e (envelope)', linewidth=2)
plt.plot(scale_bound, 'b--', label=f'E(t-{tau})/exp(2τ/h·t)', linewidth=1, alpha=0.5)
plt.title(f'Inequality Visualization: E(t-τ)/exp(2τ/h·t) ≤ E(t) with τ={tau}, h={h}')
plt.xlabel('Index t')
plt.ylabel('Value')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# Verify the inequality holds
inequality_holds = np.all(scale_bound <= e)
print(f"Inequality E(t-{tau})/exp(2·{tau}/{h}·t) ≤ E(t) holds: {inequality_holds}")
Inequality E(t-1)/exp(2·1/50.0·t) ≤ E(t) holds: True
Forward Mollification¶
Definition/Proposition (Forward Mollification). For $\rho>0$ let $K\in C_c^\infty((0,\rho))$ be a standard bump function with $K\ge0$ and $\int_0^\rho K(\tau)\,d\tau=1$. For a locally bounded $E:[0,\infty)\to(0,\infty)$, define $\mathcal{M}_\rho(E): [0,\infty)\to(0,\infty)$ as:
$$ \mathcal{M}_\rho(E)(t)\;:=\;\int_{0}^{\rho} K(\tau)\,E(t-\tau)\,d\tau. $$
where we extended the domain of definition of $E$ to negative values via $E(-t) = E(0).$
In [238]:
Copied!
def Mollify(f, rho):
"""
Compute forward mollification of function f.
Parameters:
f: array-like, function values
rho: float, mollification parameter (support width)
Returns:
numpy array of mollified values
"""
f = np.array(f)
n = len(f)
mollified = np.zeros(n)
# Create a simple bump function K on [0, rho]
# Using a normalized truncated Gaussian as approximation
tau_vals = np.linspace(0, rho, int(rho * 10) + 1) # Discretize [0, rho]
dtau = tau_vals[1] - tau_vals[0] if len(tau_vals) > 1 else rho
# Simple bump function: use exp(-1/(rho^2 - tau^2)) for tau in (0, rho)
K = np.zeros_like(tau_vals)
interior = (tau_vals > 0) & (tau_vals < rho)
K[interior] = np.exp(-1.0 / ((rho - tau_vals[interior]) * tau_vals[interior]))
# Normalize so integral = 1
integral_K = np.trapezoid(K, tau_vals,)
if integral_K > 0:
K = K / integral_K
for t in range(n):
# Compute integral of K(tau) * E(t-tau) over [0, rho]
mollified_val = 0.0
for i, tau in enumerate(tau_vals):
t_shifted = t - tau
# Extension: E(-t) = E(0) for negative arguments
if t_shifted < 0:
E_val = f[0]
elif t_shifted >= n:
E_val = f[-1] # Use last value for extrapolation
else:
# Linear interpolation for non-integer indices
t_low = int(np.floor(t_shifted))
t_high = min(t_low + 1, n - 1)
if t_low == t_high:
E_val = f[t_low]
else:
alpha = t_shifted - t_low
E_val = (1 - alpha) * f[t_low] + alpha * f[t_high]
mollified_val += K[i] * E_val * dtau
mollified[t] = mollified_val
return mollified
# Test the mollification
h = 100
e = LL(f, h)
rho = 50.0
m = Mollify(e, rho)
plt.figure(figsize=(15, 6))
plt.plot(f, 'k-', label='f (Original function)', linewidth=2, alpha=0.5)
plt.plot(e, 'g-', label=f'e (Lasry-Lions envelope, h={h})', linewidth=2)
plt.plot(m, 'b-', label=f'm (Mollified Lasry-Lions, ρ={rho})', linewidth=2)
plt.title(f'Forward Mollification with ρ = {rho}')
plt.xlabel('Index t')
plt.ylabel('Value')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
def Mollify(f, rho):
"""
Compute forward mollification of function f.
Parameters:
f: array-like, function values
rho: float, mollification parameter (support width)
Returns:
numpy array of mollified values
"""
f = np.array(f)
n = len(f)
mollified = np.zeros(n)
# Create a simple bump function K on [0, rho]
# Using a normalized truncated Gaussian as approximation
tau_vals = np.linspace(0, rho, int(rho * 10) + 1) # Discretize [0, rho]
dtau = tau_vals[1] - tau_vals[0] if len(tau_vals) > 1 else rho
# Simple bump function: use exp(-1/(rho^2 - tau^2)) for tau in (0, rho)
K = np.zeros_like(tau_vals)
interior = (tau_vals > 0) & (tau_vals < rho)
K[interior] = np.exp(-1.0 / ((rho - tau_vals[interior]) * tau_vals[interior]))
# Normalize so integral = 1
integral_K = np.trapezoid(K, tau_vals,)
if integral_K > 0:
K = K / integral_K
for t in range(n):
# Compute integral of K(tau) * E(t-tau) over [0, rho]
mollified_val = 0.0
for i, tau in enumerate(tau_vals):
t_shifted = t - tau
# Extension: E(-t) = E(0) for negative arguments
if t_shifted < 0:
E_val = f[0]
elif t_shifted >= n:
E_val = f[-1] # Use last value for extrapolation
else:
# Linear interpolation for non-integer indices
t_low = int(np.floor(t_shifted))
t_high = min(t_low + 1, n - 1)
if t_low == t_high:
E_val = f[t_low]
else:
alpha = t_shifted - t_low
E_val = (1 - alpha) * f[t_low] + alpha * f[t_high]
mollified_val += K[i] * E_val * dtau
mollified[t] = mollified_val
return mollified
# Test the mollification
h = 100
e = LL(f, h)
rho = 50.0
m = Mollify(e, rho)
plt.figure(figsize=(15, 6))
plt.plot(f, 'k-', label='f (Original function)', linewidth=2, alpha=0.5)
plt.plot(e, 'g-', label=f'e (Lasry-Lions envelope, h={h})', linewidth=2)
plt.plot(m, 'b-', label=f'm (Mollified Lasry-Lions, ρ={rho})', linewidth=2)
plt.title(f'Forward Mollification with ρ = {rho}')
plt.xlabel('Index t')
plt.ylabel('Value')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()