epilink 0.2.1a0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epilink-0.2.1a0/.github/workflows/ci.yml +59 -0
- epilink-0.2.1a0/.github/workflows/release.yml +35 -0
- epilink-0.2.1a0/.github/workflows/test-release.yml +93 -0
- epilink-0.2.1a0/.gitignore +35 -0
- epilink-0.2.1a0/.pre-commit-config.yaml +14 -0
- epilink-0.2.1a0/CITATION.cff +24 -0
- epilink-0.2.1a0/LICENSE +21 -0
- epilink-0.2.1a0/PKG-INFO +287 -0
- epilink-0.2.1a0/PUBLISHING_GUIDE.md +579 -0
- epilink-0.2.1a0/README.md +199 -0
- epilink-0.2.1a0/docs/api.md +27 -0
- epilink-0.2.1a0/docs/index.md +15 -0
- epilink-0.2.1a0/docs/usage.md +40 -0
- epilink-0.2.1a0/example_tree/transmission_tree.gml +5584 -0
- epilink-0.2.1a0/examples/grid_to_csv.py +38 -0
- epilink-0.2.1a0/examples/quickstart.py +20 -0
- epilink-0.2.1a0/mkdocs.yml +16 -0
- epilink-0.2.1a0/pyproject.toml +199 -0
- epilink-0.2.1a0/src/epilink/__init__.py +41 -0
- epilink-0.2.1a0/src/epilink/__main__.py +4 -0
- epilink-0.2.1a0/src/epilink/_version.py +34 -0
- epilink-0.2.1a0/src/epilink/cli.py +287 -0
- epilink-0.2.1a0/src/epilink/infectiousness_profile.py +884 -0
- epilink-0.2.1a0/src/epilink/simulate_epidemic_and_genomic.py +571 -0
- epilink-0.2.1a0/src/epilink/transmission_linkage_model.py +638 -0
- epilink-0.2.1a0/tests/test_cli.py +106 -0
- epilink-0.2.1a0/tests/test_profiles.py +863 -0
- epilink-0.2.1a0/tests/test_simulate_epidemic_and_genomic.py +322 -0
- epilink-0.2.1a0/tests/test_transmission_linkage_model.py +1421 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.9", "3.10", "3.11"]
|
|
14
|
+
jit: ["on", "off"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e .[dev]
|
|
29
|
+
|
|
30
|
+
- name: Lint + type-check
|
|
31
|
+
run: |
|
|
32
|
+
ruff check .
|
|
33
|
+
black --check .
|
|
34
|
+
mypy src/epilink
|
|
35
|
+
|
|
36
|
+
- name: Test
|
|
37
|
+
env:
|
|
38
|
+
NUMBA_DISABLE_JIT: ${{ matrix.jit == 'off' && '1' || '0' }}
|
|
39
|
+
run: |
|
|
40
|
+
if [ "${{ matrix.jit }}" = "off" ]; then
|
|
41
|
+
THRESH=80
|
|
42
|
+
else
|
|
43
|
+
THRESH=70
|
|
44
|
+
fi
|
|
45
|
+
pytest --cov=epilink --cov-report=xml --cov-report=term-missing \
|
|
46
|
+
--cov-fail-under=$THRESH \
|
|
47
|
+
tests/ \
|
|
48
|
+
-W error::DeprecationWarning:epilink
|
|
49
|
+
|
|
50
|
+
- name: Upload coverage to Codecov
|
|
51
|
+
if: always()
|
|
52
|
+
uses: codecov/codecov-action@v4
|
|
53
|
+
with:
|
|
54
|
+
files: ./coverage.xml
|
|
55
|
+
# Set a secret CODECOV_TOKEN for private repos; for public repos this can be omitted
|
|
56
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
57
|
+
fail_ci_if_error: false
|
|
58
|
+
flags: unittests
|
|
59
|
+
name: py${{ matrix.python-version }}-jit-${{ matrix.jit }}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment:
|
|
15
|
+
name: pypi
|
|
16
|
+
url: https://pypi.org/p/epilink
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Setup Python
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.11"
|
|
27
|
+
|
|
28
|
+
- name: Build distributions
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip build twine
|
|
31
|
+
python -m build
|
|
32
|
+
python -m twine check dist/*
|
|
33
|
+
|
|
34
|
+
- name: Publish to PyPI
|
|
35
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Test Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version_suffix:
|
|
7
|
+
description: 'PEP 440 suffix (e.g., dev123, rc1, b1). Leave blank for dev<run_number>.'
|
|
8
|
+
required: false
|
|
9
|
+
default: ''
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
publish-testpypi:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment:
|
|
19
|
+
name: testpypi
|
|
20
|
+
url: https://test.pypi.org/p/epilink
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
|
|
27
|
+
- name: Setup Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.11"
|
|
31
|
+
|
|
32
|
+
- name: Set test version
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip tomlkit
|
|
35
|
+
SUFFIX="${{ inputs.version_suffix }}"
|
|
36
|
+
if [ -z "$SUFFIX" ]; then
|
|
37
|
+
SUFFIX="dev${GITHUB_RUN_NUMBER}"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
export EPILINK_SUFFIX="$SUFFIX"
|
|
41
|
+
python - <<'PY'
|
|
42
|
+
import os
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
import subprocess
|
|
45
|
+
import tomlkit
|
|
46
|
+
|
|
47
|
+
path = Path("pyproject.toml")
|
|
48
|
+
text = path.read_text(encoding="utf-8")
|
|
49
|
+
data = tomlkit.parse(text)
|
|
50
|
+
project = data.get("project")
|
|
51
|
+
if not project:
|
|
52
|
+
raise SystemExit("version not found in pyproject.toml")
|
|
53
|
+
|
|
54
|
+
if "version" in project:
|
|
55
|
+
base_version = str(project["version"])
|
|
56
|
+
elif "version" in project.get("dynamic", []):
|
|
57
|
+
try:
|
|
58
|
+
tag = subprocess.check_output(
|
|
59
|
+
["git", "describe", "--tags", "--abbrev=0"],
|
|
60
|
+
text=True,
|
|
61
|
+
).strip()
|
|
62
|
+
except subprocess.CalledProcessError:
|
|
63
|
+
tag = "0.0.0"
|
|
64
|
+
base_version = tag[1:] if tag.startswith("v") else tag
|
|
65
|
+
else:
|
|
66
|
+
raise SystemExit("version not found in pyproject.toml")
|
|
67
|
+
|
|
68
|
+
suffix = os.environ["EPILINK_SUFFIX"]
|
|
69
|
+
new_version = f"{base_version}.{suffix}"
|
|
70
|
+
|
|
71
|
+
project["version"] = new_version
|
|
72
|
+
if "dynamic" in project:
|
|
73
|
+
dynamic = [item for item in project["dynamic"] if item != "version"]
|
|
74
|
+
if dynamic:
|
|
75
|
+
project["dynamic"] = dynamic
|
|
76
|
+
else:
|
|
77
|
+
del project["dynamic"]
|
|
78
|
+
|
|
79
|
+
path.write_text(tomlkit.dumps(data), encoding="utf-8")
|
|
80
|
+
print(f"Using test version: {new_version}")
|
|
81
|
+
PY
|
|
82
|
+
|
|
83
|
+
- name: Build distributions
|
|
84
|
+
run: |
|
|
85
|
+
python -m pip install --upgrade pip build twine
|
|
86
|
+
python -m build
|
|
87
|
+
python -m twine check dist/*
|
|
88
|
+
|
|
89
|
+
- name: Publish to TestPyPI
|
|
90
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
91
|
+
with:
|
|
92
|
+
repository-url: https://test.pypi.org/legacy/
|
|
93
|
+
verbose: true
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Python bytecode
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
|
|
5
|
+
# Build artifacts
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg-info/
|
|
9
|
+
*.egg
|
|
10
|
+
*.whl
|
|
11
|
+
src/epilink/_version.py
|
|
12
|
+
|
|
13
|
+
# Test and coverage artifacts
|
|
14
|
+
.coverage
|
|
15
|
+
.coverage.*
|
|
16
|
+
htmlcov/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.mypy_cache/
|
|
20
|
+
|
|
21
|
+
# Virtual environments
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
env/
|
|
25
|
+
|
|
26
|
+
# MkDocs
|
|
27
|
+
site/
|
|
28
|
+
|
|
29
|
+
# IDEs
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
*.iml
|
|
33
|
+
|
|
34
|
+
# macOS
|
|
35
|
+
.DS_Store
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/psf/black
|
|
3
|
+
rev: 24.8.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: black
|
|
6
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
7
|
+
rev: v0.6.4
|
|
8
|
+
hooks:
|
|
9
|
+
- id: ruff
|
|
10
|
+
args: [--fix]
|
|
11
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
12
|
+
rev: v1.11.2
|
|
13
|
+
hooks:
|
|
14
|
+
- id: mypy
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
title: "epilink: Epidemiological Linkage Inference from Temporal and Genetic Data with a Variable Infectiousness (E/P/I) Model for SARS-CoV-2"
|
|
4
|
+
version: "0.1.0"
|
|
5
|
+
license: "MIT"
|
|
6
|
+
url: "https://github.com/ydnkka/epilink"
|
|
7
|
+
authors:
|
|
8
|
+
- family-names: Arthur
|
|
9
|
+
given-names: Dominic
|
|
10
|
+
repository-code: "https://github.com/ydnkka/epilink"
|
|
11
|
+
references:
|
|
12
|
+
- type: article
|
|
13
|
+
authors:
|
|
14
|
+
- family-names: Hart
|
|
15
|
+
given-names: William S
|
|
16
|
+
- family-names: Maini
|
|
17
|
+
given-names: Philip K
|
|
18
|
+
- family-names: Thompson
|
|
19
|
+
given-names: Robin N
|
|
20
|
+
title: "High infectiousness immediately before COVID-19 symptom onset highlights the importance of continued contact tracing"
|
|
21
|
+
journal: "eLife"
|
|
22
|
+
year: 2021
|
|
23
|
+
volume: "10"
|
|
24
|
+
doi: "10.7554/eLife.65534"
|
epilink-0.2.1a0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dominic Arthur
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the “Software”), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
epilink-0.2.1a0/PKG-INFO
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: epilink
|
|
3
|
+
Version: 0.2.1a0
|
|
4
|
+
Summary: Epidemiological Linkage Inference from Temporal and Genetic Data with a Variable Infectiousness (E/P/I) Model for SARS-CoV-2.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ydnkka/epilink
|
|
6
|
+
Project-URL: Issues, https://github.com/ydnkka/epilink/issues
|
|
7
|
+
Project-URL: Documentation, https://github.com/ydnkka/epilink#readme
|
|
8
|
+
Project-URL: Repository, https://github.com/ydnkka/epilink
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/ydnkka/epilink/issues
|
|
10
|
+
Project-URL: Release Notes, https://github.com/ydnkka/epilink/releases
|
|
11
|
+
Author-email: Dominic Arthur <arthurdominic04@gmail.com>
|
|
12
|
+
Maintainer-email: Dominic Arthur <arthurdominic04@gmail.com>
|
|
13
|
+
License: MIT License
|
|
14
|
+
|
|
15
|
+
Copyright (c) 2025 Dominic Arthur
|
|
16
|
+
|
|
17
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
|
+
of this software and associated documentation files (the “Software”), to deal
|
|
19
|
+
in the Software without restriction, including without limitation the rights
|
|
20
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
21
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
22
|
+
furnished to do so, subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in
|
|
25
|
+
all copies or substantial portions of the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
33
|
+
THE SOFTWARE.
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
Keywords: SARS-CoV-2,epidemiology,genomics,infectious-disease,linkage-inference,transmission
|
|
36
|
+
Classifier: Development Status :: 4 - Beta
|
|
37
|
+
Classifier: Intended Audience :: Science/Research
|
|
38
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
39
|
+
Classifier: Natural Language :: English
|
|
40
|
+
Classifier: Operating System :: OS Independent
|
|
41
|
+
Classifier: Programming Language :: Python :: 3
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
45
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
46
|
+
Classifier: Topic :: Scientific/Engineering
|
|
47
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
48
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
49
|
+
Requires-Python: >=3.9
|
|
50
|
+
Requires-Dist: llvmlite<0.44,>=0.41
|
|
51
|
+
Requires-Dist: networkx<4.0,>=3.0
|
|
52
|
+
Requires-Dist: numba<0.61,>=0.58
|
|
53
|
+
Requires-Dist: numpy<2.0,>=1.22
|
|
54
|
+
Requires-Dist: pandas<3.0,>=2.0
|
|
55
|
+
Requires-Dist: scipy<2.0,>=1.9
|
|
56
|
+
Provides-Extra: all
|
|
57
|
+
Requires-Dist: black>=24.1; extra == 'all'
|
|
58
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'all'
|
|
59
|
+
Requires-Dist: mkdocs>=1.5; extra == 'all'
|
|
60
|
+
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'all'
|
|
61
|
+
Requires-Dist: mypy>=1.4; extra == 'all'
|
|
62
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'all'
|
|
63
|
+
Requires-Dist: pytest-mock>=3.10; extra == 'all'
|
|
64
|
+
Requires-Dist: pytest>=7.3; extra == 'all'
|
|
65
|
+
Requires-Dist: ruff>=0.5; extra == 'all'
|
|
66
|
+
Provides-Extra: dev
|
|
67
|
+
Requires-Dist: black>=24.1; extra == 'dev'
|
|
68
|
+
Requires-Dist: mypy>=1.4; extra == 'dev'
|
|
69
|
+
Requires-Dist: pre-commit>=3.3; extra == 'dev'
|
|
70
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
71
|
+
Requires-Dist: pytest-mock>=3.10; extra == 'dev'
|
|
72
|
+
Requires-Dist: pytest>=7.3; extra == 'dev'
|
|
73
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
74
|
+
Provides-Extra: docs
|
|
75
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
76
|
+
Requires-Dist: mkdocs>=1.5; extra == 'docs'
|
|
77
|
+
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
|
|
78
|
+
Provides-Extra: lint
|
|
79
|
+
Requires-Dist: black>=24.1; extra == 'lint'
|
|
80
|
+
Requires-Dist: ruff>=0.5; extra == 'lint'
|
|
81
|
+
Provides-Extra: test
|
|
82
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'test'
|
|
83
|
+
Requires-Dist: pytest-mock>=3.10; extra == 'test'
|
|
84
|
+
Requires-Dist: pytest>=7.3; extra == 'test'
|
|
85
|
+
Provides-Extra: type-check
|
|
86
|
+
Requires-Dist: mypy>=1.4; extra == 'type-check'
|
|
87
|
+
Description-Content-Type: text/markdown
|
|
88
|
+
|
|
89
|
+
# epilink: Epidemiological Linkage from Temporal and Genetic Data
|
|
90
|
+
|
|
91
|
+
[](https://codecov.io/gh/ydnkka/epilink)
|
|
92
|
+
|
|
93
|
+
Estimate the probability that two cases are epidemiologically linked from their temporal and genetic distances. Implements a mechanistic SARS‑CoV‑2 infectiousness model (E/P/I) with optional Numba acceleration. Usable from Python or the command line.
|
|
94
|
+
|
|
95
|
+
## Features
|
|
96
|
+
|
|
97
|
+
- Estimate P(link | genetic distance g, temporal gap t)
|
|
98
|
+
- Parameterised infectiousness profiles (TOIT/TOST; configurable)
|
|
99
|
+
- Fast simulation kernels with optional JIT (Numba)
|
|
100
|
+
- Python API plus a lightweight CLI for batch runs
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
From PyPI (yet to be released):
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install epilink
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Recommended (conda/mamba):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Create a fresh env (uses compiled deps from conda-forge)
|
|
116
|
+
conda create -n epilink -c conda-forge python=3.11 numpy scipy numba networkx pandas pip
|
|
117
|
+
conda activate epilink
|
|
118
|
+
git clone https://github.com/ydnkka/epilink.git
|
|
119
|
+
cd epilink
|
|
120
|
+
|
|
121
|
+
# Install the package from source without touching conda-managed deps
|
|
122
|
+
pip install -e . --no-deps
|
|
123
|
+
|
|
124
|
+
# (Optional) Dev tools: tests, linting, docs
|
|
125
|
+
pip install "pytest>=7.3" "pytest-cov>=4.0" "mypy>=1.4" "ruff>=0.5" "black>=24.1" \
|
|
126
|
+
"pre-commit>=3.3" "mkdocs>=1.5" "mkdocs-material>=9.5" "mkdocstrings[python]>=0.24"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Alternative (pip + venv):
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
python -m venv .venv
|
|
133
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
134
|
+
python -m pip install --upgrade pip
|
|
135
|
+
pip install -e .[dev]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Notes:
|
|
139
|
+
- Prefer conda-forge for NumPy/SciPy/Numba (especially on Apple Silicon).
|
|
140
|
+
- In a conda env, avoid pip-installing compiled deps; use `pip install -e . --no-deps`.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Quickstart
|
|
145
|
+
|
|
146
|
+
### Python API
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
import numpy as np
|
|
150
|
+
from epilink import TOIT, MolecularClock, linkage_probability, linkage_probability_matrix
|
|
151
|
+
|
|
152
|
+
toit = TOIT(rng_seed=123)
|
|
153
|
+
clock = MolecularClock(relax_rate=False, rng_seed=123)
|
|
154
|
+
|
|
155
|
+
# Probability that a pair with 2 SNPs and 4 days apart is directly linked (m=0)
|
|
156
|
+
p = linkage_probability(
|
|
157
|
+
toit=toit,
|
|
158
|
+
clock=clock,
|
|
159
|
+
genetic_distance=2,
|
|
160
|
+
temporal_distance=4,
|
|
161
|
+
intermediate_generations=0,
|
|
162
|
+
num_simulations=10_000,
|
|
163
|
+
)
|
|
164
|
+
print("P(link):", p)
|
|
165
|
+
|
|
166
|
+
# Grid over genetic distances (SNPs) and temporal gaps (days)
|
|
167
|
+
gd = np.arange(0, 6) # 0..5 SNPs
|
|
168
|
+
td = np.arange(0, 15, 3) # 0..12 days, step 3
|
|
169
|
+
mat = linkage_probability_matrix(
|
|
170
|
+
toit=toit,
|
|
171
|
+
clock=clock,
|
|
172
|
+
genetic_distances=gd,
|
|
173
|
+
temporal_distances=td,
|
|
174
|
+
num_simulations=10_000,
|
|
175
|
+
)
|
|
176
|
+
print(mat)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### CLI
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Help
|
|
183
|
+
epilink --help
|
|
184
|
+
epilink point --help
|
|
185
|
+
epilink grid --help
|
|
186
|
+
|
|
187
|
+
# Single pair
|
|
188
|
+
epilink point -g 2 -t 4 --nsims 200
|
|
189
|
+
|
|
190
|
+
# Multiple pairs (CSV to stdout)
|
|
191
|
+
epilink point -g 0 1 2 -t 0 2 5 --nsims 500 > out.csv
|
|
192
|
+
|
|
193
|
+
# Custom infectiousness profile and TOIT support
|
|
194
|
+
epilink point -g 2 -t 4 --nsims 200 \
|
|
195
|
+
--a 0 --b 40 \
|
|
196
|
+
--incubation-shape 5.0 --incubation-scale 1.1 \
|
|
197
|
+
--latent-shape 2.0 --symptomatic-rate 0.4 \
|
|
198
|
+
--symptomatic-shape 1.2 --rel-presymptomatic-infectiousness 2.0
|
|
199
|
+
|
|
200
|
+
# Grid (CSV to file)
|
|
201
|
+
epilink grid --g-start 0 --g-stop 5 --g-step 1 \
|
|
202
|
+
--t-start 0 --t-stop 12 --t-step 3 \
|
|
203
|
+
--nsims 10000 --out grid.csv
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Commonly used options (see `--help` for full list):
|
|
207
|
+
- `-m, --intermediate-generations` e.g. `"0,1,2"`
|
|
208
|
+
- `--relax-rate`
|
|
209
|
+
- `--subs-rate 1e-3`
|
|
210
|
+
- `--subs-rate-sigma 0.33`
|
|
211
|
+
- `--seed 12345`
|
|
212
|
+
- `--a 0 --b 60` (TOIT support bounds)
|
|
213
|
+
- `--incubation-shape`, `--incubation-scale`, `--latent-shape`, `--symptomatic-rate`, `--symptomatic-shape`, `--rel-presymptomatic-infectiousness`
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Development
|
|
218
|
+
|
|
219
|
+
Run tests:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
pytest --cov=epilink --cov-report=term-missing
|
|
223
|
+
# To count Python-side coverage of JIT kernels:
|
|
224
|
+
# NUMBA_DISABLE_JIT=1 pytest
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Code quality:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
ruff check .
|
|
231
|
+
black .
|
|
232
|
+
mypy src/epilink
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Pre-commit:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
pre-commit install
|
|
239
|
+
pre-commit run -a
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Docs:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
mkdocs serve
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Examples
|
|
251
|
+
|
|
252
|
+
Examples in `examples/`:
|
|
253
|
+
- `python examples/quickstart.py`
|
|
254
|
+
- `python examples/grid_to_csv.py --out grid.csv`
|
|
255
|
+
|
|
256
|
+
Install plotting deps if needed:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
mamba install -c conda-forge matplotlib seaborn
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT License (see [LICENSE](LICENSE))
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Development & Contributing
|
|
271
|
+
|
|
272
|
+
For developers:
|
|
273
|
+
- **Testing releases**: See [TESTPYPI.md](TESTPYPI.md) for instructions on testing package releases on TestPyPI before publishing to PyPI
|
|
274
|
+
- **Contributing**: Pull requests welcome! Please ensure tests pass and coverage remains high
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Contact
|
|
279
|
+
|
|
280
|
+
- Questions or issues: open an [issue](https://github.com/ydnkka/epilink/issues)
|
|
281
|
+
- Maintainer: [@ydnkka](https://github.com/ydnkka)
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Reference
|
|
286
|
+
|
|
287
|
+
Hart WS, Maini PK, Thompson RN (2021). High infectiousness immediately before COVID‑19 symptom onset highlights the importance of continued contact tracing. eLife, 10:e65534. https://doi.org/10.7554/eLife.65534
|