dnakit 0.1.0.dev0__py3-none-any.whl

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 (210) hide show
  1. dnakit/__init__.py +108 -0
  2. dnakit/_version.py +5 -0
  3. dnakit/alignment/__init__.py +6 -0
  4. dnakit/alignment/pairwise.py +512 -0
  5. dnakit/alignment/results.py +188 -0
  6. dnakit/backends/__init__.py +80 -0
  7. dnakit/backends/external.py +517 -0
  8. dnakit/backends/registry.py +207 -0
  9. dnakit/batch.py +452 -0
  10. dnakit/cache/__init__.py +5 -0
  11. dnakit/cache/store.py +424 -0
  12. dnakit/chunking.py +920 -0
  13. dnakit/cli/__init__.py +5 -0
  14. dnakit/cli/__main__.py +6 -0
  15. dnakit/cli/app.py +814 -0
  16. dnakit/cli/workflow.py +94 -0
  17. dnakit/config/__init__.py +5 -0
  18. dnakit/config/loader.py +381 -0
  19. dnakit/core/__init__.py +80 -0
  20. dnakit/core/_json.py +206 -0
  21. dnakit/core/backend_info.py +70 -0
  22. dnakit/core/collection.py +163 -0
  23. dnakit/core/coordinates.py +295 -0
  24. dnakit/core/enums.py +95 -0
  25. dnakit/core/facade.py +719 -0
  26. dnakit/core/feature.py +90 -0
  27. dnakit/core/gap.py +82 -0
  28. dnakit/core/issues.py +62 -0
  29. dnakit/core/provenance.py +305 -0
  30. dnakit/core/record.py +161 -0
  31. dnakit/core/results.py +134 -0
  32. dnakit/core/sequence.py +327 -0
  33. dnakit/datasets/__init__.py +100 -0
  34. dnakit/datasets/_advanced_shared.py +198 -0
  35. dnakit/datasets/_metadata.py +33 -0
  36. dnakit/datasets/advanced_deduplicate.py +183 -0
  37. dnakit/datasets/advanced_split.py +302 -0
  38. dnakit/datasets/clustering.py +286 -0
  39. dnakit/datasets/config.py +531 -0
  40. dnakit/datasets/deduplicate.py +303 -0
  41. dnakit/datasets/evaluation.py +244 -0
  42. dnakit/datasets/filter.py +135 -0
  43. dnakit/datasets/neural_clustering.py +221 -0
  44. dnakit/datasets/results.py +520 -0
  45. dnakit/datasets/split.py +450 -0
  46. dnakit/descriptors/__init__.py +102 -0
  47. dnakit/descriptors/_dinucleotide.py +345 -0
  48. dnakit/descriptors/_shared.py +180 -0
  49. dnakit/descriptors/all.py +917 -0
  50. dnakit/descriptors/basic.py +193 -0
  51. dnakit/descriptors/codon.py +195 -0
  52. dnakit/descriptors/complexity.py +201 -0
  53. dnakit/descriptors/entropy.py +101 -0
  54. dnakit/descriptors/homopolymer.py +105 -0
  55. dnakit/descriptors/kmer.py +88 -0
  56. dnakit/descriptors/results.py +286 -0
  57. dnakit/descriptors/schema.py +414 -0
  58. dnakit/descriptors/window.py +203 -0
  59. dnakit/download/__init__.py +54 -0
  60. dnakit/download/catalogs.py +166 -0
  61. dnakit/download/ena.py +131 -0
  62. dnakit/download/encode.py +103 -0
  63. dnakit/download/files.py +469 -0
  64. dnakit/download/indexes.py +233 -0
  65. dnakit/download/metadata.py +164 -0
  66. dnakit/download/models.py +227 -0
  67. dnakit/download/ncbi.py +333 -0
  68. dnakit/download/sequence.py +133 -0
  69. dnakit/download/ucsc.py +99 -0
  70. dnakit/evaluation/__init__.py +81 -0
  71. dnakit/evaluation/_shared.py +288 -0
  72. dnakit/evaluation/collection.py +270 -0
  73. dnakit/evaluation/config.py +591 -0
  74. dnakit/evaluation/distribution.py +287 -0
  75. dnakit/evaluation/frechet.py +223 -0
  76. dnakit/evaluation/generative.py +381 -0
  77. dnakit/evaluation/reference.py +409 -0
  78. dnakit/evaluation/results.py +137 -0
  79. dnakit/evaluation/scorecard.py +211 -0
  80. dnakit/evaluation/sequence.py +403 -0
  81. dnakit/evaluation/synthesis.py +249 -0
  82. dnakit/exceptions.py +189 -0
  83. dnakit/fingerprints/__init__.py +109 -0
  84. dnakit/fingerprints/_shared.py +116 -0
  85. dnakit/fingerprints/advanced.py +829 -0
  86. dnakit/fingerprints/bit.py +229 -0
  87. dnakit/fingerprints/encoding.py +351 -0
  88. dnakit/fingerprints/kmer.py +170 -0
  89. dnakit/fingerprints/results.py +531 -0
  90. dnakit/fingerprints/sketch.py +246 -0
  91. dnakit/io/__init__.py +112 -0
  92. dnakit/io/_advanced_common.py +105 -0
  93. dnakit/io/_formats.py +994 -0
  94. dnakit/io/annotations.py +1179 -0
  95. dnakit/io/api.py +759 -0
  96. dnakit/io/config.py +315 -0
  97. dnakit/io/genbank.py +523 -0
  98. dnakit/io/indexed.py +1146 -0
  99. dnakit/io/metadata.py +452 -0
  100. dnakit/io/parquet.py +56 -0
  101. dnakit/io/results.py +131 -0
  102. dnakit/io/source.py +138 -0
  103. dnakit/io/tables.py +1237 -0
  104. dnakit/molbio/__init__.py +101 -0
  105. dnakit/molbio/_shared.py +266 -0
  106. dnakit/molbio/assembly.py +279 -0
  107. dnakit/molbio/crispr.py +181 -0
  108. dnakit/molbio/optimization.py +897 -0
  109. dnakit/molbio/primer3_design.py +667 -0
  110. dnakit/molbio/primers.py +899 -0
  111. dnakit/molbio/restriction.py +573 -0
  112. dnakit/molbio/results.py +583 -0
  113. dnakit/ops/__init__.py +588 -0
  114. dnakit/ops/_common.py +201 -0
  115. dnakit/ops/circular.py +193 -0
  116. dnakit/ops/concat.py +204 -0
  117. dnakit/ops/direction.py +27 -0
  118. dnakit/ops/edit.py +443 -0
  119. dnakit/ops/evolution.py +991 -0
  120. dnakit/ops/mutation.py +151 -0
  121. dnakit/ops/records.py +1012 -0
  122. dnakit/ops/translation.py +257 -0
  123. dnakit/patterns/__init__.py +81 -0
  124. dnakit/patterns/_shared.py +282 -0
  125. dnakit/patterns/coding.py +360 -0
  126. dnakit/patterns/crispr.py +337 -0
  127. dnakit/patterns/motif.py +721 -0
  128. dnakit/patterns/regions.py +341 -0
  129. dnakit/patterns/repeats.py +480 -0
  130. dnakit/patterns/restriction.py +281 -0
  131. dnakit/patterns/results.py +411 -0
  132. dnakit/py.typed +1 -0
  133. dnakit/references/__init__.py +27 -0
  134. dnakit/references/models.py +155 -0
  135. dnakit/references/ncbi.py +785 -0
  136. dnakit/representations/__init__.py +29 -0
  137. dnakit/representations/backends.py +928 -0
  138. dnakit/representations/checkpoints.py +356 -0
  139. dnakit/representations/extraction.py +220 -0
  140. dnakit/representations/models.py +395 -0
  141. dnakit/representations/results.py +79 -0
  142. dnakit/search/__init__.py +132 -0
  143. dnakit/search/_http.py +271 -0
  144. dnakit/search/_shared.py +108 -0
  145. dnakit/search/blast.py +516 -0
  146. dnakit/search/ena.py +245 -0
  147. dnakit/search/encode.py +102 -0
  148. dnakit/search/ensembl.py +874 -0
  149. dnakit/search/models.py +159 -0
  150. dnakit/search/ncbi.py +756 -0
  151. dnakit/search/ucsc.py +388 -0
  152. dnakit/secondary_structure/__init__.py +38 -0
  153. dnakit/secondary_structure/dotbracket.py +410 -0
  154. dnakit/secondary_structure/nupack.py +589 -0
  155. dnakit/secondary_structure/results.py +142 -0
  156. dnakit/similarity/__init__.py +125 -0
  157. dnakit/similarity/_shared.py +152 -0
  158. dnakit/similarity/approximate.py +258 -0
  159. dnakit/similarity/compare.py +162 -0
  160. dnakit/similarity/dashing.py +936 -0
  161. dnakit/similarity/distance.py +279 -0
  162. dnakit/similarity/index.py +365 -0
  163. dnakit/similarity/matrix.py +254 -0
  164. dnakit/similarity/results.py +676 -0
  165. dnakit/similarity/search.py +286 -0
  166. dnakit/similarity/sketch.py +96 -0
  167. dnakit/similarity/vector.py +662 -0
  168. dnakit/standardize/__init__.py +77 -0
  169. dnakit/standardize/_shared.py +100 -0
  170. dnakit/standardize/config.py +411 -0
  171. dnakit/standardize/gaps.py +1060 -0
  172. dnakit/standardize/normalize.py +565 -0
  173. dnakit/standardize/results.py +247 -0
  174. dnakit/standardize/validate.py +487 -0
  175. dnakit/structure3d/__init__.py +47 -0
  176. dnakit/structure3d/_shared.py +47 -0
  177. dnakit/structure3d/geometry.py +773 -0
  178. dnakit/structure3d/pdb.py +253 -0
  179. dnakit/structure3d/results.py +233 -0
  180. dnakit/structure3d/threedna.py +174 -0
  181. dnakit/thermodynamics/__init__.py +112 -0
  182. dnakit/thermodynamics/_shared.py +92 -0
  183. dnakit/thermodynamics/backends.py +653 -0
  184. dnakit/thermodynamics/calculations.py +837 -0
  185. dnakit/thermodynamics/config.py +175 -0
  186. dnakit/thermodynamics/equilibrium.py +340 -0
  187. dnakit/thermodynamics/optics.py +385 -0
  188. dnakit/thermodynamics/parameters.py +79 -0
  189. dnakit/thermodynamics/results.py +445 -0
  190. dnakit/visualization/__init__.py +43 -0
  191. dnakit/visualization/_svg.py +185 -0
  192. dnakit/visualization/advanced.py +314 -0
  193. dnakit/visualization/config.py +255 -0
  194. dnakit/visualization/export.py +349 -0
  195. dnakit/visualization/matrix.py +279 -0
  196. dnakit/visualization/report.py +431 -0
  197. dnakit/visualization/results.py +236 -0
  198. dnakit/visualization/sequence.py +459 -0
  199. dnakit/workflows/__init__.py +45 -0
  200. dnakit/workflows/manifest.py +365 -0
  201. dnakit/workflows/runner.py +1548 -0
  202. dnakit/workflows/schema.py +898 -0
  203. dnakit-0.1.0.dev0.dist-info/METADATA +126 -0
  204. dnakit-0.1.0.dev0.dist-info/RECORD +210 -0
  205. dnakit-0.1.0.dev0.dist-info/WHEEL +5 -0
  206. dnakit-0.1.0.dev0.dist-info/entry_points.txt +2 -0
  207. dnakit-0.1.0.dev0.dist-info/licenses/DISCLAIMER.md +26 -0
  208. dnakit-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
  209. dnakit-0.1.0.dev0.dist-info/licenses/THIRD_PARTY_NOTICES.md +73 -0
  210. dnakit-0.1.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,181 @@
1
+ """CRISPR candidate scanning with bounded sequence-only off-target enumeration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+
7
+ from dnakit.core import DNASequence, Issue, IssueSeverity, Strand
8
+ from dnakit.exceptions import ConfigurationError
9
+ from dnakit.patterns import PAMRule, scan_pam_candidates
10
+ from dnakit.patterns.results import GuideCandidate
11
+
12
+ from ._shared import freeze_parameters, native_provenance, require_sequence, validate_positive_int
13
+ from .primers import match_primer
14
+ from .results import CrisprScanResult, OffTargetHit
15
+
16
+
17
+ def scan_crispr_candidates(
18
+ target: DNASequence,
19
+ rule: str | PAMRule,
20
+ *,
21
+ guide_length: int | None = None,
22
+ min_gc: float = 0.0,
23
+ max_gc: float = 1.0,
24
+ references: Mapping[str, DNASequence] | None = None,
25
+ max_off_target_mismatches: int = 0,
26
+ max_candidates: int = 10_000,
27
+ max_off_targets: int = 100_000,
28
+ max_reference_sequences: int = 10_000,
29
+ max_reference_length: int = 10_000_000,
30
+ max_total_comparison_cells: int = 500_000_000,
31
+ ) -> CrisprScanResult:
32
+ """Reuse PAM rules and optionally enumerate ungapped sequence matches.
33
+
34
+ This function intentionally does not predict editing efficiency, bulges,
35
+ chromatin effects, or biological off-target risk.
36
+ """
37
+
38
+ require_sequence(
39
+ target,
40
+ operation="CRISPR candidate scanning",
41
+ max_length=max_reference_length,
42
+ )
43
+ validate_positive_int(max_candidates, "max_candidates", maximum=1_000_000)
44
+ validate_positive_int(max_off_targets, "max_off_targets", maximum=10_000_000)
45
+ validate_positive_int(max_reference_sequences, "max_reference_sequences", maximum=1_000_000)
46
+ validate_positive_int(
47
+ max_total_comparison_cells,
48
+ "max_total_comparison_cells",
49
+ maximum=10_000_000_000,
50
+ )
51
+ if (
52
+ isinstance(max_off_target_mismatches, bool)
53
+ or not isinstance(max_off_target_mismatches, int)
54
+ or max_off_target_mismatches < 0
55
+ ):
56
+ raise ConfigurationError("max_off_target_mismatches must be non-negative.")
57
+ candidate_scan = scan_pam_candidates(
58
+ target,
59
+ rule,
60
+ guide_length=guide_length,
61
+ min_gc=min_gc,
62
+ max_gc=max_gc,
63
+ max_matches=max_candidates,
64
+ max_scan_length=max_reference_length,
65
+ )
66
+ resolved_references = references or {}
67
+ if len(resolved_references) > max_reference_sequences:
68
+ raise ConfigurationError("Reference collection exceeds max_reference_sequences.")
69
+ reference_items = tuple(resolved_references.items())
70
+ total_reference_length = 0
71
+ for reference_id, sequence in reference_items:
72
+ if not isinstance(reference_id, str) or not reference_id.strip():
73
+ raise ConfigurationError("Reference identifiers must be non-empty strings.")
74
+ total_reference_length += len(
75
+ require_sequence(
76
+ sequence,
77
+ operation="CRISPR off-target enumeration",
78
+ max_length=max_reference_length,
79
+ canonical=True,
80
+ )
81
+ )
82
+ estimated_cells = sum(
83
+ total_reference_length * len(candidate.guide_sequence) * 2
84
+ for candidate in candidate_scan.hits
85
+ )
86
+ if estimated_cells > max_total_comparison_cells:
87
+ raise ConfigurationError(
88
+ "CRISPR off-target enumeration exceeds max_total_comparison_cells.",
89
+ code="CRISPR_OFF_TARGET_LIMIT_EXCEEDED",
90
+ context={
91
+ "estimated_cells": estimated_cells,
92
+ "max_total_comparison_cells": max_total_comparison_cells,
93
+ },
94
+ )
95
+ off_targets: list[OffTargetHit] = []
96
+ off_target_truncated = False
97
+ for candidate in candidate_scan.hits:
98
+ if not isinstance(candidate, GuideCandidate):
99
+ raise AssertionError("Unexpected PAM candidate result type.")
100
+ guide = DNASequence(candidate.guide_sequence)
101
+ for reference_id, reference in reference_items:
102
+ matches = match_primer(
103
+ guide,
104
+ reference,
105
+ primer_name="guide",
106
+ strand=Strand.BOTH,
107
+ max_mismatches=max_off_target_mismatches,
108
+ max_hits=max_off_targets,
109
+ max_template_length=max_reference_length,
110
+ max_comparison_cells=max_total_comparison_cells,
111
+ )
112
+ for hit in matches.hits:
113
+ if len(off_targets) >= max_off_targets:
114
+ off_target_truncated = True
115
+ break
116
+ off_targets.append(
117
+ OffTargetHit(
118
+ guide_sequence=candidate.guide_sequence,
119
+ reference_id=reference_id,
120
+ strand=hit.strand,
121
+ start=hit.start,
122
+ end=hit.end,
123
+ mismatch_positions_5to3=hit.mismatch_positions_5to3,
124
+ wraps_origin=hit.wraps_origin,
125
+ )
126
+ )
127
+ if matches.truncated:
128
+ off_target_truncated = True
129
+ if off_target_truncated:
130
+ break
131
+ if off_target_truncated:
132
+ break
133
+ issues: list[Issue] = []
134
+ if not reference_items:
135
+ issues.append(
136
+ Issue(
137
+ "CRISPR_REFERENCE_NOT_PROVIDED",
138
+ IssueSeverity.INFO,
139
+ "Candidates were returned without sequence off-target enumeration.",
140
+ )
141
+ )
142
+ issues.append(
143
+ Issue(
144
+ "CRISPR_BIOLOGICAL_RISK_NOT_PREDICTED",
145
+ IssueSeverity.INFO,
146
+ "Sequence matches are not editing-efficiency or biological off-target predictions.",
147
+ )
148
+ )
149
+ return CrisprScanResult(
150
+ candidates=tuple(candidate_scan.hits),
151
+ off_targets=tuple(off_targets),
152
+ candidate_truncated=candidate_scan.truncated,
153
+ off_target_truncated=off_target_truncated,
154
+ efficiency_prediction_performed=False,
155
+ method="pam_scan_plus_bounded_ungapped_sequence_matching",
156
+ algorithm_version="dnakit-crispr-candidates-v1",
157
+ parameters=freeze_parameters(
158
+ {
159
+ "guide_length": guide_length,
160
+ "min_gc": min_gc,
161
+ "max_gc": max_gc,
162
+ "reference_count": len(reference_items),
163
+ "max_off_target_mismatches": max_off_target_mismatches,
164
+ "allow_indels_or_bulges": False,
165
+ "efficiency_prediction": False,
166
+ "estimated_comparison_cells": estimated_cells,
167
+ "max_total_comparison_cells": max_total_comparison_cells,
168
+ "max_candidates": max_candidates,
169
+ "max_off_targets": max_off_targets,
170
+ }
171
+ ),
172
+ provenance=native_provenance(
173
+ reimplementation=True,
174
+ reference_name="DNAKit common PAM rule definitions",
175
+ reference_version="common-nucleases-v1",
176
+ ),
177
+ issues=tuple(issues),
178
+ )
179
+
180
+
181
+ __all__ = ["scan_crispr_candidates"]