Metadata-Version: 2.4
Name: grnlab
Version: 0.3.0
Summary: Data generator for gene regulatory networks with Hill kinetics and ground truth
Project-URL: Homepage, https://github.com/almomaa/GRNLab
Author-email: Abd AlRahman AlMomani <almomaniar@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Abd AlRahman AlMomani
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: Hill kinetics,benchmark,gene regulatory networks,synthetic data,systems biology
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.9
Requires-Dist: matplotlib>=3.5
Requires-Dist: numpy>=1.22
Requires-Dist: scipy>=1.8
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# GRNLab (Python)

A data generator for gene regulatory networks (GRNs) with Hill-function
kinetics. Every dataset comes with the ground truth that produced it: the
signed regulatory network, every kinetic parameter, the initial
conditions, the seeds, and the solver settings. GRNLab generates data; it
does not infer, recover, or score networks.

This is the Python implementation of the GRNLab MATLAB toolbox. The two
share the same functions, options, conventions, kinetics, integrators,
accuracy audit, and figure style. Seeds are reproducible within each
implementation but not across the two (different random-number streams).

## Installation

```bash
pip install grnlab
```

Requires Python 3.9 or newer, NumPy, SciPy, and Matplotlib.

## Ten lines to a dataset

```python
import grnlab as grn

grn.catalog()                                          # motifs and random topologies

S     = grn.random_network(30, topology="scalefree", seed=2)          # ground truth
model = grn.build_model(S, logic="multiplicative", seed=2)            # kinetics
sol   = grn.simulate(model, cells=50, random_ic=True,
                     noise="langevin", noise_strength=0.2, seed=2)    # data

sol.x.shape            # (501, 30, 50): samples x genes x cells
sol.S                  # the signed adjacency that generated the data
grn.plot_network(model)            # arrowheads = activation, bars = repression
grn.plot_expression(sol, cell=2)
grn.export(sol, "grn30.csv")       # + grn30_truth.csv, grn30_adjacency.csv
```

## Conventions

- `S[i, j] = +1` means gene `j` activates gene `i`, `-1` that it represses
  it, `0` no direct regulation. Row `i` lists the regulators of gene `i`.
- Gene indices are 0-based. Labels (`"G1"`, `"G2"`, ... by default) may be
  used instead of indices wherever a gene is named: perturbations,
  stimuli, screens.
- Kinetics: `dx_i/dt = production_i(x) - gamma_i x_i` with Hill functions
  `h+(x_j) = x_j^h / (K^h + x_j^h)` for activators and `1 - h+` for
  repressors, combined additively (`basal + sum k_ij R_ij`, OR-like) or
  multiplicatively (`basal + vmax_i prod_j R_ij`, AND-like). Genes without
  regulators are constitutive at `vmax_i`.
- `stages=2` gives every gene an mRNA and a protein state; regulators act
  through their proteins. The state is `[mRNA, protein]`; solutions expose
  `sol.mrna` and `sol.protein`.
- Every random draw is governed by a `seed` (default 0). Cell `k` of a
  multi-cell simulation uses `seed + k`. Pass `seed=None` to leave the
  generator unseeded.

## Motifs by name

```python
sol = grn.simulate("repressilator")                  # oscillates (preset applied)
model = grn.build_model("toggle")                    # bistable
hi = grn.steady_state(model, initial_condition=[5, 0])
lo = grn.steady_state(model, initial_condition=[0, 5])
grn.plot_motifs()                                    # gallery of all ten
```

Registered motifs: `toggle`, `repressilator`, `nar`, `par`, `ffl_coherent`,
`ffl_incoherent`, `cascade`, `sim`, `bifan`, `goodwin`.

## Perturbations, steady states, screens

```python
model = grn.build_model(grn.random_network(10, seed=3), seed=3)
sol = grn.simulate(model, knockout=3, overexpress="G7")
ko  = grn.knockout_screen(model, noise_sigma=0.05)   # ko.X: (n+1) x n, row 0 = wild type
kd  = grn.knockout_screen(model, type="knockdown")
xss, info = grn.steady_state(model, info=True)       # info["converged"], info["stable"]
```

## Time-varying stimuli

```python
on   = grn.stimulus("X", "step", baseline=0, amplitude=1, start=5)   # switch X on at t = 5
sol  = grn.simulate("ffl_incoherent", stimulus=on, duration=30)
sol.u                                                                # realised input, samples x n

pulse = grn.stimulus(0, "step", amplitude=3, start=5, stop=10)
ramp  = grn.stimulus(0, "ramp", baseline=0, amplitude=3, start=0, stop=40)
drive = grn.stimulus("G2", "sine", amplitude=3, period=12)
xss   = grn.steady_state(model, stimulus=ramp, time=20)              # stimulus held at t = 20
```

Stimuli scale (or, with `mode="add"`, add to) a gene's production rate.
The ODE integration restarts at every stimulus edge, so steps are exact.

## Two-stage mRNA/protein kinetics

```python
model = grn.build_model("repressilator", stages=2)
sol = grn.simulate(model, duration=60)
sol.x.shape, sol.state_labels        # (601, 6), ('lacI_mRNA', ..., 'cI_protein')
grn.plot_expression(sol, species="mrna")
```

## Figures

```python
fig = grn.plot_network(model, layout="layered")     # circle, force, or layered
grn.export_figure(fig, "network.pdf")               # vector; .png at 300 dpi by default
fig = grn.plot_expression(sol)
grn.export_figure(fig, "expression.png", resolution=600)
```

## Export

`grn.export(data, "name.csv")` writes the data table plus `name_truth.csv`
(edge list: source, target, sign) and `name_adjacency.csv`. `.npz` stores
the arrays and a pickled copy of the whole object under `data`; `.pkl`
stores the object.

## Documentation

The full user guide (13 chapters with worked examples, their output, and
figures) is at [almomaa.github.io/GRNLab/python](https://almomaa.github.io/GRNLab/python/).

## Function reference

| Function | Purpose |
|---|---|
| `catalog()` | List motifs and random topologies |
| `motif(name, size=4)` | Signed adjacency, labels, and preset of a classic motif |
| `random_network(n, topology, mean_degree, fraction_repressors, self_regulation, modules, within_fraction, rewire, seed, info)` | Random signed adjacency |
| `build_model(S, logic, stages, seed, params, labels, name, hill_range, gamma_range, vmax_range, k_rel_range, basal_fraction, gammap_range, beta_ratio_range)` | Kinetic model with sampled parameters |
| `perturb(model, knockout, knockdown, overexpress, knockdown_factor, overexpress_factor)` | Scale gene production |
| `stimulus(gene, type, amplitude, baseline, start, stop, period, rise, mode, function, breaks)` | Time-varying input |
| `stimulate(model, stim)` | Attach stimuli to a model |
| `simulate(model, duration, sample_dt, transient, cells, random_ic, initial_condition, solver, noise, noise_strength, dt, noise_sigma, seed, knockout, knockdown, overexpress, ..., stimulus)` | Expression time series |
| `steady_state(model, initial_condition, max_time, tol, ..., stimulus, time, info)` | Fixed point with Newton polish |
| `knockout_screen(model, type, factor, genes, noise_sigma, seed, max_time, tol, stimulus, time)` | Steady states under single-gene perturbations |
| `export(data, filename)` | `.csv` (+ ground-truth companions), `.npz`, `.pkl` |
| `plot_network`, `plot_motifs`, `plot_expression`, `export_figure` | Figures |
| `attach_handles(model)` | Rebuild handles after editing parameters by hand |

Every function has a full docstring (`help(grn.simulate)`).

## Accuracy

```bash
python -m grnlab.audit
```

runs 19 checks of the public API against closed-form results
(exponential relaxation, Hill function values, analytic steady states,
the repressilator's Hopf threshold, the toggle switch's bistability
threshold, Euler–Maruyama convergence, Ornstein–Uhlenbeck and
chemical-Langevin statistics, two-stage limits, exact step stimuli,
knockout sign agreement, seeding) and writes `AUDIT.md`.

## Development

```bash
pip install -e ".[test]"
pytest
python -m build            # sdist and wheel in dist/
```

## License

MIT. Author: Abd AlRahman AlMomani, Embry-Riddle Aeronautical University.
