Point Processes — Operators¶
Immutable equinox.Module process objects that bundle an intensity model with
sampling. Operators are pytrees, safe under jit / grad / vmap, and
delegate all math to the primitives — so an
operator is a convenience wrapper, never a separate implementation.
Use these when you want a process as a reusable object (fit it, sample from it, check it); use the primitives when you want a single function inside a larger model.
Temporal processes¶
The full temporal family, from constant-rate Poisson through self-exciting
Hawkes. These are the operators that inherit the goodness-of-fit mixin, so
each exposes residuals, goodness_of_fit, and compensator_curve built on
the time-rescaling theorem.
Equinox-based temporal and spatial point process operators.
HomogeneousPoissonProcess(rate: ArrayLike, observation_window: ArrayLike)
¶
Bases: GoodnessOfFitMixin, Module
Homogeneous Poisson process on [0, T].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
ArrayLike
|
Intensity |
required |
observation_window
|
ArrayLike
|
Window length |
required |
Source code in src/xtremax/point_processes/operators/temporal.py
log_prob(n_events: Int[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood :math:n \log \lambda - \lambda T.
sample(key: PRNGKeyArray, max_events: int, sample_shape: tuple[int, ...] = ()) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Draw a padded event sequence.
Returns the same (times, mask, n_events) triple as
:func:hpp_sample.
Source code in src/xtremax/point_processes/operators/temporal.py
intensity(t: Float[Array, ...]) -> Float[Array, ...]
¶
cumulative_intensity(t: Float[Array, ...]) -> Float[Array, ...]
¶
survival(t: Float[Array, ...], given_time: Float[Array, ...] | float = 0.0) -> Float[Array, ...]
¶
:math:S(t|s) = e^{-\lambda(t - s)}.
hazard(t: Float[Array, ...]) -> Float[Array, ...]
¶
inter_event_log_prob(tau: Float[Array, ...]) -> Float[Array, ...]
¶
Log density of inter-event delays τ ~ Exp(λ).
mean_residual_life(given_time: Float[Array, ...] | None = None) -> Float[Array, ...]
¶
Memorylessness: :math:1/\lambda regardless of given_time.
Source code in src/xtremax/point_processes/operators/temporal.py
predict_count(start_time: Float[Array, ...], end_time: Float[Array, ...]) -> Float[Array, ...]
¶
Expected count on [start, end].
Source code in src/xtremax/point_processes/operators/temporal.py
return_period(probability: Float[Array, ...]) -> Float[Array, ...]
¶
Time :math:\tau with :math:P(\text{event within } \tau) = p.
exceedance_log_prob(k: Int[Array, ...], start_time: Float[Array, ...], end_time: Float[Array, ...]) -> Float[Array, ...]
¶
Log-probability of more than k events on [start, end].
Source code in src/xtremax/point_processes/operators/temporal.py
InhomogeneousPoissonProcess(log_intensity_fn: Callable[[Array], Array], observation_window: ArrayLike, integrated_intensity: ArrayLike | None = None, lambda_max: ArrayLike | None = None, n_integration_points: int = 100)
¶
Bases: GoodnessOfFitMixin, LiveIntensityMixin, Module
Inhomogeneous Poisson process on [0, T].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
log_intensity_fn
|
Callable[[Array], Array]
|
Callable mapping an array of times to an array
of log-intensities (same shape). If this is itself an
|
required |
observation_window
|
ArrayLike
|
Window length |
required |
integrated_intensity
|
ArrayLike | None
|
:math: |
None
|
lambda_max
|
ArrayLike | None
|
Upper bound on :math: |
None
|
n_integration_points
|
int
|
Static grid size used by every method that falls back to quadrature. Override once here rather than plumbing kwargs through every call. |
100
|
Note
log_intensity_fn is stored as a regular PyTree leaf — not
a static field. This way, when the function is itself an
eqx.Module (e.g. an MLP), its array leaves participate in the
PyTree and are differentiable through :meth:log_prob. A plain
Python callable stored as a leaf makes the operator incompatible
with raw jax.jit / jax.vmap (functions are not valid
traced leaves) — use eqx.filter_jit / eqx.filter_vmap or
wrap the callable in an eqx.Module.
Source code in src/xtremax/point_processes/operators/temporal.py
from_fn(log_intensity_fn: Callable[[Array], Array], observation_window: Float[Array, ...], **kwargs) -> InhomogeneousPoissonProcess
classmethod
¶
Constructor alias for clarity at call sites.
Source code in src/xtremax/point_processes/operators/temporal.py
from_piecewise_constant(bin_edges: ArrayLike, rates: ArrayLike, **kwargs: Any) -> InhomogeneousPoissonProcess
classmethod
¶
Build an IPP with piecewise-constant intensity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bin_edges
|
ArrayLike
|
Sorted array of length |
required |
rates
|
ArrayLike
|
Array of length |
required |
**kwargs
|
Any
|
Forwarded to :meth: |
{}
|
Returns:
| Type | Description |
|---|---|
InhomogeneousPoissonProcess
|
Operator whose |
InhomogeneousPoissonProcess
|
class: |
InhomogeneousPoissonProcess
|
|
InhomogeneousPoissonProcess
|
so you can differentiate through them with |
InhomogeneousPoissonProcess
|
|
InhomogeneousPoissonProcess
|
sum of |
InhomogeneousPoissonProcess
|
|
Source code in src/xtremax/point_processes/operators/temporal.py
effective_integrated_intensity(a: ArrayLike | None = None, b: ArrayLike | None = None) -> Float[Array, ...]
¶
Return :math:\Lambda(b) - \Lambda(a) using live leaves.
Precedence: (1) use :attr:integrated_intensity if the caller
pinned a fixed endpoint-to-endpoint value and is asking for the
default [0, T] interval; (2) otherwise use
log_intensity_fn.integrate(a, b) when the module exposes it
(exact closed form, differentiable); (3) otherwise fall back to
quadrature with n_integration_points nodes. Never reads a
cached value for non-default intervals, since the cache holds
only :math:\Lambda(T).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
ArrayLike | None
|
Lower limit (default |
None
|
b
|
ArrayLike | None
|
Upper limit (default :attr: |
None
|
Source code in src/xtremax/point_processes/operators/temporal.py
log_prob(event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood :math:\sum_i \log \lambda(t_i) - \Lambda(T).
Uses :meth:effective_integrated_intensity so that gradients
through the intensity module's parameters flow into both the
sum-of-log-intensities and the :math:-\Lambda(T) term.
Source code in src/xtremax/point_processes/operators/temporal.py
sample(key: PRNGKeyArray, max_candidates: int) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Thinning-based sampler.
Uses :meth:effective_lambda_max so that, after an optimiser
update to the intensity module's parameters, the thinning
envelope still exceeds the live λ(t) and the sampler
remains valid.
Source code in src/xtremax/point_processes/operators/temporal.py
sample_inversion(key: PRNGKeyArray, inverse_cumulative_intensity_fn: Callable[[Array], Array], max_events: int) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Exact sampler when the inverse compensator is available.
Source code in src/xtremax/point_processes/operators/temporal.py
intensity(t: Float[Array, ...]) -> Float[Array, ...]
¶
cumulative_intensity(t: Float[Array, ...]) -> Float[Array, ...]
¶
Compensator :math:\Lambda(t) = \int_0^t \lambda(s) ds.
Prefers the intensity module's closed-form .integrate when
available, falling back to quadrature otherwise. This keeps
diagnostics (residuals, compensator curve) consistent with
:meth:log_prob after the intensity parameters have been
updated.
Source code in src/xtremax/point_processes/operators/temporal.py
survival(t: Float[Array, ...], given_time: Float[Array, ...] | float = 0.0) -> Float[Array, ...]
¶
:math:S(t|s) = \exp(-[\Lambda(t) - \Lambda(s)]).
Routed through :meth:effective_integrated_intensity so the
result agrees with :meth:log_prob (closed-form .integrate
when available; quadrature otherwise).
Source code in src/xtremax/point_processes/operators/temporal.py
hazard(t: Float[Array, ...]) -> Float[Array, ...]
¶
cumulative_hazard(t: Float[Array, ...], given_time: Float[Array, ...] | float = 0.0) -> Float[Array, ...]
¶
:math:\Lambda(t) - \Lambda(s).
Routed through :meth:effective_integrated_intensity (pinned →
closed-form .integrate → quadrature) so every integral-
consuming method uses the same integrator as :meth:log_prob.
Source code in src/xtremax/point_processes/operators/temporal.py
inter_event_log_prob(tau: Float[Array, ...], current_time: Float[Array, ...] | float = 0.0) -> Float[Array, ...]
¶
Log density of next-event delay given last event at current_time.
The compensator term is routed through
:meth:effective_integrated_intensity for consistency with
:meth:log_prob.
Source code in src/xtremax/point_processes/operators/temporal.py
predict_count(start_time: Float[Array, ...], end_time: Float[Array, ...]) -> Float[Array, ...]
¶
Expected count :math:\Lambda(\text{end}) - \Lambda(\text{start}).
Routed through :meth:effective_integrated_intensity so it
agrees exactly with :meth:log_prob's compensator (closed form
when the intensity module exposes .integrate).
Source code in src/xtremax/point_processes/operators/temporal.py
RenewalProcess(inter_event_dist: dist.Distribution, observation_window: ArrayLike, n_integration_points: int = 100)
¶
Bases: GoodnessOfFitMixin, Module
Renewal process on [0, T] with a user-supplied inter-event law.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inter_event_dist
|
Distribution
|
NumPyro Distribution for inter-event times.
Must implement |
required |
observation_window
|
ArrayLike
|
Window length |
required |
n_integration_points
|
int
|
Grid size used by compensator-based diagnostics that cannot be reduced to the hazard closed form. |
100
|
Source code in src/xtremax/point_processes/operators/renewal.py
log_prob(event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Joint event-time log-likelihood.
Source code in src/xtremax/point_processes/operators/renewal.py
sample(key: PRNGKeyArray, max_events: int) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Sample event times by accumulating iid gaps.
Source code in src/xtremax/point_processes/operators/renewal.py
intensity(t: Float[Array, ...], event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Conditional intensity λ*(t) = h(t − t_{N(t)}).
Source code in src/xtremax/point_processes/operators/renewal.py
hazard(tau: Float[Array, ...]) -> Float[Array, ...]
¶
cumulative_hazard(tau: Float[Array, ...]) -> Float[Array, ...]
¶
survival(tau: Float[Array, ...]) -> Float[Array, ...]
¶
inter_event_log_prob(tau: Float[Array, ...]) -> Float[Array, ...]
¶
Log density of a single inter-event gap.
predict_count(start_time: Float[Array, ...] | float = 0.0, end_time: Float[Array, ...] | None = None) -> Float[Array, ...]
¶
Expected event count on [start, end] via renewal equation.
Source code in src/xtremax/point_processes/operators/renewal.py
ExponentialHawkes(mu: ArrayLike, alpha: ArrayLike, beta: ArrayLike, observation_window: ArrayLike)
¶
Bases: GoodnessOfFitMixin, Module
Hawkes process with exponential excitation on [0, T].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
ArrayLike
|
Baseline rate |
required |
alpha
|
ArrayLike
|
Excitation amplitude |
required |
beta
|
ArrayLike
|
Excitation decay |
required |
observation_window
|
ArrayLike
|
Window length |
required |
Notes
The process is sub-critical (stationary) iff
:math:\alpha / \beta < 1; the sampler and log-likelihood are
well-defined regardless, but super-critical branching ratios
make the adaptive thinning envelope blow up and the sampler
will often truncate at max_events.
Source code in src/xtremax/point_processes/operators/hawkes.py
log_prob(event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood using Ozaki's O(n) recursion.
Source code in src/xtremax/point_processes/operators/hawkes.py
sample(key: PRNGKeyArray, max_events: int, max_candidates: int | None = None) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Adaptive thinning sample; envelope μ + α N(t).
Source code in src/xtremax/point_processes/operators/hawkes.py
intensity(t: Float[Array, ...], event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Conditional intensity λ*(t | H).
Source code in src/xtremax/point_processes/operators/hawkes.py
cumulative_intensity(t: Float[Array, ...], event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Compensator Λ(t | H).
Source code in src/xtremax/point_processes/operators/hawkes.py
hazard(t: Float[Array, ...], event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Hazard equals intensity for a simple point process.
Source code in src/xtremax/point_processes/operators/hawkes.py
effective_lambda_max(history: EventHistory) -> Float[Array, ...]
¶
Thinning envelope μ + α N(t) read from live parameters.
effective_integrated_intensity(event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Compensator Λ(T | H) evaluated at the window edge.
Source code in src/xtremax/point_processes/operators/hawkes.py
GeneralHawkesProcess(mu: ArrayLike, kernel: eqx.Module, observation_window: ArrayLike, n_integration_points: int = 100, max_kernel_value: ArrayLike | None = None)
¶
Bases: GoodnessOfFitMixin, Module
Hawkes process with a user-supplied kernel module.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
ArrayLike
|
Baseline rate |
required |
kernel
|
Module
|
|
required |
observation_window
|
ArrayLike
|
Window length. |
required |
n_integration_points
|
int
|
Quadrature grid size for the fallback compensator and for diagnostics. |
100
|
max_kernel_value
|
ArrayLike | None
|
Optional static upper bound on
:math: |
None
|
Source code in src/xtremax/point_processes/operators/hawkes.py
MarkedTemporalPointProcess(ground: eqx.Module, mark_distribution_fn: Callable[[Array, EventHistory], dist.Distribution], mark_dim: int | None = None, history_at_each_event: bool = True)
¶
Bases: SeparableMarkedPP
Separable marked TPP: ground process + time-/history-conditioned marks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ground
|
Module
|
Any temporal point process operator exposing
|
required |
mark_distribution_fn
|
Callable[[Array, EventHistory], Distribution]
|
Callable |
required |
mark_dim
|
int | None
|
Dimensionality of each mark. Use |
None
|
history_at_each_event
|
bool
|
Pass per-event histories to
|
True
|
Source code in src/xtremax/point_processes/operators/marked.py
observation_window: Float[Array, ...]
property
¶
Forward to the ground operator.
log_prob(event_times: Float[Array, ...], mask: Bool[Array, ...], marks: Float[Array, ...]) -> Float[Array, ...]
¶
Joint log-likelihood: ground + marks.
The ground-process log-prob signature varies across families —
Hawkes needs (times, mask), HPP needs count, etc. This
wrapper passes (times, mask) when the ground operator
accepts that signature and falls back to just the mask-derived
count for HPP-like families.
Source code in src/xtremax/point_processes/operators/marked.py
sample(key: PRNGKeyArray, max_events: int, **ground_kwargs) -> MarkedSampleResult
¶
Sample ground event times, then draw marks at each time.
Returns:
| Type | Description |
|---|---|
MarkedSampleResult
|
class: |
MarkedSampleResult
|
unpacks like the tuple it replaces. |
MarkedSampleResult
|
|
MarkedSampleResult
|
|
MarkedSampleResult
|
positions are filled with zeros. Any operator-specific |
MarkedSampleResult
|
sample kwargs (e.g. |
MarkedSampleResult
|
ground sampler) are forwarded to the ground. |
Source code in src/xtremax/point_processes/operators/marked.py
ground_log_prob(event_times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood of the ground events only.
marks_log_prob(event_times: Float[Array, ...], marks: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood of the marks only, conditional on observed times.
Source code in src/xtremax/point_processes/operators/marked.py
ThinningProcess(base: eqx.Module, retention_fn: Callable[..., Array], observation_window: ArrayLike | None = None, n_integration_points: int = 100)
¶
Bases: Module
Thinned TPP: base ground process + retention callable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
Module
|
Any temporal operator exposing |
required |
retention_fn
|
Callable[..., Array]
|
Callable
|
required |
observation_window
|
ArrayLike | None
|
Window length. Defaults to
|
None
|
n_integration_points
|
int
|
Trapezoid grid size for the retention compensator. |
100
|
Source code in src/xtremax/point_processes/operators/thinning.py
log_prob(event_times: Float[Array, ...], mask: Bool[Array, ...], marks: Float[Array, ...] | None = None, *, mark_sample_key: PRNGKeyArray | None = None, n_mark_samples: int = 8) -> Float[Array, ...]
¶
Log-likelihood of the observed (retained) events.
See the module docstring for the decomposition. Supports a
marked base via the optional marks argument — the retention
callable is then invoked with (t, history, mark) so e.g.
magnitude-gated detectors work out of the box.
For mark-dependent retention against a marked base, pass
mark_sample_key so the compensator integral Monte-Carlo-
averages retention over the base's mark distribution at each
quadrature node. Without a key, retention_fn is called with
proposed_mark=None in the compensator path (correct only
for mark-independent retention).
Source code in src/xtremax/point_processes/operators/thinning.py
sample(key: PRNGKeyArray, max_events: int, max_candidates: int | None = None, **base_kwargs) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]] | tuple[Float[Array, ...], Bool[Array, ...], Float[Array, ...]]
¶
Sample by first drawing from the base and then Bernoulli-thinning.
Two-pass draw: (1) draw a full latent realisation from the
base; (2) for each latent event evaluate the retention
probability given the retained history so far and accept
with that probability. This correctly propagates history- and
mark-dependent retention. max_candidates is forwarded to
the base's own sampler for families (Hawkes, IPP) that use
thinning internally.
Returns:
| Type | Description |
|---|---|
tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]] | tuple[Float[Array, ...], Bool[Array, ...], Float[Array, ...]]
|
|
tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]] | tuple[Float[Array, ...], Bool[Array, ...], Float[Array, ...]]
|
|
tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]] | tuple[Float[Array, ...], Bool[Array, ...], Float[Array, ...]]
|
process. The third element disambiguates the two cases. |
Source code in src/xtremax/point_processes/operators/thinning.py
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | |
Spatial processes¶
Patterns on a rectangular domain with no time dimension. Diagnostics here are
function-based rather than methods — see csr_ripleys_k and friends in the
primitives.
Equinox-based temporal and spatial point process operators.
HomogeneousSpatialPP(rate: ArrayLike, domain: RectangularDomain)
¶
Bases: Module
Homogeneous spatial Poisson process on a rectangular domain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
ArrayLike
|
Intensity |
required |
domain
|
RectangularDomain
|
Rectangular domain |
required |
Source code in src/xtremax/point_processes/operators/hpp_spatial.py
n_dims: int
property
¶
Spatial dimension d (forwarded from the domain).
log_prob(locations: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Joint log-likelihood of an observed CSR pattern.
Uses the Janossy form n log λ − λ|D| (the -log n! and
-n log |D| normalisers are deliberately dropped so the
result is directly comparable to the IPP/Hawkes log-probs; see
:func:~xtremax.point_processes.primitives.hpp_spatial.hpp_spatial_log_prob).
locations is consumed only through its event count via
mask — under CSR the location density is constant.
Source code in src/xtremax/point_processes/operators/hpp_spatial.py
count_log_prob(n_events: Int[Array, ...]) -> Float[Array, ...]
¶
Marginal Poisson-count log-pmf log P(N = n).
sample(key: PRNGKeyArray, max_events: int) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Draw a padded (locations, mask, n_events) realisation.
Source code in src/xtremax/point_processes/operators/hpp_spatial.py
intensity(locations: Float[Array, ...] | None = None) -> Float[Array, ...]
¶
Pointwise intensity (constant). locations is optional.
Source code in src/xtremax/point_processes/operators/hpp_spatial.py
ripleys_k(r: Float[Array, ...]) -> Float[Array, ...]
¶
l_function(r: Float[Array, ...]) -> Float[Array, ...]
¶
pair_correlation(r: Float[Array, ...]) -> Float[Array, ...]
¶
predict_count(subdomain: RectangularDomain | None = None) -> tuple[Float[Array, ...], Float[Array, ...]]
¶
Mean and variance of the count over a sub-domain (defaults to D).
Source code in src/xtremax/point_processes/operators/hpp_spatial.py
predict_nearest_neighbor_distance() -> Float[Array, ...]
¶
Expected nearest-neighbour distance under CSR.
InhomogeneousSpatialPP(log_intensity_fn: Callable[[Array], Array], domain: RectangularDomain, integrated_intensity: ArrayLike | None = None, lambda_max: ArrayLike | None = None, n_integration_points: int = 2048, integration_method: SpatialMethod = 'qmc')
¶
Bases: Module
Inhomogeneous spatial Poisson process on a rectangular domain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
log_intensity_fn
|
Callable[[Array], Array]
|
Callable mapping |
required |
domain
|
RectangularDomain
|
Rectangular domain |
required |
integrated_intensity
|
ArrayLike | None
|
Optional fixed value of |
None
|
lambda_max
|
ArrayLike | None
|
Optional upper bound on |
None
|
n_integration_points
|
int
|
Static node count for quadrature (trapezoid total nodes or QMC samples). |
2048
|
integration_method
|
SpatialMethod
|
|
'qmc'
|
Source code in src/xtremax/point_processes/operators/ipp_spatial.py
n_dims: int
property
¶
Spatial dimension d.
effective_integrated_intensity(subdomain: RectangularDomain | None = None) -> Float[Array, ...]
¶
Compute :math:\Lambda(A) for A = subdomain (default D).
Precedence: (1) pinned :attr:integrated_intensity for the
default domain only; (2) quadrature against the live
log_intensity_fn otherwise. Sub-domain queries always
re-integrate so they stay consistent with the live intensity.
Source code in src/xtremax/point_processes/operators/ipp_spatial.py
effective_lambda_max() -> Float[Array, ...]
¶
Return the thinning bound used by :meth:sample.
Prefers the pinned :attr:lambda_max; otherwise queries the
intensity module's .max_intensity() if it has one (e.g. a
:class:~xtremax.point_processes.operators.temporal.PiecewiseConstantLogIntensity-style
helper that tracks log_rates and reports
exp(max(log_rates)) live). Raises when neither is
available — Lewis–Shedler thinning requires a true upper
bound, and no quadrature-derived estimate (e.g. 2 Λ / |D|,
twice the mean intensity) can guarantee that for an arbitrary
intensity surface.
Source code in src/xtremax/point_processes/operators/ipp_spatial.py
log_prob(locations: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood :math:\sum \log \lambda(s_i) - \Lambda(D).
Source code in src/xtremax/point_processes/operators/ipp_spatial.py
sample(key: PRNGKeyArray, max_candidates: int) -> tuple[Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Lewis–Shedler thinning sampler with a static buffer.
Source code in src/xtremax/point_processes/operators/ipp_spatial.py
intensity(locations: Float[Array, ...]) -> Float[Array, ...]
¶
λ(s) = exp(log_intensity_fn(s)) evaluated at locations.
predict_count(subdomain: RectangularDomain | None = None) -> Float[Array, ...]
¶
Expected count over a sub-domain (defaults to the full D).
Delegates to :meth:effective_integrated_intensity so a pinned
integrated_intensity takes precedence for the full-domain
query — matching both :meth:log_prob and the spatiotemporal
operator, which already honoured the pin.
Source code in src/xtremax/point_processes/operators/ipp_spatial.py
MarkedSpatialPP(ground: object, mark_distribution_fn: Callable[[Array], dist.Distribution], mark_dim: int | None = None)
¶
Bases: SeparableMarkedPP
Separable marked spatial PP: ground process × mark distribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ground
|
object
|
Spatial PP operator exposing
|
required |
mark_distribution_fn
|
Callable[[Array], Distribution]
|
Callable |
required |
mark_dim
|
int | None
|
Static dimensionality of each mark; |
None
|
Source code in src/xtremax/point_processes/operators/marked_spatial.py
domain: RectangularDomain
property
¶
Forward to the ground operator's domain.
n_dims: int
property
¶
Spatial dimension d.
log_prob(locations: Float[Array, ...], mask: Bool[Array, ...], marks: Float[Array, ...]) -> Float[Array, ...]
¶
Joint log-likelihood: log L_ground(s) + Σ log f(mᵢ | sᵢ).
Source code in src/xtremax/point_processes/operators/marked_spatial.py
sample(key: PRNGKeyArray, max_events: int, **ground_kwargs) -> MarkedSpatialSampleResult
¶
Sample ground locations, then draw marks at each location.
Returns :class:MarkedSpatialSampleResult
(locations, mask, marks) — unpacks like the strict
three-tuple it replaces. The ground's event count is dropped;
mask already encodes which slots are real.
Source code in src/xtremax/point_processes/operators/marked_spatial.py
ground_log_prob(locations: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood of the ground locations only.
marks_log_prob(locations: Float[Array, ...], marks: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood of the marks only, conditional on locations.
Source code in src/xtremax/point_processes/operators/marked_spatial.py
intensity(locations: Float[Array, ...]) -> Float[Array, ...]
¶
Forward to the ground operator's intensity.
Source code in src/xtremax/point_processes/operators/marked_spatial.py
Spatiotemporal processes¶
Events carrying both a location and a time, on \(D \times [t_0, t_1)\) — including the ETAS-style Hawkes process for spatially spreading contagion.
Equinox-based temporal and spatial point process operators.
HomogeneousSpatioTemporalPP(rate: ArrayLike, spatial: RectangularDomain, temporal: TemporalDomain)
¶
Bases: Module
Homogeneous Poisson process on D × [t0, t1).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
ArrayLike
|
Intensity |
required |
spatial
|
RectangularDomain
|
Spatial domain. |
required |
temporal
|
TemporalDomain
|
Temporal interval. |
required |
Source code in src/xtremax/point_processes/operators/hpp_spatiotemporal.py
n_dims: int
property
¶
Spatial dimension d (forwarded from the spatial domain).
log_prob(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Janossy log-likelihood n log λ − λ |D| (t1 − t0).
Source code in src/xtremax/point_processes/operators/hpp_spatiotemporal.py
count_log_prob(n_events: Int[Array, ...]) -> Float[Array, ...]
¶
Marginal Poisson-count log-pmf with mean λ |D| T.
Source code in src/xtremax/point_processes/operators/hpp_spatiotemporal.py
sample(key: PRNGKeyArray, max_events: int) -> tuple[Float[Array, ...], Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Draw (locations, times, mask, n_events); times sorted ascending.
Source code in src/xtremax/point_processes/operators/hpp_spatiotemporal.py
intensity(locations: Float[Array, ...] | None = None, times: Float[Array, ...] | None = None) -> Float[Array, ...]
¶
Pointwise intensity (constant).
Source code in src/xtremax/point_processes/operators/hpp_spatiotemporal.py
predict_count(sub_spatial: RectangularDomain | None = None, sub_temporal: TemporalDomain | None = None) -> tuple[Float[Array, ...], Float[Array, ...]]
¶
Mean and variance of the count over a sub-slab (defaults to full slab).
Source code in src/xtremax/point_processes/operators/hpp_spatiotemporal.py
InhomogeneousSpatioTemporalPP(log_intensity_fn: Callable[[Array, Array], Array], spatial: RectangularDomain, temporal: TemporalDomain, integrated_intensity: ArrayLike | None = None, lambda_max: ArrayLike | None = None, n_integration_points: int = 4096, integration_method: SpatiotemporalMethod = 'qmc')
¶
Bases: Module
Inhomogeneous Poisson process on D × [t0, t1).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
log_intensity_fn
|
Callable[[Array, Array], Array]
|
Callable |
required |
spatial
|
RectangularDomain
|
Spatial domain. |
required |
temporal
|
TemporalDomain
|
Temporal interval. |
required |
integrated_intensity
|
ArrayLike | None
|
Optional fixed value of :math: |
None
|
lambda_max
|
ArrayLike | None
|
Optional upper bound on |
None
|
n_integration_points
|
int
|
Static node count for quadrature. |
4096
|
integration_method
|
SpatiotemporalMethod
|
|
'qmc'
|
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
effective_integrated_intensity(sub_spatial: RectangularDomain | None = None, sub_temporal: TemporalDomain | None = None) -> Float[Array, ...]
¶
Compute :math:\Lambda over a sub-slab (defaults to full slab).
Pinned :attr:integrated_intensity is honoured only when
both sub_spatial and sub_temporal are None (i.e. the
caller is asking about the originally-pinned slab); subdomain
queries always re-integrate against the live intensity.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
effective_lambda_max() -> Float[Array, ...]
¶
Return the thinning bound used by :meth:sample.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
log_prob(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Janossy log-likelihood ∑_i log λ(s_i, t_i) − Λ.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
sample(key: PRNGKeyArray, max_candidates: int) -> tuple[Float[Array, ...], Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Lewis–Shedler thinning sampler on the full slab.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
intensity(locations: Float[Array, ...], times: Float[Array, ...]) -> Float[Array, ...]
¶
λ(s, t) = exp(log_intensity_fn(s, t)) evaluated on a batch.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
predict_count(sub_spatial: RectangularDomain | None = None, sub_temporal: TemporalDomain | None = None) -> tuple[Float[Array, ...], Float[Array, ...]]
¶
Mean and variance of the count over a sub-slab.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
marginal_spatial_intensity(locations: Float[Array, ...], n_time_points: int | None = None) -> Float[Array, ...]
¶
λ_S(s) = ∫_{t0}^{t1} λ(s, t) dt via trapezoid in time.
The default grid size derives from :attr:n_integration_points
(floored at the historical default of 100), so raising the
operator's quadrature budget also refines the marginals.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
marginal_temporal_intensity(times: Float[Array, ...], n_spatial_points: int | None = None) -> Float[Array, ...]
¶
λ_T(t) = ∫_D λ(s, t) ds via Halton QMC in space.
The default node count derives from :attr:n_integration_points
(floored at the historical default of 256).
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
intensity_surface_at_time(t: Float[Array, ...], grid_size: int = 50) -> tuple[Float[Array, ...], Float[Array, ...]]
¶
Evaluate λ(s, t) on a tensor-product spatial grid at fixed t.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
pearson_residuals(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...], n_spatial_bins: int = 5, n_temporal_bins: int = 5, n_integration_points: int = 16) -> Float[Array, ...]
¶
Cell-wise Pearson residuals on a space-time grid.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
chi_square_gof(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...], n_spatial_bins: int = 5, n_temporal_bins: int = 5, n_integration_points: int = 16) -> Float[Array, ...]
¶
Pearson :math:\chi^2 statistic from binned residuals.
Source code in src/xtremax/point_processes/operators/ipp_spatiotemporal.py
MarkedSpatioTemporalPP(ground: object, mark_distribution_fn: Callable[[Array, Array], dist.Distribution], mark_dim: int | None = None)
¶
Bases: SeparableMarkedPP
Separable marked spatiotemporal PP: ground × mark distribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ground
|
object
|
Spatiotemporal PP operator exposing
|
required |
mark_distribution_fn
|
Callable[[Array, Array], Distribution]
|
Callable |
required |
mark_dim
|
int | None
|
Static dimensionality of the marks; |
None
|
Source code in src/xtremax/point_processes/operators/marked_spatiotemporal.py
spatial: RectangularDomain
property
¶
Forward to the ground operator's spatial domain.
temporal: TemporalDomain
property
¶
Forward to the ground operator's temporal interval.
log_prob(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...], marks: Float[Array, ...]) -> Float[Array, ...]
¶
Joint log-likelihood: log L_ground + Σ log f(mᵢ | sᵢ, tᵢ).
Source code in src/xtremax/point_processes/operators/marked_spatiotemporal.py
sample(key: PRNGKeyArray, max_events: int, **ground_kwargs) -> MarkedSpatiotemporalSampleResult
¶
Sample ground (locations, times, mask), then marks at each event.
Returns :class:MarkedSpatiotemporalSampleResult — unpacks like
the (locations, times, mask, marks) tuple it replaces.
Source code in src/xtremax/point_processes/operators/marked_spatiotemporal.py
ground_log_prob(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood of the ground process only.
Source code in src/xtremax/point_processes/operators/marked_spatiotemporal.py
marks_log_prob(locations: Float[Array, ...], times: Float[Array, ...], marks: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood of the marks only, conditional on (s, t).
Source code in src/xtremax/point_processes/operators/marked_spatiotemporal.py
intensity(locations: Float[Array, ...], times: Float[Array, ...]) -> Float[Array, ...]
¶
Forward to the ground operator's intensity.
Source code in src/xtremax/point_processes/operators/marked_spatiotemporal.py
SpatioTemporalHawkes(mu: ArrayLike, alpha: ArrayLike, beta: ArrayLike, sigma: ArrayLike, spatial: RectangularDomain, temporal: TemporalDomain, boundary_correction: bool = True)
¶
Bases: Module
ETAS-style separable exp-Gaussian Hawkes process.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mu
|
ArrayLike
|
Baseline rate |
required |
alpha
|
ArrayLike
|
Excitation amplitude ( |
required |
beta
|
ArrayLike
|
Temporal decay rate |
required |
sigma
|
ArrayLike
|
Spatial bandwidth |
required |
spatial
|
RectangularDomain
|
Spatial domain. |
required |
temporal
|
TemporalDomain
|
Temporal interval. |
required |
boundary_correction
|
bool
|
When |
True
|
Source code in src/xtremax/point_processes/operators/hawkes_spatiotemporal.py
intensity(s: Float[Array, ...], t: Float[Array, ...], history_locations: Float[Array, ...], history_times: Float[Array, ...], history_mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Conditional intensity :math:\lambda^*(s, t \mid \mathcal H_t).
Source code in src/xtremax/point_processes/operators/hawkes_spatiotemporal.py
compensator(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Closed-form compensator :math:\Lambda over the slab.
Source code in src/xtremax/point_processes/operators/hawkes_spatiotemporal.py
log_prob(locations: Float[Array, ...], times: Float[Array, ...], mask: Bool[Array, ...]) -> Float[Array, ...]
¶
Log-likelihood :math:\sum_i \log \lambda^*(s_i, t_i) - \Lambda.
Source code in src/xtremax/point_processes/operators/hawkes_spatiotemporal.py
sample(key: PRNGKeyArray, max_events: int) -> tuple[Float[Array, ...], Float[Array, ...], Bool[Array, ...], Int[Array, ...]]
¶
Ogata-style spatiotemporal thinning sample.
Source code in src/xtremax/point_processes/operators/hawkes_spatiotemporal.py
Building blocks¶
Components the processes above are assembled from: excitation kernels for Hawkes, a piecewise-constant log-intensity for non-parametric IPP fits, and the diagnostics bundle.
Equinox-based temporal and spatial point process operators.
ExponentialKernel
¶
Bases: Module
Exponential excitation kernel :math:\phi(\tau) = \alpha e^{-\beta \tau}.
Useful as a GeneralHawkesProcess kernel when you want to share
the general-kernel plumbing (e.g. with a learnable multi-peak
super-kernel made by summing modules) but still get the exact
integral for the exponential component.
PiecewiseConstantLogIntensity(bin_edges: ArrayLike, rates: ArrayLike | None = None, log_rates: ArrayLike | None = None)
¶
Bases: Module
Piecewise-constant log-intensity as an eqx.Module.
Storing bin_edges and log_rates as PyTree leaves (rather
than captured in a closure) lets them be inspected, vmapped over,
and optimised via eqx.filter_grad when used inside an
:class:InhomogeneousPoissonProcess. Parameterising the log
rates means the rate is always positive without a further
constraint — useful for unconstrained gradient-based fitting.
Source code in src/xtremax/point_processes/operators/temporal.py
integrate(a: ArrayLike, b: ArrayLike) -> Array
¶
Closed-form :math:\int_a^b \exp(\log \lambda(s))\, ds.
Implemented exactly as a sum over bin overlaps — unlike the
operator's default quadrature, this scales linearly in the
number of bins rather than the number of quadrature nodes and
has no truncation error, so eqx.filter_grad through
log_rates is exact. The :class:InhomogeneousPoissonProcess
operator prefers this method over quadrature whenever the
intensity module exposes it and the caller has not pinned a
fixed integrated_intensity.
Source code in src/xtremax/point_processes/operators/temporal.py
max_intensity() -> Array
¶
Live λ_max = max(exp(log_rates)) from the current leaves.
Reading this from log_rates every call means thinning
samplers stay safe even after log_rates has been updated by
an optimiser.
Source code in src/xtremax/point_processes/operators/temporal.py
GoodnessOfFit
¶
Bases: NamedTuple
Bundle of diagnostics returned by goodness_of_fit.
Attributes:
| Name | Type | Description |
|---|---|---|
residuals |
Float[Array, ...]
|
Time-rescaled inter-event residuals :math: |
mask |
Bool[Array, ...]
|
Real-event mask aligned with |
ks_statistic |
Float[Array, ...]
|
Kolmogorov–Smirnov statistic versus |
theoretical_quantiles |
Float[Array, ...]
|
QQ-plot theoretical quantiles. |
empirical_quantiles |
Float[Array, ...]
|
QQ-plot empirical quantiles. |