This page explains what a temporal point process (TPP) is and how every quantity TorchPP
computes follows from one function, the conditional intensity. Notation is
the compact reference for the same symbols. The notebook
examples/00_tpp_introduction.ipynb computes and plots
every quantity on a small model.
A TPP models sequences of events in continuous time. Each event has a time
The sequence is observed on the window
The history
In TorchPP a sequence is an MVEventData, and several sequences of different lengths form a
padded BatchedMVEventData.
seq = MVEventData(torch.tensor([0.4, 1.1, 1.3, 2.7]), torch.tensor([0, 1, 1, 0]), T_obs=3.0)
batch = BatchedMVEventData(mv_events=[seq, other_seq])The conditional intensity
The history is always implied, so we write
- A Poisson process ignores the history:
$\lambda_d(t) = \mu_d$ , a constant rate per type. - A Hawkes process lets past events raise the rate. Each event of type
$j$ at$t_i$ adds a bump that decays over time, so events cluster.
The ground intensity
In TorchPP, model.intensity(t, batch) returns (B, D). With query
times of shape (B, Q), it returns (B, Q, D).
Everything else follows from
Fix the history up to the last event
Cumulative intensity. Integrating the rate gives the expected number of events in a window, if the history stayed as it is.
In TorchPP, model.cumulative_intensity(T, batch, T_low=a) returns (B, D). For many models this integral has a closed form. For the others, TorchPP integrates
numerically.
Survival. The probability that no event happens in
Why the exponential? Split
In TorchPP, model.survival_time(t, seq) computes model.cdf_time(t, seq) computes.
Next-event density. The next event happens at
The other next-event quantities follow from this joint density.
| Quantity | Formula | TorchPP |
|---|---|---|
| joint density of time and type | density_joint(t, seq) |
|
| density of the time | density_time(t, seq) |
|
| type, given the time | prob_mark_given_time(t, seq) |
|
| type, whatever the time | prob_mark(seq) |
|
| mean and quantiles of the time | from |
time_mean(seq), time_quantile(u, seq)
|
In prob_mark* because event types are also called marks. All methods
return logarithms by default. Pass log=False for the plain values.
Queries must lie at or after the forecast origin
To fit a model we need the probability of a whole observed sequence. Chain the next-event
densities, one per event, each with the history up to the previous event, and finish with
the probability that nothing happened between the last event and
Write each density as
The first term rewards a high rate where events happened. The second term charges the model
for rate everywhere, including where nothing happened. The censored tail
In TorchPP, model.likelihood(batch) returns (B,), and
model.loss(batch) is its negative.
Where does the window start? The formula above assumes that time 0 means something, for
example admission to a hospital or birth (token_roles=TokenRoles(has_origin=True)). In many
benchmarks time 0 is arbitrary. TorchPP then treats the first event as given
(has_origin=False, the default). The window starts at model.num_scored_events(batch) counts them, and every "per event" number divides by this
count. Likelihoods from the two settings are not
comparable.
In some data each event type can occur at most once, for example a first diagnosis, a death or
a failure.
These are terminating sequences (terminating=True). After type
There are two ways to draw the next event.
-
Inversion (the default). By the time-rescaling theorem,
$\Lambda(t_0, t_\mathrm{next})$ follows an exponential distribution with rate 1. So draw$u \sim \mathrm{Exp}(1)$ , solve$\Lambda(t_0, t) = u$ for$t$ , then draw the type from$p(d \mid t)$ . It needs the cumulative intensity but no bound on the rate. -
Thinning (Ogata). Propose candidate times at a constant rate
$\bar\lambda$ that is at least the true intensity, and keep each candidate with probability$\lambda(s) / \bar\lambda$ . It only needs the intensity, but the bound must hold.
model.sample(seq, num_steps=k) repeats this Sample whose sequence is the history followed by the new events.
SamplingConfig selects the method.
Models differ only in how they compute
Hawkes processes (top) add a baseline and one kernel term per past event.
After fitting, model.baseline_values(t), model.kernel_values(dt) and
model.branching_matrix() return [target d, source j].
Neural TPPs (bottom) run an encoder (a transformer, GRU or continuous-time LSTM) over the
history. After each event it outputs a state, and a small head turns that state into
Density-based models (IntensityFree) skip the intensity. They model the density of the
time to the next event directly (a mixture of log-normals) together with a type distribution,
and provide the same density_*, prob_* and time_* methods.
Models lists every model. For your own model, see Extending.
examples/00_tpp_introduction.ipynb: every quantity on this page, computed and plotted, with sliders for the Hawkes parameters.examples/06_compare_models.ipynb: fit a Poisson process, two Hawkes processes and a neural TPP to the same data and compare them.- Getting started: train on a benchmark dataset or on your own data.
- Metrics: how models are evaluated.