ldetect-lite 0.1.0__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.
- ldetect_lite-0.1.0/.gitattributes +2 -0
- ldetect_lite-0.1.0/.github/workflows/publish.yml +47 -0
- ldetect_lite-0.1.0/.github/workflows/tests.yml +50 -0
- ldetect_lite-0.1.0/.gitignore +203 -0
- ldetect_lite-0.1.0/AGENTS.md +117 -0
- ldetect_lite-0.1.0/LICENSE +21 -0
- ldetect_lite-0.1.0/PKG-INFO +172 -0
- ldetect_lite-0.1.0/README.md +117 -0
- ldetect_lite-0.1.0/benchmarks/bench_ld_kernel.py +154 -0
- ldetect_lite-0.1.0/benchmarks/ld_numba_speedup.pdf +0 -0
- ldetect_lite-0.1.0/docs/optimizations.md +167 -0
- ldetect_lite-0.1.0/docs/pipeline-steps.md +142 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/Snakefile +756 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/config.yaml +137 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/compare_blocks.py +185 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/compare_boundaries.py +110 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/compare_maps.py +181 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/convert_decode_map.py +80 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/diagnose_boundaries.py +444 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/download_data.py +100 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/postprocess.py +177 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/prep_individuals.py +96 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/sync_results.sh +28 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/validate_maps.py +123 -0
- ldetect_lite-0.1.0/examples/MacDonald2022/scripts/verify_local_search.py +189 -0
- ldetect_lite-0.1.0/examples/ldetect_example/README.md +37 -0
- ldetect_lite-0.1.0/examples/ldetect_example/Snakefile +284 -0
- ldetect_lite-0.1.0/examples/ldetect_example/config.yaml +15 -0
- ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_bed.py +68 -0
- ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_bpoints.py +99 -0
- ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_partitions.py +81 -0
- ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_vector.py +82 -0
- ldetect_lite-0.1.0/examples/ldetect_example/scripts/convert_covariance.py +102 -0
- ldetect_lite-0.1.0/examples/ldetect_original/README.md +437 -0
- ldetect_lite-0.1.0/examples/ldetect_original/Snakefile +293 -0
- ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.alternate_source_rerun +366 -0
- ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.compression_diagnostics +307 -0
- ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.diagnostics +514 -0
- ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.legacy_diagnostics +335 -0
- ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.provenance_diagnostics +291 -0
- ldetect_lite-0.1.0/examples/ldetect_original/alternate_source_rerun.yaml +19 -0
- ldetect_lite-0.1.0/examples/ldetect_original/compression_diagnostics.yaml +29 -0
- ldetect_lite-0.1.0/examples/ldetect_original/config.yaml +47 -0
- ldetect_lite-0.1.0/examples/ldetect_original/diagnostics.yaml +30 -0
- ldetect_lite-0.1.0/examples/ldetect_original/legacy_diagnostics.yaml +31 -0
- ldetect_lite-0.1.0/examples/ldetect_original/plots/afr_chr22_concordant_only.png +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/plots/afr_chr22_divergent_only.png +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/plots/afr_chr22_mixed.png +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/plots/eur_chr10_concordant_only.png +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/plots/eur_chr10_divergent_only.png +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/plots/eur_chr10_mixed.png +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/provenance_diagnostics.yaml +61 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/check_reference_beds.py +104 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/combine_tsv.py +38 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_blocks.py +197 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_compression.py +246 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_legacy_outputs.py +146 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_partition_overlap_duplicates.py +203 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_vcf_ld.py +387 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_vcf_positions.py +97 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/diagnose_inputs.py +169 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/diagnose_run.py +243 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/download_legacy_example_data.py +79 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/download_provenance_input.py +68 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_hdf5_to_gz.py +70 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/LDETECT_LICENSE.txt +9 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/P01_matrix_to_vector_pipeline.py +80 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/P02_minima_pipeline.py +233 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/P03_extract_bpoints.py +42 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/commanderline/__init__.py +2 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/commanderline/commander_line.py +15 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/__init__.py +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/__init__.py +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/binary_search.py +56 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/filters.py +58 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/flat_file.py +367 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/flat_file_consts.py +80 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/read_data.py +60 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E03_matrix_to_vector.py +651 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E05_find_minima.py +147 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E07_metric.py +446 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E08_local_search.py +624 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/__init__.py +0 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/plot_profile_timeline.py +159 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/plot_resource_scaling.py +198 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/plot_vector_boundaries.py +160 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/prep_individuals.py +77 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/profile_run.py +159 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/run_legacy_ldetect.py +199 -0
- ldetect_lite-0.1.0/examples/ldetect_original/scripts/snp_filter_diagnostics.py +158 -0
- ldetect_lite-0.1.0/notes/README.md +17 -0
- ldetect_lite-0.1.0/notes/findings/ldetect-original-reproduction.md +44 -0
- ldetect_lite-0.1.0/notes/findings/macdonald2022-reproduction.md +37 -0
- ldetect_lite-0.1.0/notes/logs/implementation-plan.md +340 -0
- ldetect_lite-0.1.0/notes/logs/ldetect-original-main-pipeline-audit.md +1273 -0
- ldetect_lite-0.1.0/notes/logs/local-search-divergence-asn22.md +261 -0
- ldetect_lite-0.1.0/notes/logs/macdonald2022-boundary-diagnostics.md +244 -0
- ldetect_lite-0.1.0/notes/logs/macdonald2022-interpolation-port.md +143 -0
- ldetect_lite-0.1.0/notes/logs/macdonald2022-pyrho-handoff.md +1087 -0
- ldetect_lite-0.1.0/notes/logs/post-covariance-optimization-review.md +136 -0
- ldetect_lite-0.1.0/pyproject.toml +76 -0
- ldetect_lite-0.1.0/src/ldetect_lite/__init__.py +8 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/__init__.py +0 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_covariance.py +75 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_covariance_summary.py +95 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_extract_bpoints.py +70 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_find_minima.py +150 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_interpolate_maps.py +61 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_matrix_to_vector.py +95 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_partition.py +70 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_run.py +415 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_cli/main.py +64 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/__init__.py +0 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/binary_search.py +67 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/covariance_array.py +1024 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/covariance_summary.py +220 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/intervals.py +125 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/logging.py +26 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/memory.py +60 -0
- ldetect_lite-0.1.0/src/ldetect_lite/_util/vector_array.py +908 -0
- ldetect_lite-0.1.0/src/ldetect_lite/filters.py +82 -0
- ldetect_lite-0.1.0/src/ldetect_lite/find_minima.py +159 -0
- ldetect_lite-0.1.0/src/ldetect_lite/interpolate_maps.py +165 -0
- ldetect_lite-0.1.0/src/ldetect_lite/io/__init__.py +0 -0
- ldetect_lite-0.1.0/src/ldetect_lite/io/bed.py +118 -0
- ldetect_lite-0.1.0/src/ldetect_lite/io/covariance.py +390 -0
- ldetect_lite-0.1.0/src/ldetect_lite/io/covariance_hdf5.py +714 -0
- ldetect_lite-0.1.0/src/ldetect_lite/io/partitions.py +110 -0
- ldetect_lite-0.1.0/src/ldetect_lite/io/vcf.py +17 -0
- ldetect_lite-0.1.0/src/ldetect_lite/local_search.py +1821 -0
- ldetect_lite-0.1.0/src/ldetect_lite/matrix_analysis.py +511 -0
- ldetect_lite-0.1.0/src/ldetect_lite/metric.py +265 -0
- ldetect_lite-0.1.0/src/ldetect_lite/pipeline.py +662 -0
- ldetect_lite-0.1.0/src/ldetect_lite/shrinkage.py +846 -0
- ldetect_lite-0.1.0/tests/__init__.py +0 -0
- ldetect_lite-0.1.0/tests/_partition_fixtures.py +190 -0
- ldetect_lite-0.1.0/tests/conftest.py +63 -0
- ldetect_lite-0.1.0/tests/integration/__init__.py +0 -0
- ldetect_lite-0.1.0/tests/integration/test_pipeline.py +367 -0
- ldetect_lite-0.1.0/tests/test_bed.py +195 -0
- ldetect_lite-0.1.0/tests/test_binary_search.py +178 -0
- ldetect_lite-0.1.0/tests/test_cmd_covariance.py +34 -0
- ldetect_lite-0.1.0/tests/test_cmd_find_minima.py +41 -0
- ldetect_lite-0.1.0/tests/test_cmd_matrix_to_vector.py +33 -0
- ldetect_lite-0.1.0/tests/test_cmd_run.py +113 -0
- ldetect_lite-0.1.0/tests/test_compare_compression_script.py +323 -0
- ldetect_lite-0.1.0/tests/test_covariance_array.py +92 -0
- ldetect_lite-0.1.0/tests/test_covariance_io.py +782 -0
- ldetect_lite-0.1.0/tests/test_covariance_summary.py +200 -0
- ldetect_lite-0.1.0/tests/test_duplicate_overlap_integration.py +151 -0
- ldetect_lite-0.1.0/tests/test_filters.py +108 -0
- ldetect_lite-0.1.0/tests/test_find_minima.py +23 -0
- ldetect_lite-0.1.0/tests/test_interpolate_maps.py +255 -0
- ldetect_lite-0.1.0/tests/test_intervals.py +42 -0
- ldetect_lite-0.1.0/tests/test_local_search.py +819 -0
- ldetect_lite-0.1.0/tests/test_macdonald_diagnose_boundaries.py +81 -0
- ldetect_lite-0.1.0/tests/test_macdonald_validate_maps.py +64 -0
- ldetect_lite-0.1.0/tests/test_metric.py +281 -0
- ldetect_lite-0.1.0/tests/test_partitions.py +131 -0
- ldetect_lite-0.1.0/tests/test_shrinkage.py +479 -0
- ldetect_lite-0.1.0/tests/test_vcf.py +23 -0
- ldetect_lite-0.1.0/uv.lock +2442 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v7
|
|
13
|
+
|
|
14
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
enable-cache: true
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: uv sync --extra dev --extra heatmap
|
|
21
|
+
|
|
22
|
+
- name: Lint (ruff)
|
|
23
|
+
run: uv run ruff check src tests
|
|
24
|
+
|
|
25
|
+
- name: Type-check (mypy)
|
|
26
|
+
run: uv run mypy src
|
|
27
|
+
|
|
28
|
+
- name: Run unit tests
|
|
29
|
+
run: uv run pytest -m "not integration"
|
|
30
|
+
|
|
31
|
+
publish:
|
|
32
|
+
needs: test
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
environment: pypi
|
|
35
|
+
permissions:
|
|
36
|
+
id-token: write # required for trusted publishing (OIDC)
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v7
|
|
40
|
+
|
|
41
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
42
|
+
|
|
43
|
+
- name: Build distributions
|
|
44
|
+
run: uv build
|
|
45
|
+
|
|
46
|
+
- name: Publish to PyPI
|
|
47
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
|
+
|
|
20
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --extra dev --extra heatmap
|
|
27
|
+
|
|
28
|
+
- name: Lint (ruff)
|
|
29
|
+
run: uv run ruff check src tests
|
|
30
|
+
|
|
31
|
+
- name: Type-check (mypy)
|
|
32
|
+
run: uv run mypy src
|
|
33
|
+
|
|
34
|
+
- name: Run unit tests
|
|
35
|
+
run: uv run pytest -m "not integration"
|
|
36
|
+
|
|
37
|
+
- name: Run integration tests
|
|
38
|
+
run: uv run pytest -m integration
|
|
39
|
+
|
|
40
|
+
build:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v7
|
|
44
|
+
|
|
45
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
46
|
+
with:
|
|
47
|
+
enable-cache: true
|
|
48
|
+
|
|
49
|
+
- name: Build sdist and wheel
|
|
50
|
+
run: uv build
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
**/.DS_Store
|
|
2
|
+
**/.vscode
|
|
3
|
+
_reference
|
|
4
|
+
tests/data/
|
|
5
|
+
data/
|
|
6
|
+
**/.snakemake/
|
|
7
|
+
examples/*/results/
|
|
8
|
+
examples/*/data/
|
|
9
|
+
examples/*/ref/
|
|
10
|
+
examples/*/resources/
|
|
11
|
+
examples/*/work/
|
|
12
|
+
|
|
13
|
+
# Large downloaded inputs and optional legacy toy data for replication examples.
|
|
14
|
+
examples/ldetect_original/**/*.vcf.gz
|
|
15
|
+
examples/ldetect_original/**/*.vcf.gz.tbi
|
|
16
|
+
examples/ldetect_original/**/*.interpolated_genetic_map.gz
|
|
17
|
+
examples/ldetect_original/scripts/legacy_ldetect/ldetect/examples/example_data/
|
|
18
|
+
|
|
19
|
+
# Downloaded copyrighted supplementary material (Berisa & Pickrell 2016) --
|
|
20
|
+
# useful locally for provenance research, must not be redistributed via git.
|
|
21
|
+
examples/ldetect_original/document-sup.pdf
|
|
22
|
+
|
|
23
|
+
# Byte-compiled / optimized / DLL files
|
|
24
|
+
__pycache__/
|
|
25
|
+
*.py[cod]
|
|
26
|
+
*$py.class
|
|
27
|
+
|
|
28
|
+
# C extensions
|
|
29
|
+
*.so
|
|
30
|
+
|
|
31
|
+
# Distribution / packaging
|
|
32
|
+
.Python
|
|
33
|
+
build/
|
|
34
|
+
develop-eggs/
|
|
35
|
+
dist/
|
|
36
|
+
downloads/
|
|
37
|
+
eggs/
|
|
38
|
+
.eggs/
|
|
39
|
+
lib/
|
|
40
|
+
lib64/
|
|
41
|
+
parts/
|
|
42
|
+
sdist/
|
|
43
|
+
var/
|
|
44
|
+
wheels/
|
|
45
|
+
share/python-wheels/
|
|
46
|
+
*.egg-info/
|
|
47
|
+
.installed.cfg
|
|
48
|
+
*.egg
|
|
49
|
+
MANIFEST
|
|
50
|
+
|
|
51
|
+
# PyInstaller
|
|
52
|
+
# Usually these files are written by a python script from a template
|
|
53
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
54
|
+
*.manifest
|
|
55
|
+
*.spec
|
|
56
|
+
|
|
57
|
+
# Installer logs
|
|
58
|
+
pip-log.txt
|
|
59
|
+
pip-delete-this-directory.txt
|
|
60
|
+
|
|
61
|
+
# Unit test / coverage reports
|
|
62
|
+
htmlcov/
|
|
63
|
+
.tox/
|
|
64
|
+
.nox/
|
|
65
|
+
.coverage
|
|
66
|
+
.coverage.*
|
|
67
|
+
.cache
|
|
68
|
+
nosetests.xml
|
|
69
|
+
coverage.xml
|
|
70
|
+
*.cover
|
|
71
|
+
*.py,cover
|
|
72
|
+
.hypothesis/
|
|
73
|
+
.pytest_cache/
|
|
74
|
+
cover/
|
|
75
|
+
|
|
76
|
+
# Translations
|
|
77
|
+
*.mo
|
|
78
|
+
*.pot
|
|
79
|
+
|
|
80
|
+
# Django stuff:
|
|
81
|
+
*.log
|
|
82
|
+
local_settings.py
|
|
83
|
+
db.sqlite3
|
|
84
|
+
db.sqlite3-journal
|
|
85
|
+
|
|
86
|
+
# Flask stuff:
|
|
87
|
+
instance/
|
|
88
|
+
.webassets-cache
|
|
89
|
+
|
|
90
|
+
# Scrapy stuff:
|
|
91
|
+
.scrapy
|
|
92
|
+
|
|
93
|
+
# Sphinx documentation
|
|
94
|
+
docs/_build/
|
|
95
|
+
|
|
96
|
+
# PyBuilder
|
|
97
|
+
.pybuilder/
|
|
98
|
+
target/
|
|
99
|
+
|
|
100
|
+
# Jupyter Notebook
|
|
101
|
+
.ipynb_checkpoints
|
|
102
|
+
|
|
103
|
+
# IPython
|
|
104
|
+
profile_default/
|
|
105
|
+
ipython_config.py
|
|
106
|
+
|
|
107
|
+
# pyenv
|
|
108
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
109
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
110
|
+
# .python-version
|
|
111
|
+
|
|
112
|
+
# pipenv
|
|
113
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
114
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
115
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
116
|
+
# install all needed dependencies.
|
|
117
|
+
#Pipfile.lock
|
|
118
|
+
|
|
119
|
+
# UV
|
|
120
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
121
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
122
|
+
# commonly ignored for libraries.
|
|
123
|
+
#uv.lock
|
|
124
|
+
|
|
125
|
+
# poetry
|
|
126
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
127
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
128
|
+
# commonly ignored for libraries.
|
|
129
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
130
|
+
#poetry.lock
|
|
131
|
+
|
|
132
|
+
# pdm
|
|
133
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
134
|
+
#pdm.lock
|
|
135
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
136
|
+
# in version control.
|
|
137
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
138
|
+
.pdm.toml
|
|
139
|
+
.pdm-python
|
|
140
|
+
.pdm-build/
|
|
141
|
+
|
|
142
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
143
|
+
__pypackages__/
|
|
144
|
+
|
|
145
|
+
# Celery stuff
|
|
146
|
+
celerybeat-schedule
|
|
147
|
+
celerybeat.pid
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.venv
|
|
155
|
+
env/
|
|
156
|
+
venv/
|
|
157
|
+
ENV/
|
|
158
|
+
env.bak/
|
|
159
|
+
venv.bak/
|
|
160
|
+
|
|
161
|
+
# Spyder project settings
|
|
162
|
+
.spyderproject
|
|
163
|
+
.spyproject
|
|
164
|
+
|
|
165
|
+
# Rope project settings
|
|
166
|
+
.ropeproject
|
|
167
|
+
|
|
168
|
+
# mkdocs documentation
|
|
169
|
+
/site
|
|
170
|
+
|
|
171
|
+
# mypy
|
|
172
|
+
.mypy_cache/
|
|
173
|
+
.dmypy.json
|
|
174
|
+
dmypy.json
|
|
175
|
+
|
|
176
|
+
# Pyre type checker
|
|
177
|
+
.pyre/
|
|
178
|
+
|
|
179
|
+
# pytype static type analyzer
|
|
180
|
+
.pytype/
|
|
181
|
+
|
|
182
|
+
# Cython debug symbols
|
|
183
|
+
cython_debug/
|
|
184
|
+
|
|
185
|
+
# PyCharm
|
|
186
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
187
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
188
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
189
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
190
|
+
#.idea/
|
|
191
|
+
|
|
192
|
+
# Ruff stuff:
|
|
193
|
+
.ruff_cache/
|
|
194
|
+
|
|
195
|
+
# PyPI configuration file
|
|
196
|
+
.pypirc
|
|
197
|
+
|
|
198
|
+
# Cursor
|
|
199
|
+
# Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to
|
|
200
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
201
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
202
|
+
.cursorignore
|
|
203
|
+
.cursorindexingignore
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to coding agents when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**ldetect-lite** is a modern reimplementation of [ldetect](https://bitbucket.org/nygcresearch/ldetect), a bioinformatics tool that calculates approximately independent linkage disequilibrium (LD) blocks in the human genome. The algorithm is described in [Berisa & Pickrell, 2016](https://academic.oup.com/bioinformatics/article/32/2/283/1743626).
|
|
8
|
+
|
|
9
|
+
The complete reference implementation lives in `_reference/ldetect_original/ldetect/` (also `_reference/LDblocks_GRCh38/` for the MacDonald et al. 2022 GRCh38 reproduction). The new implementation is in `src/ldetect_lite/`.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Install in editable mode (uv recommended)
|
|
15
|
+
uv sync
|
|
16
|
+
|
|
17
|
+
# Install with heatmap support (matplotlib, required for --generate-heatmap)
|
|
18
|
+
uv sync --extra heatmap
|
|
19
|
+
|
|
20
|
+
# Install dev extras (pytest, ruff, mypy, snakemake — needed for examples/)
|
|
21
|
+
uv sync --extra dev
|
|
22
|
+
|
|
23
|
+
# Run unit tests only (fast)
|
|
24
|
+
uv run pytest -m "not integration"
|
|
25
|
+
|
|
26
|
+
# Run a single test
|
|
27
|
+
uv run pytest tests/test_filters.py::test_unimodal_single_minimum -v
|
|
28
|
+
|
|
29
|
+
# Run integration tests (downloads ~5 files from BitBucket on first run, cached to tests/data/)
|
|
30
|
+
uv run pytest -m integration
|
|
31
|
+
|
|
32
|
+
# Run all tests
|
|
33
|
+
uv run pytest
|
|
34
|
+
|
|
35
|
+
# Lint / type-check
|
|
36
|
+
uv run ruff check src tests
|
|
37
|
+
uv run mypy src
|
|
38
|
+
|
|
39
|
+
# CLI entry point
|
|
40
|
+
uv run ldetect --help
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
The package is under `src/ldetect_lite/`. Key modules:
|
|
46
|
+
|
|
47
|
+
| Module | Role |
|
|
48
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `shrinkage.py` | Steps 1–2: `partition_chromosome` and `calc_covariance` (Wen/Stephens shrinkage LD estimator). The pairwise LD kernel (`_pairwise_ld_impl`) is JIT-compiled with Numba when available (~50x speedup), falling back to pure Python. Writes indexed HDF5 covariance partitions. |
|
|
50
|
+
| `matrix_analysis.py` | Step 3: `MatrixAnalysis` class — reduces covariance partitions to a `[position, diagonal_sum]` vector. `calc_diag_lean` (dictionary path) and `calc_diag_array` (array-backed path, via `_util/vector_array.py`) are the two production paths; `calc_diag`/`generate_img` support the `--generate-heatmap` PNG output and require full-schema partitions. |
|
|
51
|
+
| `filters.py` | Hanning-window convolution (`np.hanning`, symmetric) and `scipy.signal.argrelextrema` minima extraction |
|
|
52
|
+
| `find_minima.py` | Step 4 core: `FlexibleBoundedAccessor`, binary search for optimal filter width, `custom_binary_search_with_trackback` |
|
|
53
|
+
| `metric.py` | `Metric` class — sum of squared correlations across blocks. Float by default; `use_decimal=True` opts into 50-digit `decimal.Decimal` precision (dictionary path) instead of the array-backed path. |
|
|
54
|
+
| `local_search.py` | `LocalSearch` — refines each breakpoint by local search. Array-backed path (`_init_search_array`/`_search_array`) is used for single-partition windows only; multi-partition windows and `--high-precision` fall back to the legacy dictionary/Decimal path (`init_search`/`search`) because the array path does not yet reproduce the legacy multi-partition effective locus list (see `notes/logs/local-search-divergence-asn22.md`). |
|
|
55
|
+
| `pipeline.py` | `find_breakpoints` — orchestrates steps 4–5: binary search for filter width, minima extraction, metric computation, local search, JSON output with four subsets (`fourier`, `fourier_ls`, `uniform`, `uniform_ls`). Supports computing only requested subsets plus their local-search dependencies, and reuses an in-memory `ChromosomeCovariance` cache across metric calls where possible. |
|
|
56
|
+
| `interpolate_maps.py` | `interpolate()` — maps SNP positions to genetic positions via point-based linear interpolation (point-sampled maps). `interpolate_intervals()` — port of MacDonald et al.'s R interval-rate algorithm, for interval-rate maps (e.g. deCODE); selected via `ldetect interpolate-maps --mode {point,interval}` (see `notes/findings/macdonald2022-reproduction.md`) |
|
|
57
|
+
| `io/partitions.py` | `CovarianceStore` dataclass (replaces reference's global `flat_file_consts.py` dict), `read_partitions`, `relevant_subpartitions`, `first_last`, `get_final_partitions` |
|
|
58
|
+
| `io/covariance.py` | Legacy dictionary-based covariance matrix I/O: `insert_into_matrix_lean`, `read_partition_into_matrix_lean`, `delete_loci_*`, `write_corr_vector` |
|
|
59
|
+
| `io/covariance_hdf5.py` | Indexed HDF5 partition I/O: `write_covariance_partition_hdf5` (full schema), `write_compact_covariance_partition_hdf5_chunks`/`_append` (compact, streamed), `HDF5CovariancePartitionReader`, `open_covariance_reader`, `validate_covariance_hdf5` |
|
|
60
|
+
| `io/bed.py` | `write_bed` |
|
|
61
|
+
| `io/vcf.py` | `read_vcf_samples` — sample IDs via `bcftools query -l` |
|
|
62
|
+
| `_util/binary_search.py` | `find_le/ge/lt/gt` and `*_ind` variants wrapping `bisect` |
|
|
63
|
+
| `_util/covariance_array.py` | Array-backed covariance loading for metrics/local search: `ChromosomeCovariance`, `CovariancePartition`, `load_covariance_arrays`, `load_chromosome_covariance`, `metric_from_arrays`, `metric_from_files` (parallel row-pass streaming metric via `--metric-workers`) |
|
|
64
|
+
| `_util/vector_array.py` | Array-backed matrix-to-vector implementation used by `calc_diag_array`; streams and chunks HDF5 partitions to bound memory |
|
|
65
|
+
| `_util/covariance_summary.py` | `summarize_covariance` — per-partition row counts and estimated covariance-array memory, backing `ldetect covariance-summary` |
|
|
66
|
+
| `_util/memory.py` | `current_rss_mib`/`max_rss_mib`/`log_memory_checkpoint` — process RSS diagnostics logged at pipeline checkpoints (debug logging) |
|
|
67
|
+
| `_util/intervals.py` | Interval helpers |
|
|
68
|
+
| `_util/logging.py` | `log_msg`/`log_debug`/`configure_logging` wrapping stdlib logging (replaces `print_log_msg`) |
|
|
69
|
+
| `_cli/` | argparse subcommands: `partition-chromosome`, `calc-covariance`, `covariance-summary`, `matrix-to-vector`, `find-minima`, `extract-bpoints`, `interpolate-maps`, `run`; global `-v/--verbosity {debug,info,warning,error}` flag handled in `main.py` |
|
|
70
|
+
|
|
71
|
+
## Key Design Decisions
|
|
72
|
+
|
|
73
|
+
- **`CovarianceStore`** (frozen dataclass) replaces the reference's `flat_file_consts.py` global path-config dict. Every function that previously took `input_config: dict` now takes `store: CovarianceStore`.
|
|
74
|
+
- **Numba JIT**: `_pairwise_ld_impl` in `shrinkage.py` is decorated with `@_jit` — a decorator defined as `njit(cache=True)` when Numba is available, or a no-op otherwise. The inner k loop was replaced with `np.sum(a * b)` / `np.sum(a)` / `np.sum(b)` to let LLVM vectorize (~50x speedup over pure Python).
|
|
75
|
+
- **Parallel covariance**: `ldetect run --workers N` uses `concurrent.futures.ProcessPoolExecutor` to calculate covariance matrices for multiple partitions concurrently. Each partition is independent (unique output file, no shared state). The tabix spawn + `calc_covariance` call is extracted into a module-level `_calc_partition` function in `_cli/cmd_run.py` so it is picklable by the process pool. `--workers` also sets the default for the Step 3 (`--matrix-workers`) and Step 4 (`--local-search-workers`, `--metric-workers`) process pools, which run strictly after Step 2 finishes (never concurrently with it or each other), so a single `--workers N` parallelizes the whole pipeline unless a stage-specific override is passed.
|
|
76
|
+
- **Pickle → JSON**: Intermediate breakpoint output uses `.json` (not `.pickle`) for portability.
|
|
77
|
+
- **Flat file → indexed HDF5**: Covariance partitions are stored as indexed HDF5 (`.h5`), not the reference's gzipped space-delimited flat files. `full` schema (naive LD, genetic positions, SNP IDs) supports `--generate-heatmap` and debugging; `compact` schema (canonical `lo`/`hi` pairs, `shrink_ld`, diagonal entries, lookup indexes) is the `ldetect run` default and supports restartable production runs with bounded memory.
|
|
78
|
+
- **Array-backed vs. dictionary paths**: `matrix_analysis.py`, `metric.py`, and `local_search.py` each have a legacy dictionary-based path (ported near-verbatim from the reference, precise but slow) and a newer NumPy array-backed path (faster, vectorized). Float-mode single-partition local search and normal-float metrics use the array path by default; `--high-precision` (Decimal) and multi-partition local-search windows use the dictionary path because the array-backed local search does not yet reproduce the legacy multi-partition effective locus list exactly. Treat `--high-precision` as the correctness oracle when investigating local-search discrepancies.
|
|
79
|
+
- **Float arithmetic by default**: The reference used 50-digit `decimal.Decimal` for all breakpoint metric/local-search arithmetic (~10–30x slower than float, with no practical difference for typical LD data). `float` is now the default; `--high-precision` opts back into Decimal.
|
|
80
|
+
- **Symmetric Hann window**: `filters.py` uses `np.hanning(2*width+1)` — the symmetric variant. `scipy.signal.get_window('hann', N)` (default `fftbins=True`) is a periodic DFT window and gives different results.
|
|
81
|
+
- **Test data**: Downloaded on first run from BitBucket raw URLs into `tests/data/` (gitignored). Session-scoped fixtures in `tests/conftest.py` handle this.
|
|
82
|
+
|
|
83
|
+
## Input Data Formats
|
|
84
|
+
|
|
85
|
+
- **Genetic map**: gzipped TSV, columns: `chr position genetic_position_cM`
|
|
86
|
+
- **Covariance matrix**: indexed HDF5 partition file (`.h5`); `full` schema also readable/writable as the reference's gzipped space-delimited 8-column flat format (`i_id j_id i_pos j_pos i_gpos j_gpos naive_ld shrink_ld`) via the legacy `io/covariance.py` path
|
|
87
|
+
- **Partition file**: space-delimited `start end` pairs, one per line
|
|
88
|
+
- **Reference panel**: phased VCF piped via `tabix`
|
|
89
|
+
- **Breakpoint JSON** (from `find-minima`): `{"n_bpoints", "found_width", "computed_subsets", "skipped_subsets", "fourier"/"fourier_ls"/"uniform"/"uniform_ls": {"loci": [...], "metric": {"sum", "N_nonzero", "N_zero"}}}`. `metric.sum`/`N_zero` are stored as strings to preserve precision (relevant when `--high-precision` produces Decimal values).
|
|
90
|
+
|
|
91
|
+
## Repository Layout Beyond `src/`
|
|
92
|
+
|
|
93
|
+
- `docs/` — human-facing reference documentation: `docs/pipeline-steps.md` (per-command CLI reference for running each pipeline stage individually) and `docs/optimizations.md` (up-to-date human-readable summary of implemented performance work).
|
|
94
|
+
- `notes/` — internal working notes, split into `notes/findings/` (distilled, current-status summaries of reproduction investigations, meant to be read and mined for the paper writeup) and `notes/logs/` (raw, dated, agent-oriented investigation logs and historical design docs — see `notes/README.md` for the full index). `notes/logs/ldetect-original-*.md` and `notes/logs/local-search-divergence-asn22.md` back the ldetect_original reproduction status; `notes/logs/macdonald2022-*.md` back the MacDonald2022 reproduction status (see Reproduction Status below).
|
|
95
|
+
- `examples/` — Snakemake reproduction workflows, each with its own `README.md`/`config.yaml`:
|
|
96
|
+
- `ldetect_example/` — toy EUR chr2 example bundled with the original ldetect, used as a correctness fixture.
|
|
97
|
+
- `ldetect_original/` — full genome-wide reproduction of the published Berisa & Pickrell EUR/AFR/ASN LD blocks from public 1000 Genomes Phase 1 VCFs, plus diagnostic Snakefiles (`Snakefile.diagnostics`, `Snakefile.legacy_diagnostics`, `Snakefile.provenance_diagnostics`) used to isolate divergences from the reference.
|
|
98
|
+
- `MacDonald2022/` — reproduction of MacDonald et al. (2022) GRCh38 LD blocks (deCODE map + pyrho superpopulation maps).
|
|
99
|
+
- `r2_zarr_exactness/` — Zarr-based exactness checks.
|
|
100
|
+
- `benchmarks/` — standalone perf benchmarks (e.g. `bench_ld_kernel.py` for the Numba LD kernel, referenced in `docs/optimizations.md`).
|
|
101
|
+
- `_reference/` — vendored reference implementations used for parity testing and porting: `ldetect_original/ldetect/` (original Berisa & Pickrell code) and `LDblocks_GRCh38/` (MacDonald et al. 2022 scripts).
|
|
102
|
+
|
|
103
|
+
## Reproduction Status
|
|
104
|
+
|
|
105
|
+
Tracked in detail in `notes/logs/ldetect-original-main-pipeline-audit.md` (log of record) and `notes/findings/ldetect-original-reproduction.md` (current-state snapshot — start here). As of the latest full genome-wide run against the published Bitbucket `ldetect-data` reference BEDs:
|
|
106
|
+
|
|
107
|
+
- **ASN**: exact match, all 22 autosomes.
|
|
108
|
+
- **AFR**: exact except chr22. (chr11 was previously tracked as divergent too, but is resolved — a data-corruption bug in the published reference BED itself, not a real pipeline divergence.)
|
|
109
|
+
- **EUR**: exact block counts and coverage on every chromosome, but chr8–12 have shifted internal boundaries relative to the published reference (contiguous range bracketed by exact chr7/chr13 matches).
|
|
110
|
+
|
|
111
|
+
EUR chr8-12 and AFR chr22 are **parked as an accepted, documented residual divergence**, not an active investigation. An extensive diagnostic effort ruled out VCF release-version provenance (including a full pipeline rerun on 4 alternate releases plus one undocumented snapshot), SNP filtering, genetic map family, `Ne` assignment, duplicate/cross-partition handling, sample/panel provenance, and reference-BED structural integrity. The leading remaining hypothesis is an unidentified upstream input/provenance divergence for those chromosomes specifically (precision and legacy-downstream diagnostics rule out arbitrary implementation-choice ambiguity as the cause) — see `notes/findings/ldetect-original-reproduction.md`'s "If this is picked up again" section for the concrete next steps if this is ever revisited.
|
|
112
|
+
|
|
113
|
+
When investigating reproduction mismatches, read `notes/findings/ldetect-original-reproduction.md` first; it likely already rules out several hypotheses. MacDonald2022 reproduction status is tracked equivalently in `notes/findings/macdonald2022-reproduction.md` (summary) and `notes/logs/macdonald2022-pyrho-handoff.md`/`notes/logs/macdonald2022-interpolation-port.md` (logs of record).
|
|
114
|
+
|
|
115
|
+
## CLI Subcommands
|
|
116
|
+
|
|
117
|
+
`partition-chromosome`, `calc-covariance`, `covariance-summary`, `matrix-to-vector`, `find-minima`, `extract-bpoints`, `interpolate-maps`, `run`. Full argument documentation and worked examples for each are in `README.md` — that is the source of truth for CLI usage; keep it in sync with `_cli/cmd_*.py` when changing flags.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adam Youlin He
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ldetect-lite
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fast calculation of approximately independent LD blocks
|
|
5
|
+
Author: Adam He
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Adam Youlin He
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Keywords: GWAS,LD,bioinformatics,linkage-disequilibrium
|
|
29
|
+
Classifier: Development Status :: 3 - Alpha
|
|
30
|
+
Classifier: Intended Audience :: Science/Research
|
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
32
|
+
Classifier: Programming Language :: Python :: 3
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
35
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
36
|
+
Requires-Python: >=3.11
|
|
37
|
+
Requires-Dist: h5py>=3.10
|
|
38
|
+
Requires-Dist: hdf5plugin>=4.4
|
|
39
|
+
Requires-Dist: numba>=0.57
|
|
40
|
+
Requires-Dist: numpy>=1.24
|
|
41
|
+
Requires-Dist: scipy>=1.10
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
47
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
48
|
+
Requires-Dist: snakemake-executor-plugin-slurm; extra == 'dev'
|
|
49
|
+
Requires-Dist: snakemake>=8.0; extra == 'dev'
|
|
50
|
+
Provides-Extra: heatmap
|
|
51
|
+
Requires-Dist: matplotlib>=3.7; extra == 'heatmap'
|
|
52
|
+
Provides-Extra: profiling
|
|
53
|
+
Requires-Dist: psutil>=5.9; extra == 'profiling'
|
|
54
|
+
Description-Content-Type: text/markdown
|
|
55
|
+
|
|
56
|
+
# LDetect-lite
|
|
57
|
+
|
|
58
|
+
[](https://pypi.org/project/ldetect-lite/)
|
|
59
|
+
[](https://github.com/adamyhe/ldetect2/actions/workflows/tests.yml)
|
|
60
|
+
[](https://pepy.tech/projects/ldetect-lite)
|
|
61
|
+
|
|
62
|
+
A modern, fast re-implementation of [LDetect](https://bitbucket.org/nygcresearch/ldetect), a method for calculating approximately independent linkage disequilibrium (LD) blocks in the human genome. The algorithm is described in [Berisa & Pickrell, 2016](https://academic.oup.com/bioinformatics/article/32/2/283/1743626).
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
Install from PyPI via:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install ldetect-lite
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or, with [uv](https://docs.astral.sh/uv/):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv add ldetect-lite
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This installs three equivalent CLI entry points — `ldetect-lite`, `ldetect`, and `ldl` — so pick whichever is most convenient; examples below use `ldetect`.
|
|
79
|
+
|
|
80
|
+
The main `ldetect run` pipeline also requires [htslib](https://www.htslib.org/). Specifically, `tabix` is used to stream VCF files to `ldetect calc-covariance`, and so must be on PATH.
|
|
81
|
+
|
|
82
|
+
**Optional** (`--generate-heatmap`): install matplotlib with `pip install "ldetect-lite[heatmap]"`, or use `uv sync --extra heatmap` from a source checkout. Generating covariance heatmaps requires a matplotlib install.
|
|
83
|
+
|
|
84
|
+
### Development
|
|
85
|
+
|
|
86
|
+
Install from source
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/adamyhe/ldetect-lite.git
|
|
90
|
+
cd ldetect-lite
|
|
91
|
+
uv sync --extra dev
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
From a development checkout, run CLI commands through `uv run` so they use the managed environment.
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
|
|
98
|
+
### End-to-end pipeline
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
uv run ldetect run \
|
|
102
|
+
--genetic-map chr2.interpolated_genetic_map.gz \
|
|
103
|
+
--reference-panel 1000G.chr2.vcf.gz \
|
|
104
|
+
--individuals eurinds.txt \
|
|
105
|
+
--chromosome chr2 \
|
|
106
|
+
--output-dir results/chr2/
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This writes `results/chr2/chr2-ld-blocks.bed` — a BED file of approximately independent LD blocks.
|
|
110
|
+
|
|
111
|
+
Global options (before the subcommand):
|
|
112
|
+
|
|
113
|
+
- `-v / --verbosity {debug,info,warning,error}` — logging verbosity (default: `info`; use `warning` to silence progress messages, `debug` for full detail)
|
|
114
|
+
|
|
115
|
+
Options:
|
|
116
|
+
|
|
117
|
+
- `--ne FLOAT` — effective population size Ne used by the Wen & Stephens shrinkage estimator (default: 11418.0, the CEU/HapMap II value; reproduction configs may override this for non-European populations)
|
|
118
|
+
- `--cov-cutoff FLOAT` — LD pairs with absolute shrinkage correlation below this threshold are not written to disk, reducing storage (default: 1e-7)
|
|
119
|
+
- `--covariance-cache {compact,full}` — partition cache schema for `ldetect run` (default: `compact`). Compact caches write only canonical position pairs, `shrink_ld`, diagonals, and lookup indexes, which is enough for restartable matrix-to-vector, metric, and local-search steps. Use `full` when debugging or when later running full-matrix/heatmap readers.
|
|
120
|
+
- `--covariance-compression {lzf,zstd}` — HDF5 compression codec for covariance partitions (default: `zstd`). `zstd` is smaller and faster to read/write than `lzf` at equal precision — see `docs/optimizations.md`.
|
|
121
|
+
- `--n-snps-bw-bpoints N` — target mean number of SNPs between consecutive breakpoints; controls block granularity (default: 10000, following Berisa & Pickrell 2016). The target breakpoint count is `ceil(n_snps / N - 1)`. Mutually exclusive with `--n-bpoints`.
|
|
122
|
+
- `--n-bpoints N` — directly specify the number of breakpoints, bypassing the `--n-snps-bw-bpoints` formula; useful when replicating a published analysis with a known block count
|
|
123
|
+
- `--subset {fourier,fourier_ls,uniform,uniform_ls}` — which of the four breakpoint sets to write to the BED file (default: `fourier_ls`; see `docs/pipeline-steps.md` step 4)
|
|
124
|
+
- `--all-breakpoint-subsets` — compute all four breakpoint sets in the JSON output. By default, `run` computes only the requested `--subset` and its dependencies to avoid unused local-search work.
|
|
125
|
+
- `--workers N` — parallel workers for the pipeline (default: 1); set to the number of available cores to speed up covariance calculation (step 2) and, unless overridden below, matrix-to-vector, local search, and metric scoring as well
|
|
126
|
+
- `--matrix-workers N` — override parallel workers for matrix-to-vector partition processing (default: inherit `--workers`)
|
|
127
|
+
- `--local-search-workers N` — override parallel workers for local search (default: inherit `--workers`). Higher values can multiply RAM use because each worker loads its own covariance window.
|
|
128
|
+
- `--metric-workers N` — override parallel workers for streaming metric row passes during breakpoint scoring (default: inherit `--workers`)
|
|
129
|
+
- `--high-precision` — use 50-digit Decimal arithmetic for local search instead of the default float path (slower; mainly useful for exact reference comparisons)
|
|
130
|
+
|
|
131
|
+
Each of the five stages (partition, covariance, matrix-to-vector, find-minima, extract-bpoints) can also be run individually, along with a `covariance-summary` inspection utility — see `docs/pipeline-steps.md`.
|
|
132
|
+
|
|
133
|
+
### Interpolate genetic maps
|
|
134
|
+
|
|
135
|
+
Convert a recombination rate map (e.g. the [deCODE map](https://www.science.org/doi/10.1126/science.aau1043) or [HapMap-interpolated 1000G maps](https://github.com/joepickrell/1000-genomes-genetic-maps)) to per-SNP genetic positions required by steps 1 and 2:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
uv run ldetect interpolate-maps \
|
|
139
|
+
--snp-file snps.bed.gz \
|
|
140
|
+
--genetic-map recombination_map.gz \
|
|
141
|
+
--output chr2.interpolated_genetic_map.gz
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Arguments:
|
|
145
|
+
- `--snp-file PATH` — bgzipped BED file of SNP positions (columns: `chrom start end rs_id`); typically extracted from a filtered VCF with `bcftools query -f '%CHROM\t%POS0\t%POS\t%ID\n'`
|
|
146
|
+
- `--genetic-map PATH` — gzipped recombination map; interpolation is used to assign a cM value to each SNP position
|
|
147
|
+
- `--output PATH` — gzipped output map in the 3-column format expected by steps 1 and 2 (`rs_id position cM`)
|
|
148
|
+
- `--mode {point,interval}` (default: `point`) — interpolation algorithm:
|
|
149
|
+
- `point` — treats `--genetic-map` as discrete `(position, cM)` points and linearly interpolates between the two points bracketing each SNP. Correct for point-sampled maps (e.g. HapMap-interpolated 1000G maps).
|
|
150
|
+
- `interval` — treats each map row as the start of a genomic interval with its own recombination rate (`Begin, rate_cM_Mb, cumulative_cM_at_End`), matching MacDonald et al.'s R interpolation scripts ([`interpolate.R`](https://github.com/jmacdon/LDblocks_GRCh38/blob/master/scripts/interpolate.R)/[`interpolate_pyhro.R`](https://github.com/jmacdon/LDblocks_GRCh38/blob/master/scripts/interpolate_pyhro.R)). Required for interval-rate maps such as the deCODE map — feeding those into `point` mode silently uses the *next* interval's rate for SNPs in the *current* interval, an off-by-one bug that produced a ~0.001–0.003 cM error per SNP in earlier testing (see `notes/findings/macdonald2022-reproduction.md`).
|
|
151
|
+
|
|
152
|
+
## Algorithm
|
|
153
|
+
|
|
154
|
+
The pipeline detects LD block boundaries by finding local minima in a smoothed diagonal-sum signal derived from the shrinkage LD covariance matrix:
|
|
155
|
+
|
|
156
|
+
1. **Partition** — chromosome split into ~5000-SNP overlapping windows at low-recombination boundaries
|
|
157
|
+
2. **Covariance** — Wen & Stephens shrinkage estimator applied to phased haplotypes; shrinks sample correlations toward the expected LD decay to reduce finite-sample noise
|
|
158
|
+
3. **Matrix → vector** — each covariance matrix reduced to a `[position, diagonal_sum]` signal; troughs correspond to LD block boundaries
|
|
159
|
+
4. **Find minima** — binary search for optimal Hanning-window filter width; `scipy.signal.argrelextrema` finds local minima; local search refines each breakpoint using sum of squared inter-block correlations as the quality metric
|
|
160
|
+
5. **Extract** — chosen breakpoint set written as BED
|
|
161
|
+
|
|
162
|
+
The available breakpoint sets are `fourier` and `uniform` (raw minima from Fourier-filtered and uniformly-spaced candidates), plus `fourier_ls` and `uniform_ls` (after local search refinement). `fourier_ls` is the recommended output.
|
|
163
|
+
|
|
164
|
+
## Known limitations
|
|
165
|
+
|
|
166
|
+
`ldetect-lite` reproduces the published Berisa & Pickrell (2016) 1000 Genomes LD blocks exactly for ASN (all 22 autosomes) and AFR (all chromosomes except chr22), and matches EUR block counts and coverage exactly but with shifted internal boundaries on chr8–chr12. These two residual divergences (EUR chr8-12, AFR chr22) are understood to stem from an unidentified upstream input/provenance difference from the original authors' pipeline, not a bug in this implementation — an extensive diagnostic effort ruled out VCF release-version provenance, SNP filtering, genetic map family, `Ne` assignment, duplicate/cross-partition handling, and reference-BED integrity as causes. See `notes/findings/ldetect-original-reproduction.md` for the full writeup, and `notes/findings/macdonald2022-reproduction.md` for the equivalent status reproducing MacDonald et al. (2022)'s GRCh38 blocks.
|
|
167
|
+
|
|
168
|
+
## Pre-computed LD blocks
|
|
169
|
+
|
|
170
|
+
Pre-computed BED files for 1000 Genomes reference populations are available from in hg19 coordinates from the [original LDetect data repository](https://bitbucket.org/nygcresearch/ldetect-data) and in hg38 coordinates from a more recent effort by [MacDonald et al. (2022)](https://www.biorxiv.org/content/10.1101/2022.03.04.483057v2).
|
|
171
|
+
|
|
172
|
+
BED files produced by our work will be released once the code base leaves alpha/finish major breaking updates.
|