Force Directed Graph

a force-directed graph simulation built with d3.js library to visualize maths olympiad contests, problems, fields, and subfields as a connected network.•

HTMLCSSJavaScriptD3.js

This interactive data visualization is built with D3.js. I wanted to see if the abstract relationships between olympiad math competition problems would reveal any visual structure when modeled as a network. The result is a force-directed graph that connects 67 problems across three prestigious competitions: IMO (International Mathematical Olympiad), APMO (Asian Pacific Mathematical Olympiad), and PUTNAM (William Lowell Putnam Mathematical Competition).

1. The Dataset

To build this, a custom dataset was compiled by hand from past competition papers. Each problem’s metadata is stored in data_math_olympiad.json with the following format:

{
"id": "IMO-2024-P2",
"contest": "IMO",
"host": "Bath, United Kingdom",
"year": 2024,
"position": 2,
"field": ["Number Theory"],
"subfield": ["Elementary Number Theory"],
"statement": "Determine all pairs $(a, b)$ of positive integers for which ...",
"image_ref": "image.png"
}

In total, the dataset spans 3 contests, 67 problems, 22 subfields, and 4 major mathematical fields: Number Theory (26), Combinatorics (24), Algebra (21), and Geometry (18). Mathematical notation is preserved in LaTeX format throughout, and I included an embedded image reference for problem 5 of the 2023 IMO.

2. Building the Graph

A Python script node-link.py transforms the flat JSON into a relational graph schema by extracting the unique entities (contests, problems, fields, and subfields) and mapping the connections between them. This yields 96 nodes and 298 links across three specific relationship types: contest-problem (67), problem-field (89), and problem-subfield (142).

Crucially, the main fields are deliberately not linked to their subfields. Connecting them would impose a rigid top-down taxonomy on the network. Hence, omitting those structural edges allows the clustering to emerge naturally from the problems themselves, which I think makes for a far more interesting graph to look at.

3. Physics & Interaction

The layout engine relies on a finely tuned D3 force simulation that balances four distinct forces to achieve equilibrium:

  • Link force treats each edge as a spring following Hooke’s law F = -k(x - L), where k controls spring stiffness and L defines the rest length. For example, contest-problem links use L = 100 and k = 0.6, which keeps problems orbiting tightly around their respective contest nodes.
  • Charge force pushes every node away from all other node, mimicking same-sign particles. This electrostatic repulsion strength is calibrated to balance link attraction, controlling the overall spread of the network.
  • Centering force gently nudges the graph’s collective center of mass towards the middle of the viewport without disturbing how the nodes sit relative to each other.
  • Collision force assigns a physical radius to every node based on its entity type, so that they can settle cleanly next to one another instead of overlapping.

When you drag a node and release it, simulation.alphaTarget().restart() briefly spikes the simulation’s internal “temperature,” reheating the system so it can respond to the disturbance. From there, alpha decay gradually cools everything back down until the forces settle into balance again. No matter how much you pull the graph apart, it always finds its way back to a stable shape, and somewhere in that shape, the clustering does end up telling you something about how these problems relate to each other.