Metadata-Version: 2.4
Name: scalednap
Version: 1.0.2
Summary: Community detection by persistence maximisation (Milano algorithm): persistence probability, NAP and Scaled-NAP measures, C++20 core.
Author-email: Alessandro Avellone <alessandro.avellone@unimib.it>, Paolo Bartesaghi <paolo.bartesaghi@unimi.it>, Stefano Benati <stefano.benati@unitn.it>, Rosanna Grassi <rosanna.grassi@unimib.it>
License-Expression: GPL-2.0-or-later
Project-URL: Paper (2025), https://doi.org/10.1016/j.ins.2025.123032
Project-URL: Paper (2023), https://doi.org/10.1007/s10288-023-00559-z
Project-URL: Source Code, https://github.com/aavellone/scalednap-python
Project-URL: Bug Tracker, https://github.com/aavellone/scalednap-python/issues
Keywords: community-detection,networks,graph,clustering,persistence
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: C++
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: networkx
Requires-Dist: networkx>=2.6; extra == "networkx"

# scalednap (Python)

Community detection by persistence maximisation (Milano algorithm).
Python bindings for the same C++20 core used by the command-line tool and
by the R package `scalednap`: given the same master seed, the three
interfaces produce bit-identical results.

## Measures (parameter `H0`)

| `H0`        | measure                                            |
|-------------|----------------------------------------------------|
| `None`      | persistence probability                            |
| `0` (default) | null-adjusted persistence (NAP)                  |
| `(0, 1]`    | scaled null-adjusted persistence (Scaled-NAP, sNAP_alpha), alpha = `H0` |

## Install

```bash
pip install .
```

Requires a C++20 compiler. No runtime dependencies.

## Usage

```python
import scalednap

vertex = [1, 2, 3, 4, 5, 6, 7]
edges = [(1, 2), (1, 3), (1, 4), (2, 3), (3, 4), (4, 5), (5, 6), (5, 7), (6, 7)]

res = scalednap.cluster_milano(vertex, edge_list=edges, seed=42, n_restarts=10)
res["membership"]       # 1-based cluster labels
res["score"]            # objective value
res["seed"]             # master seed (reproduces the whole experiment)
res["seed_winning"]     # exact 64-bit seed of the winning restart (str)

# Reproduce the single winning restart directly:
again = scalednap.cluster_milano(vertex, edge_list=edges, seed=res["seed_winning"])
assert again["membership"] == res["membership"]

# Score an existing partition:
scalednap.global_persistence(vertex, edge_list=edges,
                               membership=[1, 1, 1, 1, 2, 2, 2])
# Score a single cluster (binary incidence vector):
scalednap.local_persistence(vertex, edge_list=edges,
                              cluster=[1, 1, 1, 1, 0, 0, 0])
```

`networkx` and `igraph` graph objects are accepted directly in place of the
vertex/edge lists (no dependency: duck-typed).

## Seeds and reproducibility

- `seed=<int>` is the **master seed**; restart seeds are derived in C++ via
  SplitMix64, identically to the CLI and to the R package. `None` or `0`
  draws a random master, reported in the result.
- `seed="<digits>"` is an **exact 64-bit seed** (e.g. `seed_winning` from a
  previous run), used as-is; requires `n_restarts=1`.
- `num_threads > 1` enables lock-free parallel local search (Hogwild!),
  which is *not* deterministic even at a fixed seed.

## Tests

```bash
python -m unittest discover tests -v
```

## Authors

- Alessandro Avellone (University of Milano-Bicocca)
- Paolo Bartesaghi (University of Milan)
- Stefano Benati (University of Trento)
- Rosanna Grassi (University of Milano-Bicocca)
