Leidenfold
Elixir bindings for the Leiden community detection algorithm via libleidenalg.
The Leiden algorithm is a state-of-the-art method for detecting communities in networks. It guarantees well-connected communities and runs significantly faster than the Louvain algorithm.
Installation
Add leidenfold to your list of dependencies in mix.exs:
def deps do
[
{:leidenfold, "~> 0.2.0"}
]
endPrecompiled binaries are available for:
- macOS (Apple Silicon)
- Linux (x86_64, ARM64)
No additional setup is required for these platforms.
Building from Source
If precompiled binaries are not available for your platform, or you want to build from source, set LEIDENFOLD_BUILD=true and install the prerequisites:
macOS
```bash # Install igraph via Homebrew brew install igraph # Build and install libleidenalg git clone https://github.com/vtraag/libleidenalg.git cd libleidenalg mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/.local make -j4 make install # Build leidenfold from source LEIDENFOLD_BUILD=true mix deps.compile leidenfold ```Linux (Ubuntu/Debian)
```bash # Install igraph sudo apt-get install libigraph-dev # Build and install libleidenalg git clone https://github.com/vtraag/libleidenalg.git cd libleidenalg mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/.local make -j4 make install # Build leidenfold from source LEIDENFOLD_BUILD=true mix deps.compile leidenfold ```Linux (Fedora/RHEL)
```bash # Install igraph sudo dnf install igraph-devel # Build and install libleidenalg git clone https://github.com/vtraag/libleidenalg.git cd libleidenalg mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/.local make -j4 make install # Build leidenfold from source LEIDENFOLD_BUILD=true mix deps.compile leidenfold ```Usage
# Basic usage with edge tuples
# Two triangles connected by edge 2-3:
# 0 3
# / \ / \
# 1---2 --------- 4---5
edges = [{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5}, {5, 3}, {2, 3}]
{:ok, result} = Leidenfold.detect_from_edges(edges)
# => {:ok, %{membership: [0, 0, 0, 1, 1, 1], n_communities: 2, quality: 0.0}}
# Nodes 0,1,2 form community 0; nodes 3,4,5 form community 1
# Same graph using source/target lists (pairs source[i] -> target[i])
sources = [0, 1, 2, 3, 4, 5, 2]
targets = [1, 2, 0, 4, 5, 3, 3]
{:ok, result} = Leidenfold.detect(sources, targets)
# With options
{:ok, result} = Leidenfold.detect(sources, targets,
objective: :modularity, # or :cpm, :rber, :rbc, :significance, :surprise
resolution: 1.0,
iterations: 2,
seed: 42
)
# Weighted edges
weighted_edges = [{0, 1, 1.0}, {1, 2, 2.0}, {2, 0, 1.5}]
{:ok, result} = Leidenfold.detect_from_weighted_edges(weighted_edges)
# Bang version that raises on error
result = Leidenfold.detect!(sources, targets)Quality Functions
:cpm- Constant Potts Model (default). Good for finding communities at different resolutions.:modularity- Classic modularity optimization.:rber- Reichardt-Bornholdt with Erdos-Renyi null model.:rbc- Reichardt-Bornholdt with configuration model null model.:significance- Significance-based community detection.:surprise- Surprise-based community detection.
Options
| Option | Type | Default | Description |
|---|---|---|---|
:weights | [float] | nil | Edge weights |
:n_nodes | integer | auto | Number of nodes (inferred if not specified) |
:directed | boolean | false | Whether graph is directed |
:objective | atom | :cpm | Quality function to optimize |
:resolution | float | 1.0 | Resolution parameter for CPM/RBER/RBC |
:iterations | integer | 2 | Number of optimization iterations |
:seed | integer | 0 | Random seed (0 = random) |
Result
The detect functions return a map with:
:membership- List of community assignments (0-indexed):n_communities- Number of communities found:quality- Quality function value (modularity, CPM score, etc.)
Contributing
Dependencies
Leidenfold builds against these native libraries (statically linked in precompiled binaries):
- igraph 0.10.15
- libleidenalg 0.11.1
Development Setup
To build and test locally:
# Install build dependencies (macOS)
brew install cmake automake autoconf libtool bison flex
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Clone the repo
git clone https://github.com/georgeguimaraes/leidenfold.git
cd leidenfold
# Build igraph from source (static)
git clone --depth 1 --branch 0.10.15 https://github.com/igraph/igraph.git
cd igraph && mkdir build && cd build
cmake .. \
-DCMAKE_INSTALL_PREFIX="$HOME/.local" \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DBUILD_SHARED_LIBS=OFF \
-DIGRAPH_USE_INTERNAL_BLAS=ON \
-DIGRAPH_USE_INTERNAL_LAPACK=ON \
-DIGRAPH_USE_INTERNAL_ARPACK=ON \
-DIGRAPH_USE_INTERNAL_GLPK=ON \
-DIGRAPH_USE_INTERNAL_GMP=ON
make -j$(sysctl -n hw.ncpu)
make install
cd ../..
# Build libleidenalg from source (static)
git clone --depth 1 --branch 0.11.1 https://github.com/vtraag/libleidenalg.git
cd libleidenalg && mkdir build && cd build
cmake .. \
-DCMAKE_INSTALL_PREFIX="$HOME/.local" \
-DCMAKE_PREFIX_PATH="$HOME/.local" \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DBUILD_SHARED_LIBS=OFF
make -j$(sysctl -n hw.ncpu)
make install
cd ../..
# Build and test
mix deps.get
LEIDENFOLD_BUILD=true \
LEIDENFOLD_STATIC_LIB_PATH="$HOME/.local/lib" \
LEIDENFOLD_STATIC_INCLUDE_PATH="$HOME/.local/include" \
mix testCI Pipeline
The CI pipeline runs on GitHub Actions:
- Test job - Runs on Linux with OTP 26/27/28 and Elixir 1.17/1.18/1.19 (8 combinations)
- Build and Publish - Triggered on version tags (
v*), builds precompiled NIFs:- macOS ARM64 (Apple Silicon)
- Linux x86_64
- Linux ARM64
- Release - Uploads artifacts to GitHub Releases
- Checksums - Auto-generates and commits checksum file
- Hex Publish - Publishes to hex.pm
NIFs are built for NIF versions 2.15, 2.16, and 2.17 to support OTP 22+.
License
MIT