Skip to main content

Vinnicombe nu-gap metric and a pipeline for comparing time-course data across two conditions

Project description

# nugap

**Detect condition-specific changes in *dynamics* using the Vinnicombe ν-gap metric.**

[![PyPI](https://img.shields.io/pypi/v/nugap.svg)](https://pypi.org/project/nugap/)

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/comparativechrono/nugap/blob/main/tutorial\_nugap.ipynb)

nugap is a lightweight Python implementation of the **Vinnicombe ν-gap** — a

bounded (0–1) distance between linear dynamical systems from robust control

theory — together with the model identification and statistics needed to ask a

practical question of two-condition time-course data:

**Which variables, or which relationships between them, change their *dynamical

behaviour* between conditions?**

(for example wild type vs mutant, or untreated vs treated). Comparisons of

expression *level* answer a different question and are blind to changes in

timescale, gain or phase. The ν-gap is defined directly on dynamical models, so

it is not: two systems close in ν-gap behave the same way under feedback, and a

large ν-gap marks a genuine change in dynamics.

## Installation

pip install nugap

Requires Python ≥ 3.10. The core and pipelines depend only on **NumPy**, **SciPy**

and **pandas**; the plotting helpers additionally use **Matplotlib** and

**NetworkX**.

## Tutorial

A guided, runnable tutorial covers the metric, model fitting, and the two-condition

network comparison end to end:

- **Open in Colab:** https://colab.research.google.com/github/comparativechrono/nugap/blob/main/tutorial\_nugap.ipynb

- **View on GitHub:** [tutorial\_nugap.ipynb](https://github.com/comparativechrono/nugap/blob/main/tutorial\_nugap.ipynb)

## Quick start

The ν-gap between two systems (coefficients in descending powers of s):

from nugap import tf, nu\_gap



P1 = tf(\[1], \[1, 1])     # 1/(s+1)

P2 = tf(\[1], \[1, 3])     # 1/(s+3)

nu\_gap(P1, P2)           # -> 0.447   (0 = identical, 1 = maximally different)

Comparing a whole interaction network between two conditions, with replicate-based

significance:

from nugap import compare\_network



\# data\_A, data\_B: dict {variable\_name: array of shape (replicates, timepoints)}

\# t: the common time vector

edges = compare\_network(data\_A, data\_B, t, order=1, min\_r2=0.5)



edges.query("q\_global < 0.1")   # relationships rewired between conditions (FDR < 10%)

## What it provides

- **nu\_gap** — the Vinnicombe ν-gap for SISO systems in continuous or discrete

time, with an optional frequency-band restriction (band=) and a switchable

winding-number test (check\_winding=) for oscillatory data.

- **Model identification** — fit\_first\_order, fit\_model, fit\_arx,

fit\_prony, with simulation-based fit quality and an optional DC-gain floor

(min\_dc\_gain=); plus the dc\_gain helper.

- **compare\_conditions** — per-variable comparison of dynamics between two

conditions, with a fit-quality reliability flag.

- **compare\_network** — pairwise interaction-network comparison: a low-order

model is fitted to every ordered pair of variables in each condition, the ν-gap

is taken between conditions, and significance comes from a replicate-derived

empirical null with Benjamini–Hochberg FDR control.

- **Plotting** — nugap.viz (volcano plot, hub network, hub bar plot).

## How it works

For each variable or pairwise interaction, nugap fits a low-order linear

input–output model under each condition, then measures the ν-gap between the

fitted models. Because models are compared on mean-centred trajectories, the

metric reflects changes in the *relationship* — timescale, gain or phase — rather

than in absolute level. With biological replicates, the spread of within-condition

ν-gaps provides an empirical null and a per-edge noise floor, against which

between-condition changes are tested and FDR-controlled.

The models are single-input single-output, so an edge captures a pairwise

input–output relationship, not proven causation.

## Correctness

The ν-gap implementation is verified by several independent routes — exact

closed-form values, an algebraic invariance of the chordal metric, an independent

reference implementation, and the Vinnicombe robust-stability theorem — and is

cross-checked against MATLAB's gapmetric (Robust Control Toolbox), which it

reproduces to within ~10⁻⁶. The scripts are in [validation/](validation/), with

a fast subset run on every commit.

## Citing

If you use nugap in your work, please cite the software and the accompanying

methods paper:


Hearn, T. J. nugap: condition-specific changes in dynamical relationships via the

Vinnicombe ν-gap. Software archive: Zenodo, DOI: 10.5281/zenodo.XXXXXXX.



<methods paper reference — to be added>

## License

Released under the **MIT License**. Copyright © 2026 Tim Hearn. See

[LICENSE](LICENSE).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nugap-0.4.0.tar.gz (26.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nugap-0.4.0-py3-none-any.whl (25.1 kB view details)

Uploaded Python 3

File details

Details for the file nugap-0.4.0.tar.gz.

File metadata

  • Download URL: nugap-0.4.0.tar.gz
  • Upload date:
  • Size: 26.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for nugap-0.4.0.tar.gz
Algorithm Hash digest
SHA256 719817e9fd351e1cbc546c0d2bb59f127c13023233e8adab9026945be264b27d
MD5 f8b7be1a727beb404e4cc1e81b189e10
BLAKE2b-256 55b24b63e5ace3521e0120e98038ee23b39d00f511ceaa34ed23505050d2b46d

See more details on using hashes here.

File details

Details for the file nugap-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: nugap-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 25.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for nugap-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2676222429b17a992481d6fbc48fddc48517c0d2c7985c6fa31b03209b140552
MD5 89f969d7885fc6d303340468ae5ad68a
BLAKE2b-256 52d0e78796c765624fd0216c8e5a3ebbc4024e6281b684d3b8e214e191679fc8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page