Skip to main content

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

ArgumentRequiredColumnsDescription
edgesyes
  • src, dst: Int32, Int64, Utf8, LargeUtf8, Utf8View
  • weight (optional): Float32, Float64
  • edge_id (optional): Int32, Int64
edge relation with canonical src and dst columns, plus optional weight and edge_id columns
vertex_attributesno
  • vertex: Int32, Int64, Utf8, LargeUtf8, Utf8View
  • radius, mobility, mass (optional): Float32
optional per-vertex ForceAtlas2 attributes; at least one attribute column is required
initial_positionsno
  • vertex: Int32, Int64, Utf8, LargeUtf8, Utf8View
  • x, y: Float32
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

OptionTypeDefaultConstraintsDescription
barnes_hut_optimizebooleanfalseuse the Barnes-Hut ForceAtlas2 approximation
barnes_hut_thetanumber0.5min 0; max 1Barnes-Hut speed and accuracy trade-off
edge_weight_influencenumber1min 0non-negative edge weight influence
gravitynumber1min 0non-negative ForceAtlas2 gravity strength
jitter_tolerancenumber1min 0non-negative ForceAtlas2 jitter tolerance
lin_log_modebooleanfalseuse the ForceAtlas2 lin-log attraction model
max_iterinteger500min 1; max 2147483647ForceAtlas2 iteration limit
outbound_attraction_distributionbooleanfalsedistribute ForceAtlas2 attraction along outbound edges
overlap_scaling_rationumber2> 0positive overlap repulsion scaling ratio
prevent_overlappingbooleanfalseprevent node overlap; requires a radius attribute
scaling_rationumber2> 0positive ForceAtlas2 repulsion scaling ratio
seedinteger0min 0; max 18446744073709552000random initialization seed
strong_gravity_modebooleanfalseuse stronger gravity for distant nodes
verbosebooleanfalsewhether 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

ColumnTypeNullableDescription
vertexInt64|Utf8noVertex receiving ForceAtlas2 layout coordinates.
xFloat32noX coordinate assigned by ForceAtlas2.
yFloat32noY 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.