Time semantics

One of the most important decisions in temporal graph modeling is how to interpret the meaning of time in your data. Raphtory supports two semantic models – events and persistence – giving you maximum flexibility in how you view and query your graphs.

Under the hood, these representations share the same data timeline. The difference is purely semantic – how you interpret updates. You can seamlessly switch between views using event_graph() and persistent_graph() without duplicating data.

Event Graphs (Discrete)

In the temporal graph literature, graphs made up of instantaneous updates are known as link streams. Use Graph() when your data represents discrete occurrences – a payment hitting an account at 2024-03-15 10:32:47, a network packet arriving at a router at 14:05:23.847, or a user clicking on a page at 09:15:02. This representation also works naturally with snapshot-based data, whether you have precise timestamps or discrete periods like daily snapshots, monthly summaries, or weekly aggregates.

Querying Event Graphs

In an Event Graph, the at(t) function returns only what happened at exactly that timestamp. Since events are instantaneous, this is most useful for coarse-grained snapshots (e.g., g.at(day_1)) where you know updates occur at specific intervals.

For most use cases, you'll query with windows: g.window(t1, t2) ("What happened during this hour?"). This captures all events within a time range, which is more practical when dealing with continuous timestamps.

Deletions in Event Graphs

Deletions have no effect in an Event Graph because every update is seen as instantaneous – there's nothing to "end". If you call delete_edge() on a Graph, the deletion itself is recorded as an event, but edges don't have duration to be terminated.

Persistent Graphs (Continuous)

Not all relationships are instantaneous. When someone becomes a director of a company in 2023, that relationship persists – if you query the graph at 2025, they're still a director unless explicitly removed. The same applies to employees in a department, tenants in a property, or social media followers. Use PersistentGraph() when your edges represent ongoing state rather than discrete events.

Querying Persistent Graphs

The key difference in a PersistentGraph is that relationships have duration. When you call pg.add_edge(t1, "Alice", "Bob"), you're saying "Alice and Bob's relationship began at t1". From that point forward, any query using pg.at(t) where t >= t1 will show them as connected, even if no further updates occurred. The relationship continues indefinitely until you explicitly end it with pg.delete_edge(t2, "Alice", "Bob"), which marks the relationship as terminated at t2.

This means you can ask questions like "who was connected at midnight on January 1st?" and get meaningful answers, even if no edges were added at exactly that time.

Note that nodes exist forever once added to a PersistentGraph. Unlike edges which can be deleted, nodes persist indefinitely from the point of their creation.

Creating a PersistentGraph

The example below shows how to create and manipulate a PersistentGraph in Raphtory:

Here we have a graph with two edges: one connecting Alice and Bob, and one connecting Bob and Charlie, and three exploded edges, one for each activation of Alice and Bob's edge and the activation of Bob and Charlie's edge. If an edge is not explicitly deleted, it is assumed to last forever.

Switching Between Semantics

Since both Graph and PersistentGraph store the same underlying timeline of updates, you can freely convert between them at any time. This is useful when you want to analyze the same data through different lenses – for example, viewing your social network as events to count daily interactions, then switching to persistent mode to see who was connected at a specific moment.

Use persistent_graph() to convert a Graph to persistent semantics, and event_graph() to convert a PersistentGraph to event semantics:

Notice how the same data produces different results depending on the semantic model. At t=4, no updates occurred, so the event view returns nothing. But in persistent mode, both edges created before t=4 are still active (Alice-Bob at t=1, Bob-Charlie at t=3). After Alice-Bob is deleted at t=5, only Bob-Charlie remains in the persistent view at t=6.

Over the next few pages, we will explore how the persistent graph works to understand its behaviour and semantics and how it can unlock some interesting analysis.