Metadata-Version: 2.4
Name: domino-mesoscale
Version: 0.2.0
Summary: DOMINO, Detection Of MesoscopIc structures via iNfOrmation criteria
Author-email: Mattia Marzi <mattia.marzi@imtlucca.it>
License-Expression: MIT
Project-URL: Homepage, https://github.com/mattiamarzi/DOMINO
Project-URL: Repository, https://github.com/mattiamarzi/DOMINO
Keywords: networks,mesoscales,mesoscale-detection,sbm,leiden,bic
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Requires-Dist: networkx>=3.0
Requires-Dist: scipy>=1.10
Requires-Dist: numba>=0.57
Provides-Extra: viz
Requires-Dist: matplotlib>=3.7; extra == "viz"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: pre-commit>=3.7; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5; extra == "dev"
Dynamic: license-file

# DOMINO - Detection Of MesoscopIc structures via iNfOrmation criteria

DOMINO is a mesoscale detection framework that combines **Leiden optimization**
with **Bayesian Information Criterion (BIC) minimization** under several
Stochastic Block Model (SBM) likelihoods.

Supported modes:

- **binary**: SBM, dcSBM
- **signed**: signed SBM, signed dcSBM (positive, negative, absent)
- **weighted**: geometric-weight SBM, and its degree-corrected variant

The binary package also exposes conjugate-prior marginal likelihoods, Jeffreys-
prior criteria, and an experimental pointwise-Fisher IBIC under
`domino.bic_minimization`. IBIC overestimates the number of communities on the
G10 benchmark and is retained for reproducibility, not recommended for detection.
See `docs/math.md` and `notebooks/tests_jeffreys_ibic.ipynb`.

The recommended public entry point is `detect`.
It starts from a greedy-modularity partition by default; pass
`initial_partition=None` to opt into singleton initialization.

## Installation

Install from PyPI:

```bash
pip install domino-mesoscale
```

```python
from domino import detect
```

Run tests:

```bash
python -m pip install -e ".[dev]"
pytest -q
```

## Input conventions

DOMINO currently models undirected networks without self-loops. Matrix rows and
columns must follow graph nodes `0, ..., N-1` in the same order. Binary and
signed layers are interpreted through their support; weighted inputs must be
nonnegative. In signed mode, positive and negative layers must be disjoint for
each dyad.

Set the `THREADS` environment variable before importing DOMINO to limit Numba's
thread pool. Small workloads automatically use serial Numba kernels when thread
scheduling would be slower.

## Quickstart

### Binary SBM (non degree-corrected)

```python
import numpy as np
import networkx as nx
from domino import detect

rng = np.random.default_rng(0)
A = (rng.random((100, 100)) < 0.05).astype(int)
A = np.triu(A, 1)
A = A + A.T
G = nx.from_numpy_array(A)

res = detect(G, A, mode="binary", degree_corrected=False, max_outer=1)
part, bic = res["partition"], res["bic"]
print("K:", len(part), "BIC:", bic)
```

### Signed SBM (non degree-corrected)

You can pass a single signed matrix `A` with negative entries.

```python
import numpy as np
import networkx as nx
from domino import detect

rng = np.random.default_rng(1)
n = 100
A = np.zeros((n, n), dtype=float)

# Two blocks with positive internal edges
A[:50, :50] = (rng.random((50, 50)) < 0.10).astype(float)
A[50:, 50:] = (rng.random((50, 50)) < 0.10).astype(float)

# Negative cross-block edges
A[:50, 50:] = -(rng.random((50, 50)) < 0.08).astype(float)
A[50:, :50] = A[:50, 50:].T
np.fill_diagonal(A, 0.0)
A = np.triu(A, 1)
A = A + A.T

G = nx.from_numpy_array((np.abs(A) > 0).astype(int))
res = detect(G, A, mode="signed", degree_corrected=False, max_outer=1)
part, bic = res["partition"], res["bic"]
print("K:", len(part), "BIC:", bic)
```

### Weighted SBM (geometric, non degree-corrected)

```python
import numpy as np
import networkx as nx
from domino import detect

rng = np.random.default_rng(2)
W = rng.poisson(2.0, size=(100, 100)).astype(float)
W = np.triu(W, 1)
W = W + W.T
np.fill_diagonal(W, 0.0)

G = nx.from_numpy_array((W > 0).astype(int))
res = detect(G, W, mode="weighted", degree_corrected=False, max_outer=1)
part, bic = res["partition"], res["bic"]
print("K:", len(part), "BIC:", bic)
```

## Documentation

- Usage notes: `docs/usage.md`
- Mathematical details: `docs/math.md`
- API quick reference: `docs/api_quick_reference.md`

## Continuous integration

GitHub Actions workflow: `.github/workflows/ci.yml`

## License

See `LICENSE`.
