ForceAtlas2
SQL function: cugraph_force_atlas2
Official cuGraph reference: Python API
Place vertices in two dimensions with a force-directed simulation that attracts connected vertices and repels vertices from one another.
Quickstart
The call below supplies edges from registered relation target_edges with canonical src and dst columns and may include weight. Substitute your own registered relations.
SELECT * FROM cugraph_force_atlas2(edges => (SELECT src, dst FROM target_edges));
Inputs
Every relation is a named parenthesized SELECT subquery. The required edges role uses canonical src and dst columns; every role, its canonical columns, and their accepted Arrow types are listed under Relation arguments. Metadata validation resolves registered tables named in its JSON request.
Endpoint columns accept numeric Int32, Int64 vertex IDs or logical string Utf8, LargeUtf8, Utf8View vertex IDs; string vertex-identity outputs are canonicalized to Utf8 (native mapping Int64) while scores, distances, counts, coordinates, and opaque labels stay numeric. The shared vertex-ID contract is summarized in Vertex ID support; the concrete call-specific schema comes from gpu_validate_call.
Logical string side-input limitations:
- edge ID columns and edge-ID predicate side inputs are not supported for logical string graphs
Arguments and options
Relation arguments
| Argument | Required | Columns | Description |
|---|---|---|---|
edges | yes |
| edge relation with canonical src and dst columns, plus optional weight and edge_id columns |
vertex_attributes | no |
| optional per-vertex ForceAtlas2 attributes; at least one attribute column is required |
initial_positions | no |
| optional complete ForceAtlas2 initial positions |
Vertex columns of vertex_attributes, initial_positions must use the same vertex domain as edges: the integer type of src and dst, or any listed string type when the endpoints are strings. Every edge_id column must have the integer type of src and dst; string-keyed graphs accept no edge IDs.
Named value arguments
| Option | Type | Default | Constraints | Description |
|---|---|---|---|---|
barnes_hut_optimize | boolean | false | use the Barnes-Hut ForceAtlas2 approximation | |
barnes_hut_theta | number | 0.5 | min 0; max 1 | Barnes-Hut speed and accuracy trade-off |
edge_weight_influence | number | 1 | min 0 | non-negative edge weight influence |
gravity | number | 1 | min 0 | non-negative ForceAtlas2 gravity strength |
jitter_tolerance | number | 1 | min 0 | non-negative ForceAtlas2 jitter tolerance |
lin_log_mode | boolean | false | use the ForceAtlas2 lin-log attraction model | |
max_iter | integer | 500 | min 1; max 2147483647 | ForceAtlas2 iteration limit |
outbound_attraction_distribution | boolean | false | distribute ForceAtlas2 attraction along outbound edges | |
overlap_scaling_ratio | number | 2 | > 0 | positive overlap repulsion scaling ratio |
prevent_overlapping | boolean | false | prevent node overlap; requires a radius attribute | |
scaling_ratio | number | 2 | > 0 | positive ForceAtlas2 repulsion scaling ratio |
seed | integer | 0 | min 0; max 18446744073709552000 | random initialization seed |
strong_gravity_mode | boolean | false | use stronger gravity for distant nodes | |
verbose | boolean | false | whether ForceAtlas2 emits convergence information |
Graph construction options
This function requires directed=false (undirected/symmetric graph); all other graph construction options follow the shared defaults documented in Graph Construction Options.
Output
| Column | Type | Nullable | Description |
|---|---|---|---|
vertex | Int64|Utf8 | no | Vertex receiving ForceAtlas2 layout coordinates. |
x | Float32 | no | X coordinate assigned by ForceAtlas2. |
y | Float32 | no | Y coordinate assigned by ForceAtlas2. |
These are generic descriptor schemas; run gpu_validate_call to get the concrete, table-specific output schema.
Examples
This example runs on the citation network demo dataset.
Lay out an ego network on the GPU
A seed view resolves Attention Is All You Need from its title, two views
build the one-hop neighborhood around it (its ~40 references, its 110
most-cited citers, and every citation among them), and ForceAtlas2 returns
drawable coordinates. A second call (cugraph_louvain on the same edge view)
colors the clusters:
CREATE OR REPLACE VIEW attention_seed AS
SELECT paper_id FROM papers
WHERE title = 'Attention is all you need' AND year = 2017;
CREATE OR REPLACE VIEW attention_ego_nodes AS
SELECT paper_id FROM (
SELECT e.dst AS paper_id
FROM citation_edges e JOIN attention_seed s ON s.paper_id = e.src
UNION ALL
SELECT src AS paper_id FROM (
SELECT e.src, p.n_citation
FROM citation_edges_by_dst e
JOIN attention_seed s ON s.paper_id = e.dst
JOIN papers p ON p.paper_id = e.src
ORDER BY p.n_citation DESC, e.src LIMIT 110) t
UNION ALL
SELECT paper_id FROM attention_seed
) u GROUP BY paper_id;
CREATE OR REPLACE VIEW attention_ego_edges AS
SELECT e.src, e.dst
FROM citation_edges e
JOIN attention_ego_nodes a ON a.paper_id = e.src
JOIN attention_ego_nodes b ON b.paper_id = e.dst;
WITH layout AS (
SELECT vertex, x, y
FROM cugraph_force_atlas2(
edges => (SELECT src, dst FROM attention_ego_edges), max_iter => 500, seed => 42)),
community AS (
SELECT vertex, "partition"
FROM cugraph_louvain(edges => (SELECT src, dst FROM attention_ego_edges)))
SELECT l.vertex, l.x, l.y, c."partition", p.title, p.year
FROM layout l
JOIN community c ON c.vertex = l.vertex
JOIN papers p ON p.paper_id = l.vertex;
The figure below renders that query's actual output (133 rows of
(vertex, x, y, partition, title, year)) with no client-side layout; the
browser draws only what the SQL returned. Louvain's partitions correspond to
distinct research threads (labels assigned by inspecting each cluster's
members):
seed fixes the initial placement, but the parallel layout itself is not
bit-reproducible; expect different (equally valid) coordinates on each run;
on larger graphs individual vertices may land far apart. If downstream queries
must agree on positions, snapshot into the
mutable datafusion.public workspace with CREATE OR REPLACE TABLE … AS; that does not
write back to the Iceberg source catalog.
Refine a saved layout with radius and mass
The optional relations are complete vertex-domain tables. This second pass uses the first pass as its warm start and supplies one radius and mass value for every vertex. The first pass is a table snapshot rather than a view: the final query reads it twice, and a view would rerun the non-reproducible layout once per read, so the two side inputs would disagree:
-- Local workspace snapshot; this does not write to lake.citation_network.
CREATE OR REPLACE TABLE initial_layout AS
SELECT vertex, x, y
FROM cugraph_force_atlas2(
edges => (SELECT src, dst FROM attention_ego_edges), max_iter => 250, seed => 42);
CREATE OR REPLACE VIEW layout_attributes AS
SELECT vertex,
CAST(0.75 AS REAL) AS radius,
CAST(1.0 AS REAL) AS mass
FROM initial_layout;
SELECT vertex, x, y
FROM cugraph_force_atlas2(
edges => (SELECT src, dst FROM attention_ego_edges), max_iter => 250, seed => 42,
prevent_overlapping => true,
vertex_attributes => (SELECT vertex, radius, mass FROM layout_attributes),
initial_positions => (SELECT vertex, x, y FROM initial_layout));
Both side inputs reject null, duplicate, missing, or extra vertices. Radius,
mass, mobility, and coordinates are Float32; their vertex column must match
the edge endpoint domain.
Limits
- vertex_attributes and initial_positions must contain exactly one non-null row for every graph vertex
- duplicate, missing, and extra ForceAtlas2 side-input vertices are rejected at execution
- radius, mobility, mass, x, and y side-input columns must be Float32
- prevent_overlapping=true requires a radius binding
To dry-run validate relation metadata, column types, and options without execution, see gpu_validate_call.