# `Arcana.Graph.CommunityDetector`
[🔗](https://github.com/georgeguimaraes/arcana/blob/main/lib/arcana/graph/community_detector.ex#L1)

Behaviour for community detection in GraphRAG.

Community detectors partition entities into groups based on their
relationships. Arcana provides a built-in Leiden implementation,
but you can implement custom detectors for different algorithms.

## Built-in Implementations

- `Arcana.Graph.CommunityDetector.Leiden` - Leiden algorithm via Leidenfold (Rust NIF)

## Installation

To enable community detection, add `leidenfold` to your dependencies:

    defp deps do
      [
        {:arcana, "~> 1.2"},
        {:leidenfold, "~> 0.2"}
      ]
    end

Precompiled binaries are available for macOS (Apple Silicon) and Linux (x86_64, ARM64).

## Configuration

Configure your community detector in `config.exs`:

    # Default: Leiden algorithm (requires leidenfold)
    config :arcana, :graph,
      community_detector: :leiden

    # Disable community detection
    config :arcana, :graph,
      community_detector: nil

    # Custom module implementing this behaviour
    config :arcana, :graph,
      community_detector: MyApp.LouvainDetector

    # Custom module with options
    config :arcana, :graph,
      community_detector: {MyApp.LouvainDetector, resolution: 0.5}

    # Inline function
    config :arcana, :graph,
      community_detector: fn entities, relationships, opts ->
        {:ok, my_detect(entities, relationships, opts)}
      end

## Implementing a Custom Detector

Create a module that implements this behaviour:

    defmodule MyApp.LouvainDetector do
      @behaviour Arcana.Graph.CommunityDetector

      @impl true
      def detect(entities, relationships, opts) do
        resolution = Keyword.get(opts, :resolution, 1.0)
        # Run Louvain algorithm...
        {:ok, communities}
      end
    end

## Community Format

Detectors must return communities as maps with:

- `:level` - Hierarchy level (0 = finest, higher = coarser)
- `:entity_ids` - List of entity IDs in this community

# `detect`

```elixir
@callback detect(
  entities :: [map()],
  relationships :: [map()],
  opts :: keyword()
) :: {:ok, [map()]} | {:error, term()}
```

Detects communities in the entity graph.

## Parameters

- `entities` - List of entity maps with `:id` and `:name`
- `relationships` - List of relationship maps with `:source_id`, `:target_id`, `:strength`
- `opts` - Options passed from the detector configuration

## Returns

- `{:ok, communities}` - List of community maps
- `{:error, reason}` - On failure

# `detect`

Detects communities using the configured detector.

The detector can be:
- A `{module, opts}` tuple where module implements this behaviour
- A function `(entities, relationships, opts) -> {:ok, communities} | {:error, reason}`
- `nil` to skip community detection (returns empty list)

## Examples

    # With module
    detector = {Arcana.Graph.CommunityDetector.Leiden, resolution: 1.0}
    {:ok, communities} = CommunityDetector.detect(detector, entities, relationships)

    # With inline function
    detector = fn entities, _rels, _opts ->
      {:ok, [%{level: 0, entity_ids: Enum.map(entities, & &1.id)}]}
    end
    {:ok, communities} = CommunityDetector.detect(detector, entities, relationships)

    # Skip detection
    {:ok, []} = CommunityDetector.detect(nil, entities, relationships)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
