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.
Files changed (162) hide show
  1. ldetect_lite-0.1.0/.gitattributes +2 -0
  2. ldetect_lite-0.1.0/.github/workflows/publish.yml +47 -0
  3. ldetect_lite-0.1.0/.github/workflows/tests.yml +50 -0
  4. ldetect_lite-0.1.0/.gitignore +203 -0
  5. ldetect_lite-0.1.0/AGENTS.md +117 -0
  6. ldetect_lite-0.1.0/LICENSE +21 -0
  7. ldetect_lite-0.1.0/PKG-INFO +172 -0
  8. ldetect_lite-0.1.0/README.md +117 -0
  9. ldetect_lite-0.1.0/benchmarks/bench_ld_kernel.py +154 -0
  10. ldetect_lite-0.1.0/benchmarks/ld_numba_speedup.pdf +0 -0
  11. ldetect_lite-0.1.0/docs/optimizations.md +167 -0
  12. ldetect_lite-0.1.0/docs/pipeline-steps.md +142 -0
  13. ldetect_lite-0.1.0/examples/MacDonald2022/Snakefile +756 -0
  14. ldetect_lite-0.1.0/examples/MacDonald2022/config.yaml +137 -0
  15. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/compare_blocks.py +185 -0
  16. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/compare_boundaries.py +110 -0
  17. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/compare_maps.py +181 -0
  18. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/convert_decode_map.py +80 -0
  19. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/diagnose_boundaries.py +444 -0
  20. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/download_data.py +100 -0
  21. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/postprocess.py +177 -0
  22. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/prep_individuals.py +96 -0
  23. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/sync_results.sh +28 -0
  24. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/validate_maps.py +123 -0
  25. ldetect_lite-0.1.0/examples/MacDonald2022/scripts/verify_local_search.py +189 -0
  26. ldetect_lite-0.1.0/examples/ldetect_example/README.md +37 -0
  27. ldetect_lite-0.1.0/examples/ldetect_example/Snakefile +284 -0
  28. ldetect_lite-0.1.0/examples/ldetect_example/config.yaml +15 -0
  29. ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_bed.py +68 -0
  30. ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_bpoints.py +99 -0
  31. ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_partitions.py +81 -0
  32. ldetect_lite-0.1.0/examples/ldetect_example/scripts/compare_vector.py +82 -0
  33. ldetect_lite-0.1.0/examples/ldetect_example/scripts/convert_covariance.py +102 -0
  34. ldetect_lite-0.1.0/examples/ldetect_original/README.md +437 -0
  35. ldetect_lite-0.1.0/examples/ldetect_original/Snakefile +293 -0
  36. ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.alternate_source_rerun +366 -0
  37. ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.compression_diagnostics +307 -0
  38. ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.diagnostics +514 -0
  39. ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.legacy_diagnostics +335 -0
  40. ldetect_lite-0.1.0/examples/ldetect_original/Snakefile.provenance_diagnostics +291 -0
  41. ldetect_lite-0.1.0/examples/ldetect_original/alternate_source_rerun.yaml +19 -0
  42. ldetect_lite-0.1.0/examples/ldetect_original/compression_diagnostics.yaml +29 -0
  43. ldetect_lite-0.1.0/examples/ldetect_original/config.yaml +47 -0
  44. ldetect_lite-0.1.0/examples/ldetect_original/diagnostics.yaml +30 -0
  45. ldetect_lite-0.1.0/examples/ldetect_original/legacy_diagnostics.yaml +31 -0
  46. ldetect_lite-0.1.0/examples/ldetect_original/plots/afr_chr22_concordant_only.png +0 -0
  47. ldetect_lite-0.1.0/examples/ldetect_original/plots/afr_chr22_divergent_only.png +0 -0
  48. ldetect_lite-0.1.0/examples/ldetect_original/plots/afr_chr22_mixed.png +0 -0
  49. ldetect_lite-0.1.0/examples/ldetect_original/plots/eur_chr10_concordant_only.png +0 -0
  50. ldetect_lite-0.1.0/examples/ldetect_original/plots/eur_chr10_divergent_only.png +0 -0
  51. ldetect_lite-0.1.0/examples/ldetect_original/plots/eur_chr10_mixed.png +0 -0
  52. ldetect_lite-0.1.0/examples/ldetect_original/provenance_diagnostics.yaml +61 -0
  53. ldetect_lite-0.1.0/examples/ldetect_original/scripts/check_reference_beds.py +104 -0
  54. ldetect_lite-0.1.0/examples/ldetect_original/scripts/combine_tsv.py +38 -0
  55. ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_blocks.py +197 -0
  56. ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_compression.py +246 -0
  57. ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_legacy_outputs.py +146 -0
  58. ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_partition_overlap_duplicates.py +203 -0
  59. ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_vcf_ld.py +387 -0
  60. ldetect_lite-0.1.0/examples/ldetect_original/scripts/compare_vcf_positions.py +97 -0
  61. ldetect_lite-0.1.0/examples/ldetect_original/scripts/diagnose_inputs.py +169 -0
  62. ldetect_lite-0.1.0/examples/ldetect_original/scripts/diagnose_run.py +243 -0
  63. ldetect_lite-0.1.0/examples/ldetect_original/scripts/download_legacy_example_data.py +79 -0
  64. ldetect_lite-0.1.0/examples/ldetect_original/scripts/download_provenance_input.py +68 -0
  65. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_hdf5_to_gz.py +70 -0
  66. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/LDETECT_LICENSE.txt +9 -0
  67. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/P01_matrix_to_vector_pipeline.py +80 -0
  68. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/P02_minima_pipeline.py +233 -0
  69. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/P03_extract_bpoints.py +42 -0
  70. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/commanderline/__init__.py +2 -0
  71. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/commanderline/commander_line.py +15 -0
  72. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/__init__.py +0 -0
  73. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/__init__.py +0 -0
  74. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/binary_search.py +56 -0
  75. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/filters.py +58 -0
  76. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/flat_file.py +367 -0
  77. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/flat_file_consts.py +80 -0
  78. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/baselib/read_data.py +60 -0
  79. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E03_matrix_to_vector.py +651 -0
  80. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E05_find_minima.py +147 -0
  81. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E07_metric.py +446 -0
  82. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/E08_local_search.py +624 -0
  83. ldetect_lite-0.1.0/examples/ldetect_original/scripts/legacy_ldetect/ldetect/pipeline_elements/__init__.py +0 -0
  84. ldetect_lite-0.1.0/examples/ldetect_original/scripts/plot_profile_timeline.py +159 -0
  85. ldetect_lite-0.1.0/examples/ldetect_original/scripts/plot_resource_scaling.py +198 -0
  86. ldetect_lite-0.1.0/examples/ldetect_original/scripts/plot_vector_boundaries.py +160 -0
  87. ldetect_lite-0.1.0/examples/ldetect_original/scripts/prep_individuals.py +77 -0
  88. ldetect_lite-0.1.0/examples/ldetect_original/scripts/profile_run.py +159 -0
  89. ldetect_lite-0.1.0/examples/ldetect_original/scripts/run_legacy_ldetect.py +199 -0
  90. ldetect_lite-0.1.0/examples/ldetect_original/scripts/snp_filter_diagnostics.py +158 -0
  91. ldetect_lite-0.1.0/notes/README.md +17 -0
  92. ldetect_lite-0.1.0/notes/findings/ldetect-original-reproduction.md +44 -0
  93. ldetect_lite-0.1.0/notes/findings/macdonald2022-reproduction.md +37 -0
  94. ldetect_lite-0.1.0/notes/logs/implementation-plan.md +340 -0
  95. ldetect_lite-0.1.0/notes/logs/ldetect-original-main-pipeline-audit.md +1273 -0
  96. ldetect_lite-0.1.0/notes/logs/local-search-divergence-asn22.md +261 -0
  97. ldetect_lite-0.1.0/notes/logs/macdonald2022-boundary-diagnostics.md +244 -0
  98. ldetect_lite-0.1.0/notes/logs/macdonald2022-interpolation-port.md +143 -0
  99. ldetect_lite-0.1.0/notes/logs/macdonald2022-pyrho-handoff.md +1087 -0
  100. ldetect_lite-0.1.0/notes/logs/post-covariance-optimization-review.md +136 -0
  101. ldetect_lite-0.1.0/pyproject.toml +76 -0
  102. ldetect_lite-0.1.0/src/ldetect_lite/__init__.py +8 -0
  103. ldetect_lite-0.1.0/src/ldetect_lite/_cli/__init__.py +0 -0
  104. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_covariance.py +75 -0
  105. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_covariance_summary.py +95 -0
  106. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_extract_bpoints.py +70 -0
  107. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_find_minima.py +150 -0
  108. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_interpolate_maps.py +61 -0
  109. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_matrix_to_vector.py +95 -0
  110. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_partition.py +70 -0
  111. ldetect_lite-0.1.0/src/ldetect_lite/_cli/cmd_run.py +415 -0
  112. ldetect_lite-0.1.0/src/ldetect_lite/_cli/main.py +64 -0
  113. ldetect_lite-0.1.0/src/ldetect_lite/_util/__init__.py +0 -0
  114. ldetect_lite-0.1.0/src/ldetect_lite/_util/binary_search.py +67 -0
  115. ldetect_lite-0.1.0/src/ldetect_lite/_util/covariance_array.py +1024 -0
  116. ldetect_lite-0.1.0/src/ldetect_lite/_util/covariance_summary.py +220 -0
  117. ldetect_lite-0.1.0/src/ldetect_lite/_util/intervals.py +125 -0
  118. ldetect_lite-0.1.0/src/ldetect_lite/_util/logging.py +26 -0
  119. ldetect_lite-0.1.0/src/ldetect_lite/_util/memory.py +60 -0
  120. ldetect_lite-0.1.0/src/ldetect_lite/_util/vector_array.py +908 -0
  121. ldetect_lite-0.1.0/src/ldetect_lite/filters.py +82 -0
  122. ldetect_lite-0.1.0/src/ldetect_lite/find_minima.py +159 -0
  123. ldetect_lite-0.1.0/src/ldetect_lite/interpolate_maps.py +165 -0
  124. ldetect_lite-0.1.0/src/ldetect_lite/io/__init__.py +0 -0
  125. ldetect_lite-0.1.0/src/ldetect_lite/io/bed.py +118 -0
  126. ldetect_lite-0.1.0/src/ldetect_lite/io/covariance.py +390 -0
  127. ldetect_lite-0.1.0/src/ldetect_lite/io/covariance_hdf5.py +714 -0
  128. ldetect_lite-0.1.0/src/ldetect_lite/io/partitions.py +110 -0
  129. ldetect_lite-0.1.0/src/ldetect_lite/io/vcf.py +17 -0
  130. ldetect_lite-0.1.0/src/ldetect_lite/local_search.py +1821 -0
  131. ldetect_lite-0.1.0/src/ldetect_lite/matrix_analysis.py +511 -0
  132. ldetect_lite-0.1.0/src/ldetect_lite/metric.py +265 -0
  133. ldetect_lite-0.1.0/src/ldetect_lite/pipeline.py +662 -0
  134. ldetect_lite-0.1.0/src/ldetect_lite/shrinkage.py +846 -0
  135. ldetect_lite-0.1.0/tests/__init__.py +0 -0
  136. ldetect_lite-0.1.0/tests/_partition_fixtures.py +190 -0
  137. ldetect_lite-0.1.0/tests/conftest.py +63 -0
  138. ldetect_lite-0.1.0/tests/integration/__init__.py +0 -0
  139. ldetect_lite-0.1.0/tests/integration/test_pipeline.py +367 -0
  140. ldetect_lite-0.1.0/tests/test_bed.py +195 -0
  141. ldetect_lite-0.1.0/tests/test_binary_search.py +178 -0
  142. ldetect_lite-0.1.0/tests/test_cmd_covariance.py +34 -0
  143. ldetect_lite-0.1.0/tests/test_cmd_find_minima.py +41 -0
  144. ldetect_lite-0.1.0/tests/test_cmd_matrix_to_vector.py +33 -0
  145. ldetect_lite-0.1.0/tests/test_cmd_run.py +113 -0
  146. ldetect_lite-0.1.0/tests/test_compare_compression_script.py +323 -0
  147. ldetect_lite-0.1.0/tests/test_covariance_array.py +92 -0
  148. ldetect_lite-0.1.0/tests/test_covariance_io.py +782 -0
  149. ldetect_lite-0.1.0/tests/test_covariance_summary.py +200 -0
  150. ldetect_lite-0.1.0/tests/test_duplicate_overlap_integration.py +151 -0
  151. ldetect_lite-0.1.0/tests/test_filters.py +108 -0
  152. ldetect_lite-0.1.0/tests/test_find_minima.py +23 -0
  153. ldetect_lite-0.1.0/tests/test_interpolate_maps.py +255 -0
  154. ldetect_lite-0.1.0/tests/test_intervals.py +42 -0
  155. ldetect_lite-0.1.0/tests/test_local_search.py +819 -0
  156. ldetect_lite-0.1.0/tests/test_macdonald_diagnose_boundaries.py +81 -0
  157. ldetect_lite-0.1.0/tests/test_macdonald_validate_maps.py +64 -0
  158. ldetect_lite-0.1.0/tests/test_metric.py +281 -0
  159. ldetect_lite-0.1.0/tests/test_partitions.py +131 -0
  160. ldetect_lite-0.1.0/tests/test_shrinkage.py +479 -0
  161. ldetect_lite-0.1.0/tests/test_vcf.py +23 -0
  162. ldetect_lite-0.1.0/uv.lock +2442 -0
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/ldetect-lite)](https://pypi.org/project/ldetect-lite/)
59
+ [![Tests](https://github.com/adamyhe/ldetect2/actions/workflows/tests.yml/badge.svg)](https://github.com/adamyhe/ldetect2/actions/workflows/tests.yml)
60
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/ldetect-lite?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](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.