README.md

# Leidenfold

Elixir bindings for the Leiden community detection algorithm via [libleidenalg](https://github.com/vtraag/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`:

```elixir
def deps do
  [
    {:leidenfold, "~> 0.2.0"}
  ]
end
```

Precompiled 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:

<details>
<summary>macOS</summary>

```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
```
</details>

<details>
<summary>Linux (Ubuntu/Debian)</summary>

```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
```
</details>

<details>
<summary>Linux (Fedora/RHEL)</summary>

```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
```
</details>

## Usage

```elixir
# 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](https://github.com/igraph/igraph) 0.10.15
- [libleidenalg](https://github.com/vtraag/libleidenalg) 0.11.1

### Development Setup

To build and test locally:

```bash
# 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 test
```

### CI Pipeline

The CI pipeline runs on GitHub Actions:

1. **Test job** - Runs on Linux with OTP 26/27/28 and Elixir 1.17/1.18/1.19 (8 combinations)
2. **Build and Publish** - Triggered on version tags (`v*`), builds precompiled NIFs:
   - macOS ARM64 (Apple Silicon)
   - Linux x86_64
   - Linux ARM64
3. **Release** - Uploads artifacts to GitHub Releases
4. **Checksums** - Auto-generates and commits checksum file
5. **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