SkillAgentSearch skills...

Forceatlas2

Fastest Gephi's ForceAtlas2 graph layout algorithm implemented for Python and NetworkX

Install / Use

/learn @bhargavchippada/Forceatlas2
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ForceAtlas2 for Python

CI PyPI version Python 3.9+ Docs License: GPL v3

The fastest Python implementation of the ForceAtlas2 graph layout algorithm, with Cython optimization for 10-100x speedup. Supports NetworkX, igraph, and raw adjacency matrices.

ForceAtlas2 is a force-directed layout algorithm designed for network visualization. It spatializes weighted undirected graphs in 2D, 3D, or higher dimensions, where edge weights define connection strength. It scales well to large graphs (>10,000 nodes) using Barnes-Hut approximation (O(n log n) complexity).

Documentation · PyPI · Paper

<p align="center"> <img src="https://raw.githubusercontent.com/bhargavchippada/forceatlas2/master/examples/forceatlas2_animation.gif" alt="ForceAtlas2 layout animation — 500 nodes with 7 communities separating over 600 iterations"> </p> <p align="center"><em>500-node stochastic block model (7 communities) laid out with ForceAtlas2 LinLog mode</em></p> <p align="center"> <img width="460" height="300" src="https://raw.githubusercontent.com/bhargavchippada/forceatlas2/master/examples/geometric_graph.png" alt="Random geometric graph laid out with ForceAtlas2"> </p> <p align="center"><em>Random geometric graph (400 nodes) laid out with ForceAtlas2</em></p> <p align="center"> <img src="https://raw.githubusercontent.com/bhargavchippada/forceatlas2/master/examples/forceatlas2_3d_animation.gif" alt="ForceAtlas2 3D layout animation — 1000 nodes with 8 communities separating over 600 iterations"> </p> <p align="center"><em>1000-node stochastic block model (8 communities) laid out in 3D with ForceAtlas2 LinLog mode</em></p>

Installation

pip install fa2

For maximum performance, install with Cython (recommended):

pip install cython
pip install fa2 --no-binary fa2

To build from source:

git clone https://github.com/bhargavchippada/forceatlas2.git
cd forceatlas2
pip install cython numpy
pip install -e ".[dev]" --no-build-isolation

Dependencies

| Package | Required | Purpose | |---------|----------|---------| | numpy | Yes | Adjacency matrix handling | | scipy | Yes | Sparse matrix support | | tqdm | Yes | Progress bar | | cython | No (recommended) | 10-100x speedup | | networkx | No | NetworkX graph wrapper | | igraph | No | igraph graph wrapper | | matplotlib | No | Visualization (pip install fa2[viz]) |

Python: >= 3.9 (tested on 3.9 through 3.14)

Quick Start

Simplest — No Numpy Required

from fa2.easy import layout, visualize

# Edge list in → positions out
positions = layout([("A", "B"), ("B", "C"), ("A", "C")], mode="community")

# One call to render
visualize([("A", "B"), ("B", "C"), ("A", "C")], output="png", path="graph.png")

CLI

# Layout from JSON edge list
python -m fa2 layout edges.json --mode community -o layout.json

# Render to image
python -m fa2 render edges.csv -o graph.png

# Compute quality metrics
echo '[["A","B"],["B","C"]]' | python -m fa2 metrics

With NetworkX

import networkx as nx
import matplotlib.pyplot as plt
from fa2 import ForceAtlas2

G = nx.random_geometric_graph(400, 0.2)

forceatlas2 = ForceAtlas2(
    outboundAttractionDistribution=True,  # Dissuade hubs
    edgeWeightInfluence=1.0,
    jitterTolerance=1.0,
    barnesHutOptimize=True,
    barnesHutTheta=1.2,
    scalingRatio=2.0,
    strongGravityMode=False,
    gravity=1.0,
    verbose=True,
)

positions = forceatlas2.forceatlas2_networkx_layout(G, pos=None, iterations=2000)

nx.draw_networkx_nodes(G, positions, node_size=20, node_color="blue", alpha=0.4)
nx.draw_networkx_edges(G, positions, edge_color="green", alpha=0.05)
plt.axis("off")
plt.show()

With Raw Adjacency Matrix

import numpy as np
from fa2 import ForceAtlas2

# Create a symmetric adjacency matrix
G = np.array([
    [0, 1, 1, 0],
    [1, 0, 1, 1],
    [1, 1, 0, 0],
    [0, 1, 0, 0],
], dtype=float)

forceatlas2 = ForceAtlas2(verbose=False, seed=42)
positions = forceatlas2.forceatlas2(G, iterations=1000)
# Returns: [(x1, y1), (x2, y2), ...]

With Scipy Sparse Matrix

import scipy.sparse
from fa2 import ForceAtlas2

# For large graphs, sparse matrices are more memory-efficient
G_sparse = scipy.sparse.csr_matrix(adjacency_matrix)
forceatlas2 = ForceAtlas2(verbose=False)
positions = forceatlas2.forceatlas2(G_sparse, iterations=1000)

With igraph

import igraph
from fa2 import ForceAtlas2

G = igraph.Graph.Famous("Petersen")
forceatlas2 = ForceAtlas2(verbose=False)
layout = forceatlas2.forceatlas2_igraph_layout(G, iterations=1000)
igraph.plot(G, layout=layout)

API Reference

ForceAtlas2(**kwargs)

Create a ForceAtlas2 layout engine with the following parameters:

Behavior

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | outboundAttractionDistribution | bool | False | Dissuade hubs — distributes attraction along outbound edges so hubs are pushed to borders | | linLogMode | bool | False | Use Noack's LinLog model: F = log(1 + distance) instead of F = distance. Produces tighter community clusters | | adjustSizes | bool | False | Prevent node overlap using anti-collision forces (Gephi parity). Pass sizes or size_attr to set node radii | | edgeWeightInfluence | float | 1.0 | How much edge weights matter. 0 = all edges equal, 1 = normal, other values apply weight^influence | | normalizeEdgeWeights | bool | False | Min-max normalize edge weights to [0, 1]. Applied after inversion | | invertedEdgeWeightsMode | bool | False | Invert edge weights (w = 1/w). Applied before normalization |

Performance

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | barnesHutOptimize | bool | True | Use Barnes-Hut tree approximation for repulsion. Reduces O(n^2) to O(n log n) | | barnesHutTheta | float | 1.2 | Barnes-Hut accuracy/speed tradeoff. Lower = more accurate but slower | | jitterTolerance | float | 1.0 | How much oscillation is tolerated during convergence. Higher = faster but less precise | | backend | str | "auto" | "auto": Cython if compiled, else vectorized. "cython" / "loop": force loop-based (Cython or pure Python). "vectorized": NumPy (no BH, O(n²)) |

Tuning

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | scalingRatio | float | 2.0 | Repulsion strength. Higher = more spread out graph. Must be > 0 | | strongGravityMode | bool | False | Distance-independent gravity: constant pull regardless of distance from center | | gravity | float | 1.0 | Center attraction strength. Prevents disconnected components from drifting. Must be >= 0 |

Layout & Other

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | dim | int | 2 | Number of layout dimensions. Use 3 for 3D layouts, etc. | | seed | int/None | None | Random seed for reproducible layouts | | verbose | bool | True | Show progress bar (tqdm) and timing breakdown |

Class Methods

ForceAtlas2.inferSettings(G, **overrides)

Auto-tune parameters based on graph characteristics. Returns a configured ForceAtlas2 instance.

  • G: Any supported graph type (ndarray, sparse, networkx.Graph, igraph.Graph)
  • **overrides: Override any inferred parameter
  • Returns: ForceAtlas2 instance
fa = ForceAtlas2.inferSettings(G, verbose=False, seed=42)
pos = fa.forceatlas2(G, iterations=100)

Methods

forceatlas2(G, pos=None, iterations=100, callbacks=None, sizes=None)

Compute layout from an adjacency matrix.

  • G: numpy.ndarray or scipy.sparse matrix (must be symmetric)
  • pos: Initial positions as (N, dim) array, or None for random
  • iterations: Number of layout iterations (must be >= 1)
  • callbacks: List of callback(iteration, nodes) functions
  • sizes: Node radii as (N,) array (for adjustSizes=True)
  • Returns: List of tuples with dim elements per node

forceatlas2_networkx_layout(G, pos=None, iterations=100, weight_attr=None, callbacks=None, size_attr=None, store_pos_as=None)

Compute layout for a NetworkX graph. Supports NetworkX 2.7+ and 3.x.

  • G: networkx.Graph (undirected)
  • pos: Initial positions as {node: tuple} dict
  • weight_attr: Edge attribute name for weights
  • callbacks: List of callback(iteration, nodes) functions
  • size_attr: Node attribute name for sizes (used with adjustSizes)
  • store_pos_as: If set, saves positions as node attributes under this key
  • Returns: Dict of {node: tuple}

forceatlas2_igraph_layout(G, pos=None, iterations=100, weight_attr=None, callbacks=None, size_attr=None, store_pos_as=None)

Compute layout for an igraph graph.

  • G: igraph.Graph (must be undirected)
  • pos: Initial positions as list or (N, dim) numpy array
  • weight_attr: Edge attribute name for weights
  • callbacks: List of callback(iteration, nodes) functions
  • size_attr: Vertex attribute name for sizes
  • store_pos_as: If set, saves positions as vertex attributes
  • Returns: igraph.Layout

Advanced Usage

Repro

View on GitHub
GitHub Stars311
CategoryDevelopment
Updated2d ago
Forks88

Languages

Python

Security Score

100/100

Audited on Mar 26, 2026

No findings