Declare Capabilities¶
Seahorse evaluation profiles are gated by capability flags. Before adding a preset to benchmark examples, decide which capabilities it supports and verify them with explicit tests.
What each capability unlocks¶
Each capability is a method you implement. Implement it → its metrics turn on. Skip it → those metrics report a clean skip, never a wrong number.
log_prob() returning an exact log-likelihood
unlocks
nll_path = surrogate or monte_carlo (sample-based estimate, not a bound)
unlocks
sample_next()
unlocks
intensity_grid()
unlocks
Save/load and re-evaluation from disk come for free through the runner — there is no capability to declare.
Declaring in EventModel¶
Capabilities are declared by which methods are implemented (not raising NotImplementedError):
class MyEventModel(nn.Module):
def log_prob(self, times, locations, state, mask):
"""Exact log-likelihood — enables NLL metrics."""
...
def sample_next(self, state, t_last, n_samples=1):
"""Implement for next-event sampling; raise NotImplementedError otherwise."""
...
def intensity_grid(self, state, t_query, s_query):
"""Implement for surface diagnostics; raise NotImplementedError otherwise."""
...
When a method raises NotImplementedError, the corresponding metric is marked available: false in metrics.json with a clear reason — it is not treated as a failure.
Exact vs approximate NLL¶
NLL is only comparable across families that compute it the same way. Seahorse
labels every preset's likelihood path (EventCapabilities.nll_path: closed form,
ODE-integrated, numerical quadrature, or a sample-based approximation) and keeps
the exact and approximate tiers visibly separate in benchmark tables.
Capability Testing¶
Before adding your preset to benchmark examples, verify each claimed capability:
from seahorse import STPPEstimator, load_jsonl
train = load_jsonl("data/tiny/train.jsonl")
val = load_jsonl("data/tiny/val.jsonl")
test = load_jsonl("data/tiny/test.jsonl")
model = STPPEstimator("my_preset", device="cpu")
model.fit(train, val, test, epochs=1, batch_size=4)
# Test NLL
scores = model.evaluate(test)
assert "test_nll" in scores
# Test sampling (if claimed)
samples = model.predict_next(test[:2], n_samples=4)
assert "next_times" in samples
# Test save/load
save_dir = model.save("runs/test_save")
loaded = STPPEstimator.load(save_dir)
scores2 = loaded.evaluate(test)
assert abs(scores["test_nll"] - scores2["test_nll"]) < 1e-4
See the Testing Checklist for the full test suite.