Berlin Tech Meetup: The Future of Relational Foundation Models, Systems, and Real-World Applications

Register now:
PyG/Guide7 min read

Dynamic Graphs: Evolving Nodes and Edges Over Time

A dynamic graph changes structure over time. Nodes join and leave, edges form and dissolve. GNNs on dynamic graphs must adapt to this evolution, capturing not just the current state but the trajectory of change.

PyTorch Geometric

TL;DR

  • 1A dynamic graph evolves as nodes and edges are added or removed. Unlike static graphs, the topology itself changes over time: new users join, products are discontinued, relationships form and dissolve.
  • 2Snapshot-based approach: divide time into windows, create a static graph per window, process with GNN + sequence model across snapshots. Simple but loses fine-grained resolution.
  • 3Event-based approach: process each structural change as it arrives, updating representations incrementally. More complex but captures exact timing of topology changes.
  • 4Enterprise systems are inherently dynamic. A recommendation model trained on last month's product catalog misses new products. Dynamic GNNs adapt to structural drift.
  • 5PyG supports both approaches: standard Data/HeteroData for snapshots, TemporalData for event streams. The choice depends on your latency requirements and update frequency.

A dynamic graph is a graph that evolves as nodes and edges are added or removed over time. The structure itself changes: new users join a social network, products are added to a catalog, fraudulent accounts are created and suspended. A static graph is a frozen snapshot. A dynamic graph is a living system.

This distinction matters because real-world enterprise systems never stop changing. A fraud detection model trained on January's graph structure may miss new fraud patterns that emerge in February when new account types appear and new transaction pathways form.

Two approaches to dynamic graphs

Snapshot-based (discrete-time)

Divide time into fixed windows and create one static graph per window:

snapshot_dynamic_graph.py
import torch
from torch_geometric.data import Data

# Create weekly snapshots of a social network
snapshots = []
for week in range(52):
    # Get edges active during this week
    edges = get_edges_for_week(week)
    features = get_node_features_for_week(week)

    snapshot = Data(
        x=features,
        edge_index=edges,
    )
    snapshots.append(snapshot)

# Process each snapshot with GNN, then model sequence
# GNN output per snapshot -> RNN/Transformer across time
embeddings = [gnn(snap.x, snap.edge_index) for snap in snapshots]
temporal_output = sequence_model(torch.stack(embeddings))

Snapshot approach: one static graph per time window, processed by GNN, then a sequence model captures evolution.

Event-based (continuous-time)

Process each structural change as it arrives:

  • Node addition: initialize the new node's embedding (zero, random, or from features)
  • Edge addition: update the representations of both endpoints
  • Node/edge deletion: remove from the graph, propagate updates to affected neighbors

This is more complex but captures exact timing and avoids the information loss of discretization. TGN (Temporal Graph Network) is the primary event-based architecture in PyG.

Enterprise example: evolving product catalog

An e-commerce platform adds 1,000 new products per week and discontinues 200. The user-product interaction graph is inherently dynamic:

  • Cold start: new products have no interaction history. Dynamic GNNs can initialize their embeddings from product features and propagate information from similar existing products.
  • Trend detection: a product suddenly getting 10x more interactions than last week signals a trend. Comparing snapshots reveals this velocity.
  • Decay: discontinued products should fade from recommendations. In a static graph, they persist forever. In a dynamic graph, the removal is explicit.
dynamic_product_graph.py
# Handling new products in a dynamic graph
def update_graph(graph, new_products, new_interactions, removed_products):
    # Add new product nodes with feature-based initialization
    for product in new_products:
        graph.add_node(product.id, features=product.feature_vector)

    # Add new interaction edges
    for interaction in new_interactions:
        graph.add_edge(interaction.user_id, interaction.product_id,
                      timestamp=interaction.time, type=interaction.action)

    # Remove discontinued products
    for product_id in removed_products:
        graph.remove_node(product_id)
        # Neighbors automatically lose this connection

    # Re-run GNN on updated subgraph (incremental)
    affected_nodes = get_affected_neighborhood(new_products + new_interactions)
    updated_embeddings = gnn.forward_incremental(graph, affected_nodes)
    return updated_embeddings

Incremental graph updates. Only affected neighborhoods are recomputed, not the entire graph.

Challenges of dynamic graphs

  • Catastrophic forgetting: as the graph evolves, the model may forget patterns from earlier time periods. Combining memory modules with periodic retraining mitigates this.
  • Cold start: new nodes have no neighborhood. Feature-based initialization and few-shot propagation help bootstrap embeddings for new entities.
  • Computational cost: recomputing all embeddings after every change is expensive. Incremental updates that only recompute affected neighborhoods make dynamic GNNs practical at scale.
  • Evaluation: standard train/test splits do not work. You must evaluate on future time periods using only past data for training, matching the real deployment scenario.

Frequently asked questions

What is a dynamic graph?

A dynamic graph is a graph that evolves over time as nodes and edges are added, removed, or modified. Unlike a static graph that represents a fixed snapshot, a dynamic graph captures structural changes: new users joining a network, friendships forming and dissolving, new products being listed.

What is the difference between dynamic and temporal graphs?

Dynamic graphs focus on structural evolution: nodes and edges appearing and disappearing. Temporal graphs focus on timestamped interactions on a fixed or slowly-changing structure. In practice, the terms overlap. Dynamic emphasizes topology changes; temporal emphasizes event ordering and causal constraints.

How do you process dynamic graphs with GNNs?

Two approaches: (1) Snapshot-based: divide time into windows, create a static graph per window, process each with a GNN, then use a sequence model (RNN/Transformer) across snapshots. (2) Event-based: process each edge addition/deletion as it arrives, updating node representations incrementally. TGN is an event-based approach.

What are graph snapshots?

Graph snapshots are discrete-time views of a dynamic graph. You take the graph at regular intervals (hourly, daily, weekly) and treat each as a static graph. This simplifies processing but loses fine-grained temporal resolution. The snapshot frequency is a trade-off between resolution and computational cost.

Why do dynamic graphs matter for enterprise ML?

Enterprise systems are inherently dynamic. Customers join and leave. Products are added and discontinued. Relationships form and dissolve. A model trained on last month's static graph may miss structural changes that affect predictions. Dynamic GNNs adapt to evolving structure, catching concept drift at the graph level.

Learn more about graph ML

PyTorch Geometric is the open-source foundation for graph neural networks. Explore more layers, concepts, and production patterns.