Chapter 12: The Time-Rescaling Theorem and Goodness-of-Fit

Fitting a model is not enough — we must validate it. For standard regression, we check residuals. For point processes, the analog is the time-rescaling theorem, which transforms any fitted point process into i.i.d. uniform (or exponential) random variables. Testing uniformity then tells us whether the model is adequate.


12.1 The Time-Rescaling Theorem

Theorem (Brown et al., 2002): Let N be a point process with conditional intensity λ*(t). Define the rescaled times:

τᵢ = Λ*(tᵢ) = ∫₀^{tᵢ} λ*(s) ds

If the model is correctly specified (i.e., λ*(t) is the true conditional intensity), then:

  1. The τᵢ are the event times of a unit-rate Poisson process (HPP with rate 1)
  2. The inter-rescaled-times Δτᵢ = τᵢ − τᵢ₋₁ are i.i.d. Exponential(1)
  3. The uniform residuals uᵢ = 1 − exp(−Δτᵢ) are i.i.d. Uniform(0, 1)

Intuition: The time change t → Λ*(t) stretches time so that events arrive at a constant rate of 1. If your model is correct, this stretch perfectly regularizes the process.


12.2 Computing the Rescaled Times

For the Hawkes process with exponential kernel, Λ*(tᵢ) is:

def compute_rescaled_times(events, mu, alpha, beta):
    T = events[-1]
    tau = np.zeros(len(events))
    R = 0.0
    t_prev = 0.0

    for i, t in enumerate(events):
        # Integral of λ*(s) from t_prev to t:
        # = mu*(t - t_prev) + alpha/beta * R * (1 - exp(-beta*(t-t_prev)))
        # where R = Σ_{j<i} exp(-beta*(t_prev - t_j))
        dt = t - t_prev
        tau[i] = (tau[i-1] if i > 0 else 0) + \
                 mu * dt + (alpha / beta) * R * (1 - np.exp(-beta * dt))
        R = np.exp(-beta * dt) * R + 1.0   # update R for new event
        t_prev = t
    return tau

For an NHPP, Λ*(tᵢ) = ∫₀^{tᵢ} λ(s) ds computed numerically (or analytically if Λ is tractable).


12.3 The KS Test for Uniformity

The Kolmogorov-Smirnov (KS) test checks whether the uniform residuals u₁, ..., uₙ are Uniform(0, 1):

from scipy import stats

inter_tau = np.diff(np.concatenate([[0], tau]))
u = 1 - np.exp(-inter_tau)                    # uniform residuals
ks_stat, p_value = stats.kstest(u, 'uniform')

print(f"KS statistic: {ks_stat:.4f}")
print(f"p-value: {p_value:.4f}")
# p > 0.05 → fail to reject the model

The KS statistic D_n = sup_x |F_n(x) − x| where F_n is the empirical CDF of u. If p < 0.05, the model is rejected.


12.4 The QQ Plot

A complementary visual diagnostic is the QQ plot: plot the sorted uᵢ against the expected quantiles i/(n+1) for i = 1, ..., n. A well-fitted model gives points on the diagonal.

import matplotlib.pyplot as plt

u_sorted = np.sort(u)
expected = np.arange(1, len(u)+1) / (len(u) + 1)

plt.figure(figsize=(5, 5))
plt.plot(expected, u_sorted, 'o', ms=3)
plt.plot([0, 1], [0, 1], 'r--')
plt.xlabel("Expected Uniform Quantile")
plt.ylabel("Observed Rescaled Residual")
plt.title("Time-Rescaling QQ Plot")

Deviations from the diagonal indicate systematic misfit:


12.5 ACF of Rescaled Residuals

If the model is correct, the inter-rescaled-times Δτᵢ are i.i.d. — in particular, they should have zero autocorrelation. Plotting the ACF reveals residual dependence:

from statsmodels.graphics.tsaplots import plot_acf
plot_acf(inter_tau, lags=30)

What to look for:

The Ljung-Box test provides a formal test of joint significance of the first k autocorrelations:

from statsmodels.stats.diagnostic import acorr_ljungbox
result = acorr_ljungbox(inter_tau, lags=10, return_df=True)

12.6 Putting It Together: A Diagnostic Pipeline

A complete goodness-of-fit pipeline for any point process model:

  1. Fit the model → θ̂
  2. Compute τᵢ = Λ*(tᵢ; θ̂) for all observed events
  3. Compute Δτᵢ = τᵢ − τᵢ₋₁ and uᵢ = 1 − exp(−Δτᵢ)
  4. KS test: kstest(u, 'uniform')
  5. QQ plot: sorted u vs. expected quantiles
  6. ACF plot and Ljung-Box test on Δτ
  7. Interpret: which regions of time show misfit? What structure remains in the residuals?

See code/12_goodness_of_fit.py for a full diagnostic pipeline applied to HPP, NHPP, and Hawkes models.


12.7 Key Takeaways


← Chapter 11 Table of Contents Chapter 13: Advanced Simulation →