Source code for att.embedding.delay
"""Time-delay estimation via Average Mutual Information."""
import numpy as np
[docs]
def estimate_delay(
X: np.ndarray,
method: str = "ami",
max_lag: int = 100,
) -> int:
"""Estimate optimal time delay for Takens embedding.
Uses first minimum of Average Mutual Information (Fraser & Swinney, 1986).
Parameters
----------
X : (n_samples,) array
method : "ami" (only option currently)
max_lag : maximum lag to consider
Returns
-------
int : optimal delay (first AMI minimum, or max_lag if no minimum found)
"""
if method != "ami":
raise ValueError(f"Unknown delay method: {method}")
n = len(X)
max_lag = min(max_lag, n // 4)
ami_values = np.zeros(max_lag)
n_bins = max(int(np.sqrt(n / 5)), 16)
# Histogram-based AMI estimation
x_min, x_max = X.min(), X.max()
x_range = x_max - x_min
if x_range == 0:
return 1
# Bin edges
edges = np.linspace(x_min - 1e-10, x_max + 1e-10, n_bins + 1)
for lag in range(1, max_lag):
x_now = X[: n - lag]
x_lag = X[lag: n]
# Joint and marginal histograms
h_joint, _, _ = np.histogram2d(x_now, x_lag, bins=edges)
h_x = np.histogram(x_now, bins=edges)[0]
h_y = np.histogram(x_lag, bins=edges)[0]
# Normalize
n_pairs = len(x_now)
p_joint = h_joint / n_pairs
p_x = h_x / n_pairs
p_y = h_y / n_pairs
# Mutual information
mi = 0.0
for i in range(n_bins):
for j in range(n_bins):
if p_joint[i, j] > 0 and p_x[i] > 0 and p_y[j] > 0:
mi += p_joint[i, j] * np.log(p_joint[i, j] / (p_x[i] * p_y[j]))
ami_values[lag] = mi
# Find first minimum
for lag in range(2, max_lag - 1):
if ami_values[lag] < ami_values[lag - 1] and ami_values[lag] < ami_values[lag + 1]:
return lag
# No minimum found — return lag at 1/e of AMI(1)
if ami_values[1] > 0:
threshold = ami_values[1] / np.e
for lag in range(2, max_lag):
if ami_values[lag] < threshold:
return lag
return max_lag // 2