Event2Vec learns an embedding for each discrete event so that a sequence is the sum of its events. Two geometries, one estimator: Euclidean for flat trajectories, hyperbolic (Möbius addition on the Poincaré ball) for tree-like, hierarchical data — less distortion, the same compositional loss.
Clickstreams, purchase histories, patient timelines, support tickets, system logs — a huge share of real-world data is just discrete events in order. Event2Vec turns each sequence into a single vector you can compose, compare, and explain — not another black box you can only score.
A trajectory is the sum of its events. Add embeddings
to assemble a journey; subtract them to isolate a transition — the step
from first_job to promotion is itself a
reusable “career move” vector you can apply elsewhere.
No opaque hidden state — the geometry is the model. Distance is similarity, and the vector between two events is their relationship, so you can audit and reason about a whole trajectory rather than just predict the next step.
A scikit-learn-style estimator — fit ·
transform · most_similar. Variable-length
sequences via padded batches, CPU or GPU, and
pip install event2vector to start.
Event2Vec never looks at an event on its own — it learns from sequences. Here each example is a life trajectory — birth, school, a first job, a marriage — and during training the model reads the running total so far and predicts the next stage. So a trajectory is the sum of its events, and that sum is enough to call what comes next. Click events to build a life and watch its embedding take shape on the Poincaré ball 𝔻², where each step is composed by Möbius addition and the order you live them in leaves a trace. Events are coloured by life tier — childhood, career, family, finance… — and related stages settle into the same neighbourhood of the disk. This one demo is a deliberately simple, synthetic model, here to show the mechanism cleanly — the real-data evidence is in the sections below.
career and family each settle into their own arc of the disk; early life clusters near the centre and fans outward as a life unfolds.
Because a trajectory is the sum of its events, the model's
score for any next event o splits cleanly across the past:
score(o) = Σi ei·wo + b.
Every event carries a fixed contribution e·w toward — or
away from — that outcome, so you can add them to see what a sequence is
driving at, or remove one (a counterfactual) to see what it was responsible
for. Same model as the composer above — the synthetic life-trajectory model;
here an “outcome” is a later life event, and each earlier stage's
contribution shows how strongly it pushes the model toward predicting it.
A taxonomy is a set of root→leaf paths; each node sits at the composed state of its own path. In the Euclidean plane the paths pile up near each other. In the hyperbolic Poincaré ball the recurrence folds outward from the centre, so branches fan into their own angular sectors and leaves push toward the boundary — exponential room for a tree, the property the paper leans on. Hover a node to trace its lineage to the root.
Plain addition commutes, so the Euclidean model can't tell
butter→flour from flour→butter (Δ = 0). Möbius
addition can — Δ grows to 0.25 in 16-d. That gap is exactly why
Event2Vec ships two geometries.
The recurrent state stays within ~0.7 cosine of the exact vector sum of its events, even for 50-event sequences — the additive hypothesis holds in practice, not just in theory.paper · fig. 12
On the Brown corpus it organizes grammatical structure at silhouette 0.0564 against Word2Vec's 0.0215 — more than double, with no supervision.paper · §5.2
The vectors in the demo above are a real event2vector run on
~9,000 real recipes (the 13k Kaggle dataset) — fit in seconds
on a laptop, weights baked into this page.
The composer above runs on synthetic recipes. Here is the unchanged
event2vector package on three real, public datasets —
each one fetched, sequence-built and trained by a script in code/.
Every plot below is a genuine run: Event2Vec beside a Word2Vec
baseline on the very same sequences, coloured by a ground-truth
taxonomy the model never saw.
from event2vector import Event2Vec # events: list[list[int]] — variable-length token sequences model = Event2Vec( num_event_types=len(vocab), geometry="euclidean", embedding_dim=128, pad_sequences=True, num_epochs=50, ) model.fit(train_sequences, verbose=True) train_emb = model.transform(train_sequences) # gensim-style nearest-neighbour lookup model.most_similar("bake", topn=3) # → [("fry", .93), ("boil", .91), ("cheese", .67)] # cooking events cluster
Full derivation of the additive recurrence loss, padded-batch training, and the Brown-corpus POS evaluation are in Sulc, A. Event2Vec: A Geometric Approach to Learning Composable Representations of Event Sequences, arXiv:2509.12188.
Reference implementation, the Brown-corpus POS pipeline, and a minimal runnable example live in the public repository.
@article{sulc2025event2vec,
title = {Event2Vec: A Geometric Approach to Learning
Composable Representations of Event Sequences},
author = {Sulc, Antonin},
journal = {arXiv preprint arXiv:2509.12188},
year = {2025}
}