Metadata-Version: 2.5
Name: annnet
Version: 0.4.1
Summary: A Python tool to build, manipulate, and interoperate with flexible graph structures
Project-URL: Documentation, https://annnet.sysbioverse.org/reference/
Project-URL: Homepage, https://annnet.sysbioverse.org/
Project-URL: Issues, https://github.com/saezlab/annnet/issues
Project-URL: Repository, https://github.com/saezlab/annnet
Author-email: Youssef Zerta <youssef.zerta@biomedtech.ueuromed.org>, Daniele Bottazzi <daniele.bottazzi@mail.polimi.it>, Denes Turei <turei.denes@gmail.com>
Maintainer-email: Bottazzi Daniele <daniele.bottazzi@mail.polimi.it>, Youssef Zerta <youssef.zerta@biomedtech.ueuromed.org>
License-Expression: BSD-3-Clause
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: narwhals>=1.9
Requires-Dist: numpy>=1.24
Requires-Dist: pkg-infra>=0.1.1
Requires-Dist: scipy>=1.12
Provides-Extra: all
Requires-Dist: anndata>=0.12; extra == 'all'
Requires-Dist: cobra>=0.26; extra == 'all'
Requires-Dist: graphviz; extra == 'all'
Requires-Dist: ipython>=8.0; extra == 'all'
Requires-Dist: lxml>=4.9; extra == 'all'
Requires-Dist: matplotlib>=3.5; extra == 'all'
Requires-Dist: mudata>=0.3; extra == 'all'
Requires-Dist: narwhals>=1.9; extra == 'all'
Requires-Dist: networkx>=3.3; extra == 'all'
Requires-Dist: numcodecs>=0.12; extra == 'all'
Requires-Dist: openpyxl>=3.1; extra == 'all'
Requires-Dist: pandas>=1.5; extra == 'all'
Requires-Dist: polars; extra == 'all'
Requires-Dist: pyarrow>=10; extra == 'all'
Requires-Dist: pydot; extra == 'all'
Requires-Dist: python-igraph>=0.11; extra == 'all'
Requires-Dist: python-libsbml>=5.20; extra == 'all'
Requires-Dist: toml>=0.10; extra == 'all'
Requires-Dist: zarr>=2.18; extra == 'all'
Provides-Extra: backends
Requires-Dist: networkx>=3.3; extra == 'backends'
Requires-Dist: python-igraph>=0.11; extra == 'backends'
Provides-Extra: corneto
Requires-Dist: corneto>=1.0.0rc5; extra == 'corneto'
Requires-Dist: scipy>=1.11; extra == 'corneto'
Provides-Extra: decoupler
Requires-Dist: anndata>=0.12; extra == 'decoupler'
Requires-Dist: decoupler>=2.0; extra == 'decoupler'
Requires-Dist: pandas>=2.0; extra == 'decoupler'
Provides-Extra: excel
Requires-Dist: openpyxl>=3.1; extra == 'excel'
Provides-Extra: graphviz
Requires-Dist: graphviz; extra == 'graphviz'
Provides-Extra: igraph
Requires-Dist: python-igraph>=0.11; extra == 'igraph'
Provides-Extra: io
Requires-Dist: lxml>=4.9; extra == 'io'
Requires-Dist: numcodecs>=0.12; extra == 'io'
Requires-Dist: openpyxl>=3.1; extra == 'io'
Requires-Dist: pandas>=2.0; extra == 'io'
Requires-Dist: pyarrow>=10; extra == 'io'
Requires-Dist: python-libsbml>=5.20; extra == 'io'
Requires-Dist: toml>=0.10; extra == 'io'
Requires-Dist: zarr>=2.18; extra == 'io'
Provides-Extra: matplotlib
Requires-Dist: matplotlib>=3.5; extra == 'matplotlib'
Provides-Extra: metabo
Requires-Dist: cobra>=0.26; extra == 'metabo'
Requires-Dist: graphviz; extra == 'metabo'
Requires-Dist: lxml>=4.9; extra == 'metabo'
Provides-Extra: methods
Requires-Dist: anndata>=0.12; extra == 'methods'
Requires-Dist: corneto>=1.0.0rc5; extra == 'methods'
Requires-Dist: decoupler>=2.0; extra == 'methods'
Requires-Dist: pandas>=2.0; extra == 'methods'
Requires-Dist: scipy>=1.11; extra == 'methods'
Provides-Extra: networkx
Requires-Dist: networkx>=3.3; extra == 'networkx'
Provides-Extra: notebook
Requires-Dist: ipython>=8.0; extra == 'notebook'
Provides-Extra: pandas
Requires-Dist: pandas>=1.5; extra == 'pandas'
Provides-Extra: parquet
Requires-Dist: pyarrow>=10; extra == 'parquet'
Provides-Extra: plot
Requires-Dist: graphviz; extra == 'plot'
Requires-Dist: matplotlib>=3.5; extra == 'plot'
Requires-Dist: pydot; extra == 'plot'
Provides-Extra: polars
Requires-Dist: polars>=0.20.0; extra == 'polars'
Provides-Extra: pyarrow
Requires-Dist: pyarrow>=10; extra == 'pyarrow'
Provides-Extra: pydot
Requires-Dist: pydot; extra == 'pydot'
Provides-Extra: pyg
Requires-Dist: torch-geometric>=2.5; extra == 'pyg'
Requires-Dist: torch>=2.2; extra == 'pyg'
Provides-Extra: sbml
Requires-Dist: lxml>=4.9; extra == 'sbml'
Requires-Dist: python-libsbml>=5.20; extra == 'sbml'
Provides-Extra: scverse
Requires-Dist: anndata>=0.12; extra == 'scverse'
Requires-Dist: mudata>=0.3; extra == 'scverse'
Requires-Dist: spatialdata>=0.2; extra == 'scverse'
Provides-Extra: storage
Requires-Dist: numcodecs>=0.12; extra == 'storage'
Requires-Dist: pandas>=2.0; extra == 'storage'
Requires-Dist: pyarrow>=10; extra == 'storage'
Requires-Dist: zarr>=2.18; extra == 'storage'
Provides-Extra: zarr-io
Requires-Dist: numcodecs>=0.12; extra == 'zarr-io'
Requires-Dist: zarr>=2.18; extra == 'zarr-io'
Description-Content-Type: text/markdown

# annnet - Annotated Network Data Structure for Science

[![PyPI](https://img.shields.io/pypi/v/annnet?logo=pypi&logoColor=white)](https://pypi.org/project/annnet/)
[![Python](https://img.shields.io/pypi/pyversions/annnet?logo=python)](https://pypi.org/project/annnet/)
[![Unit Tests](https://img.shields.io/github/actions/workflow/status/saezlab/annnet/ci-testing-unit.yml?branch=main&label=tests)](https://github.com/saezlab/annnet/actions/workflows/ci-testing-unit.yml)
[![codecov](https://codecov.io/gh/saezlab/annnet/branch/main/graph/badge.svg)](https://codecov.io/gh/saezlab/annnet)
[![Docs](https://img.shields.io/badge/docs_built_with-MkDocs-blue)](https://annnet.sysbioverse.org/)
[![License](https://img.shields.io/github/license/saezlab/annnet)](https://github.com/saezlab/annnet/blob/main/LICENSE)

annnet keeps a network, its annotations, and analysis contexts in one Python
object. Import an interaction table or an existing graph, attach measurements
and results, select the part you need, and use it with other graph libraries.


> 🚧 **annnet is under active development.**
> 
> Feedback and bug reports are very welcome via
> [GitHub issues](https://github.com/saezlab/annnet/issues).


## Why annnet?

![AnnNet](https://raw.githubusercontent.com/saezlab/annnet/main/docs/assets/annnet_fig1_layout.png)

annnet aims to combine graph expressiveness, annotation-centric data handling, and practical interoperability in one model:

- Rich graph semantics: directed and undirected edges, parallel edges, self-loops, hyperedges, and edge-as-entity semantic.
- Multilayer and slice-aware modeling: represent layers, aspects, inter-layer links, and named graph slices.
- Annotated tables throughout: keep structured metadata for nodes, edges, slices, layers, and graph-level state, using [Narwhals](https://narwhals-dev.github.io/narwhals/)-compatible dataframes.
- Import and export: [NetworkX](https://networkx.org/), [igraph](https://python.igraph.org/), [graph-tool](https://graph-tool.skewed.de/), [GraphML](http://graphml.graphdrawing.org/), [GEXF](https://gexf.net/), SIF, [SBML](https://sbml.org/), CX2 ([Cytoscape](https://cytoscape.org/) exchange format v2), Excel/CSV/TSV/JSON, [Parquet](https://parquet.apache.org/) graph directories, and dataframes.
- Algorithm access: lazy calls into NetworkX/igraph/graph‑tool via the graph-owned `anet.nx`, `anet.ig`, and `anet.gt` accessors.
- Native storage: persist graph structure and annotations in a `.annnet` layout built from [Zarr](https://zarr.dev/), Parquet, and JSON.


## Features

### Minimal core dependencies

[![NumPy](https://img.shields.io/badge/basic_math_with-NumPy-013243?logo=numpy&logoColor=013243)](https://numpy.org/)
[![SciPy](https://img.shields.io/badge/sparse_matrix_via-SciPy-8CAAE6?logo=scipy)](https://scipy.org/)
[![Narwhals](https://img.shields.io/badge/dataframe--agnostic_design_with-Narwhals-blue?logo=narwhals)](https://narwhals-dev.github.io/narwhals/)

The base package keeps the required runtime small. `numpy`, `scipy`, `narwhals`, and `pkg-infra` are required, which gives you the core graph model, sparse structure handling, and dataframe interoperability layer without forcing heavyweight optional backends.

### Graph modeling

- Simple graphs, directed graphs, and multigraphs
- Hyperedges, including directed source-to-target groups
- Signed and weighted relations with rich node and edge annotations
- Efficient indexing, lookup helpers, and slice-based views
- Edge-, node-, and graph-level semantics in the same container

### Data and file interoperability

[![Polars](https://img.shields.io/badge/tabular_backend-Polars-0075FF?logo=polars&logoColor=0075FF)](https://pola.rs/)
[![pandas](https://img.shields.io/badge/or-pandas-150458?logo=pandas&logoColor=150458)](https://pandas.pydata.org/)
[![PyArrow](https://img.shields.io/badge/or-PyArrow-5C2D91?logo=apachearrow&logoColor=white)](https://arrow.apache.org/docs/python/)

annnet separates tabular in-memory backends from file formats. For annotated tables in memory, it can work through Narwhals with Polars DataFrames, pandas DataFrames, or PyArrow-backed tabular objects, letting you keep the table engine that best fits the rest of your workflow.

The file-format IO modules cover graph exchange and persistence formats such as:

- GraphML, GEXF, SIF, and SBML
- CX2 (Cytoscape exchange)
- Parquet graph directories
- CSV/TSV, JSON/NDJSON, and Excel

### Runtime backend interoperability

[![NetworkX](https://img.shields.io/badge/graph_backend-NetworkX-blue?logo=networkx)](https://networkx.org/)
[![igraph](https://img.shields.io/badge/and/or-igraph-blue)](https://python.igraph.org/)
[![graph-tool](https://img.shields.io/badge/and/or-graph--tool-blue)](https://graph-tool.skewed.de/)

- NetworkX via `anet.nx` and `to_nx` / `from_nx`
- igraph via `anet.ig` and `to_igraph` / `from_igraph`
- graph-tool via `anet.gt` and `to_graphtool` / `from_graphtool`

### Cytoscape Web integration and other network visualizations

[![Cytoscape CX2](https://img.shields.io/badge/interactive_visualization_with-Cytoscape_Web-F7DF1E?logo=cytoscapedotjs)](https://cytoscape.org/)
[![Graphviz](https://img.shields.io/badge/static_plot_with-Graphviz-blue)](https://graphviz.org/)
[![pydot](https://img.shields.io/badge/or-pydot-blue)](https://github.com/pydot/pydot)
[![Matplotlib](https://img.shields.io/badge/or-Matplotlib-blue)](https://matplotlib.org/)

annnet can export CX2 for Cytoscape and also show [Cytoscape.js](https://js.cytoscape.org/) views directly from Python. That makes it practical to inspect graphs in a browser or notebook-oriented workflow without first leaving the annnet data model. For static outputs, `annnet.utils.plot` draws with Graphviz, pydot or Matplotlib, and `annnet.utils.save` writes the drawing to a file.

### Disk-backed storage

[![Zarr](https://img.shields.io/badge/matrix_stored_with-Zarr-blue)](https://zarr.readthedocs.io/)
[![Parquet](https://img.shields.io/badge/annotation_tables_stored_with-Parquet-50ABF1?logo=apacheparquet)](https://parquet.apache.org/)

The native `.annnet` format is intended to be lossless and analysis-friendly. Sparse structure is stored in Zarr, annotated tables are stored in Parquet, and metadata is stored in JSON so graphs can be reopened without flattening away structure or annotations.

## Installation

The latest stable release is published on PyPI:

[![PyPI](https://img.shields.io/pypi/v/annnet?logo=pypi&logoColor=white)](https://pypi.org/project/annnet/)
[![Status](https://img.shields.io/pypi/status/annnet)](https://pypi.org/project/annnet/)
[![Package format](https://img.shields.io/pypi/format/annnet)](https://pypi.org/project/annnet/)
[![Implementation](https://img.shields.io/pypi/implementation/annnet)](https://pypi.org/project/annnet/)

Base install:

```bash
pip install annnet
```

Optional extras let you add tabular backends, graph backends, plotting backends, and IO/storage support only when needed:

```bash
# Graph backends
pip install "annnet[networkx,igraph]"

# IO and serialization helpers
pip install "annnet[io]"

# Plotting helpers
pip install "annnet[plot]"

# More granular extras
pip install "annnet[polars]"
pip install "annnet[pandas]"
pip install "annnet[pyarrow]"
pip install "annnet[parquet]"
pip install "annnet[zarr_io]"
pip install "annnet[excel]"
pip install "annnet[sbml]"
pip install "annnet[matplotlib]"
pip install "annnet[pydot]"
pip install "annnet[graphviz]"

# Common pip-installable extras in one bundle
pip install "annnet[all]"
```

`graph-tool` is supported through the adapters and the `anet.gt` accessor when installed, but it is not distributed on PyPI. Install it through your OS or conda-based package manager and annnet will detect it.


## Quick Start

Install `annnet[polars,storage]`. From a repository checkout, import the small
[synthetic interaction table](https://github.com/saezlab/annnet/blob/main/docs/guide/data/interactions.csv):

```python
import annnet as an
import polars as pl

interactions = pl.read_csv('docs/guide/data/interactions.csv')
anet = an.from_edge_frame(
    interactions,
    edge_id='edge_id',
    sign='effect',
    directed=True,
    slice='prior',
)
anet.attrs.backend = 'polars'
anet.summary()  # 10 nodes, 11 edges

supported = anet.view(edges=anet.E.select(confidence__gte=0.85))
supported.attrs.table('edges', derived=True)

anet.attrs.update('nodes', {'ERK': {'label': 'ERK response'}})
anet.write('analysis.annnet')
restored = an.read('analysis.annnet')
```

The [quickstart](https://annnet.sysbioverse.org/quickstart/) explains the input
columns and each step. The [Guide](https://annnet.sysbioverse.org/guide/) has five
topic notebooks covering the model, annotations and views, slices, layers, and
computation and storage. Longer analyses and integrations are in
[Tutorials](https://annnet.sysbioverse.org/tutorials/).

## Backend-Specific Integration

If an optional backend is installed, annnet can dispatch directly to that backend while handling conversion for you:

```python
centrality = anet.nx.degree_centrality(anet)
```

`anet.nx.<function>` is the function `networkx.<function>`: annnet converts the graph, puts the converted graph in place of `anet` in the call, and returns the result of NetworkX. `anet.ig` and `anet.gt` do the same for igraph and graph-tool. The converted graphs are cached. After edits, call the accessor's `clear()` or use a fresh adapter conversion; automatic invalidation does not cover every mutation. See [Backend accessors](https://annnet.sysbioverse.org/reference/core/backend-accessors/).

Use `anet.nx.backend(...)`, `anet.ig.backend(...)`, or `anet.gt.backend(...)` when you want the concrete projected graph object. Use `anet.nx.<function>(anet, ...)`, `anet.ig.<function>(anet, ...)`, or `anet.gt.<namespace>.<function>(anet, ...)` when you want conversion plus dispatch in one step.

Examples:

```python
# Operate on the active slice
bc = anet.nx.betweenness_centrality(anet)

# Build a projected NetworkX graph explicitly
nx_graph = anet.nx.backend(
    directed=True,
    hyperedge_mode='skip',  # or "expand"
    slice='prior',
    simple=True,
)

# igraph interoperability
pagerank = anet.ig.pagerank()
```


## Interoperability

High-fidelity conversions aim to preserve IDs, attributes, and directionality. When a target format is inherently lossy, annnet exposes manifests that can be reused during re-import to recover as much structure as possible.

Two adapter families are involved:

- Runtime backend adapters for in-memory conversion and algorithm dispatch with NetworkX, igraph, and graph-tool
- IO adapters for file formats and tabular exchange such as GraphML, GEXF, SIF, SBML, CX2, Parquet, Excel, JSON, and Narwhals-backed dataframes

NetworkX:

```python
import annnet as an

nx_graph, manifest = an.adapters.to_nx(anet, directed=True, hyperedge_mode='skip')

# ... run algorithms or edit nx_graph ...

from_networkx = an.adapters.from_nx(nx_graph, manifest)
```

igraph:

```python
ig_graph, ig_manifest = an.adapters.to_igraph(anet, directed=True, hyperedge_mode='skip')
from_igraph = an.adapters.from_igraph(ig_graph, ig_manifest)
```

File formats and dataframes:

```python
import annnet as an

# GraphML / GEXF / SIF
# What the format cannot hold goes into a sidecar file beside it
an.io.to_graphml(anet, 'graph.graphml', directed=True, hyperedge_mode='reify')
from_graphml = an.io.from_graphml('graph.graphml')

an.io.to_sif(anet, 'graph.sif')
from_sif = an.io.from_sif('graph.sif')

# JSON / NDJSON
an.io.to_json(anet, 'graph.json')
from_json = an.io.from_json('graph.json')

# Parquet graph directory
an.io.to_parquet(anet, 'graph_dir/')
from_parquet = an.io.from_parquet('graph_dir/')

# CX2 (Cytoscape exchange format v2)
cx2 = an.io.to_cx2(anet, hyperedges='reify')
from_cx2 = an.io.from_cx2(cx2, hyperedges='manifest')
```

Notes:

- Hyperedges may be dropped, expanded, or reified depending on the target and conversion mode.
- Backend graphs are single-view projections, so slice handling should be chosen explicitly when it matters.
- Returned manifests and sidecar files improve round-trip fidelity when the external target cannot encode all annnet semantics directly. See [What each format keeps](https://annnet.sysbioverse.org/reference/io/format-comparison/).

## Storage

The `.annnet` format is the native persisted representation for annnet graphs. It is designed to preserve structure, attributes, slices, and metadata in a layout that is both lossless and inspectable on disk.

```python
import annnet as an

an.io.write(anet, 'my_graph.annnet', overwrite=True)
restored = an.io.read('my_graph.annnet')
```

Layout highlights:

- `manifest.json` stores versioning, counts, and slice metadata
- `structure/incidence.zarr` stores sparse topology data
- `structure/*.parquet` stores structural indices and edge definitions
- `tables/*.parquet` stores node, edge, and slice annotations
- `slices/`, `audit/`, `uns/` and, for a multilayer graph, `layers/` keep slice state, history, unstructured metadata and multilayer state

## Package Overview

annnet is organized in layers. Most users interact with the high-level graph API, while adapters, IO, and backend-specific helpers stay separated underneath so graph semantics do not get tangled with storage or third-party conversions.

```text
annnet/
├── core/              # Main Graph API, managers, views, and backend accessors
├── adapters/          # In-memory conversion layers for external graph libraries
├── io/                # File readers/writers and native .annnet persistence
├── algorithms/        # Algorithms implemented against AnnNet data-structure
└── utils/             # Plotting and other shared utilities
```

### Internal design

- Sparse incidence matrix for the core topology
- Attributes decoupled from structure through annotated tables
- Lazy conversion to external backends on demand
- Optional caching for converted backend graphs

### Philosophy

- One consistent interface across multiple graph families
- Interoperability first, not ecosystem replacement
- Performance-aware design with room for richer semantics
- Modular internals rather than a monolithic graph object

## Development

### Local dev and test setup

[![uv](https://img.shields.io/badge/package_manager-uv-DE5FE9?logo=uv)](https://github.com/astral-sh/uv)
[![Pixi](https://img.shields.io/badge/secondary_env_manager-Pixi-orange)](https://pixi.sh/)

For local development, `uv` is the lightest path for syncing dependency groups and running day-to-day checks. Pixi is the heavier but more complete route when you need the broader environment, including graph-tool where supported.

```bash
# Dev tools
uv sync --group dev

# Test dependencies
uv sync --group tests
uv run pytest
```

For the full Pixi environment:

```bash
pixi install
pixi run test-all
```

Notes:

- `graph-tool` is only available on selected platforms.
- The Pixi configuration includes Python and graph-tool-aware test tasks for the supported platforms.

### Docs setup

Use the module entrypoint for MkDocs in this repository:

```bash
uv sync --group docs
uv run --group docs python -m mkdocs serve --dev-addr 127.0.0.1:8000
```

Open <http://127.0.0.1:8000>. The build executes the five Guide notebooks and
renders saved outputs for the Tutorials. Run `uv run --group docs python -m mkdocs
build --strict` to validate the site. See the [documentation contribution
guide](https://annnet.sysbioverse.org/community/contribute-docs/) for faster previews and notebook environments.

## Contributing

Contributions are welcome, especially around public API, interoperability, file adapters, and algorithms. For changes that affect graph semantics or round-trip behavior, include tests that demonstrate the intended behavior and guard against regressions.

[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-FAB040?logo=pre-commit)](https://pre-commit.com/)
[![Ruff](https://img.shields.io/badge/linting_&_formatting_with-Ruff-D7FF64?logo=ruff)](https://github.com/astral-sh/ruff)
[![tox](https://img.shields.io/badge/tested%20with-tox-darkgray)](https://tox.wiki/)

Useful contributor tooling includes `pre-commit`, `ruff`, `black`, `pytest`, and `tox`. If you are changing IO or backend conversion code, it is worth testing both happy-path import/export and round-trip fidelity.

### CI and repository activity

[![Lint](https://img.shields.io/github/actions/workflow/status/saezlab/annnet/ci-linting.yml?branch=main&label=lint)](https://github.com/saezlab/annnet/actions/workflows/ci-linting.yml)
[![Security](https://img.shields.io/github/actions/workflow/status/saezlab/annnet/ci-security.yml?branch=main&label=security)](https://github.com/saezlab/annnet/actions/workflows/ci-security.yml)
[![Docs Build](https://img.shields.io/github/actions/workflow/status/saezlab/annnet/ci-docs.yml?branch=main&label=docs%20build)](https://github.com/saezlab/annnet/actions/workflows/ci-docs.yml)
[![Issues](https://img.shields.io/github/issues/saezlab/annnet)](https://github.com/saezlab/annnet/issues)
[![Pull Requests](https://img.shields.io/github/issues-pr/saezlab/annnet)](https://github.com/saezlab/annnet/pulls)
[![Last Commit](https://img.shields.io/github/last-commit/saezlab/annnet)](https://github.com/saezlab/annnet/commits/main)

The repository uses GitHub Actions for testing, linting, docs, and security checks. Open issues and pull requests are the best place to discuss design questions or proposed changes before a larger refactor.

## License

annnet is licensed under the [BSD-3](https://opensource.org/license/bsd-3-clause) License.
