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.
- dnakit/__init__.py +108 -0
- dnakit/_version.py +5 -0
- dnakit/alignment/__init__.py +6 -0
- dnakit/alignment/pairwise.py +512 -0
- dnakit/alignment/results.py +188 -0
- dnakit/backends/__init__.py +80 -0
- dnakit/backends/external.py +517 -0
- dnakit/backends/registry.py +207 -0
- dnakit/batch.py +452 -0
- dnakit/cache/__init__.py +5 -0
- dnakit/cache/store.py +424 -0
- dnakit/chunking.py +920 -0
- dnakit/cli/__init__.py +5 -0
- dnakit/cli/__main__.py +6 -0
- dnakit/cli/app.py +814 -0
- dnakit/cli/workflow.py +94 -0
- dnakit/config/__init__.py +5 -0
- dnakit/config/loader.py +381 -0
- dnakit/core/__init__.py +80 -0
- dnakit/core/_json.py +206 -0
- dnakit/core/backend_info.py +70 -0
- dnakit/core/collection.py +163 -0
- dnakit/core/coordinates.py +295 -0
- dnakit/core/enums.py +95 -0
- dnakit/core/facade.py +719 -0
- dnakit/core/feature.py +90 -0
- dnakit/core/gap.py +82 -0
- dnakit/core/issues.py +62 -0
- dnakit/core/provenance.py +305 -0
- dnakit/core/record.py +161 -0
- dnakit/core/results.py +134 -0
- dnakit/core/sequence.py +327 -0
- dnakit/datasets/__init__.py +100 -0
- dnakit/datasets/_advanced_shared.py +198 -0
- dnakit/datasets/_metadata.py +33 -0
- dnakit/datasets/advanced_deduplicate.py +183 -0
- dnakit/datasets/advanced_split.py +302 -0
- dnakit/datasets/clustering.py +286 -0
- dnakit/datasets/config.py +531 -0
- dnakit/datasets/deduplicate.py +303 -0
- dnakit/datasets/evaluation.py +244 -0
- dnakit/datasets/filter.py +135 -0
- dnakit/datasets/neural_clustering.py +221 -0
- dnakit/datasets/results.py +520 -0
- dnakit/datasets/split.py +450 -0
- dnakit/descriptors/__init__.py +102 -0
- dnakit/descriptors/_dinucleotide.py +345 -0
- dnakit/descriptors/_shared.py +180 -0
- dnakit/descriptors/all.py +917 -0
- dnakit/descriptors/basic.py +193 -0
- dnakit/descriptors/codon.py +195 -0
- dnakit/descriptors/complexity.py +201 -0
- dnakit/descriptors/entropy.py +101 -0
- dnakit/descriptors/homopolymer.py +105 -0
- dnakit/descriptors/kmer.py +88 -0
- dnakit/descriptors/results.py +286 -0
- dnakit/descriptors/schema.py +414 -0
- dnakit/descriptors/window.py +203 -0
- dnakit/download/__init__.py +54 -0
- dnakit/download/catalogs.py +166 -0
- dnakit/download/ena.py +131 -0
- dnakit/download/encode.py +103 -0
- dnakit/download/files.py +469 -0
- dnakit/download/indexes.py +233 -0
- dnakit/download/metadata.py +164 -0
- dnakit/download/models.py +227 -0
- dnakit/download/ncbi.py +333 -0
- dnakit/download/sequence.py +133 -0
- dnakit/download/ucsc.py +99 -0
- dnakit/evaluation/__init__.py +81 -0
- dnakit/evaluation/_shared.py +288 -0
- dnakit/evaluation/collection.py +270 -0
- dnakit/evaluation/config.py +591 -0
- dnakit/evaluation/distribution.py +287 -0
- dnakit/evaluation/frechet.py +223 -0
- dnakit/evaluation/generative.py +381 -0
- dnakit/evaluation/reference.py +409 -0
- dnakit/evaluation/results.py +137 -0
- dnakit/evaluation/scorecard.py +211 -0
- dnakit/evaluation/sequence.py +403 -0
- dnakit/evaluation/synthesis.py +249 -0
- dnakit/exceptions.py +189 -0
- dnakit/fingerprints/__init__.py +109 -0
- dnakit/fingerprints/_shared.py +116 -0
- dnakit/fingerprints/advanced.py +829 -0
- dnakit/fingerprints/bit.py +229 -0
- dnakit/fingerprints/encoding.py +351 -0
- dnakit/fingerprints/kmer.py +170 -0
- dnakit/fingerprints/results.py +531 -0
- dnakit/fingerprints/sketch.py +246 -0
- dnakit/io/__init__.py +112 -0
- dnakit/io/_advanced_common.py +105 -0
- dnakit/io/_formats.py +994 -0
- dnakit/io/annotations.py +1179 -0
- dnakit/io/api.py +759 -0
- dnakit/io/config.py +315 -0
- dnakit/io/genbank.py +523 -0
- dnakit/io/indexed.py +1146 -0
- dnakit/io/metadata.py +452 -0
- dnakit/io/parquet.py +56 -0
- dnakit/io/results.py +131 -0
- dnakit/io/source.py +138 -0
- dnakit/io/tables.py +1237 -0
- dnakit/molbio/__init__.py +101 -0
- dnakit/molbio/_shared.py +266 -0
- dnakit/molbio/assembly.py +279 -0
- dnakit/molbio/crispr.py +181 -0
- dnakit/molbio/optimization.py +897 -0
- dnakit/molbio/primer3_design.py +667 -0
- dnakit/molbio/primers.py +899 -0
- dnakit/molbio/restriction.py +573 -0
- dnakit/molbio/results.py +583 -0
- dnakit/ops/__init__.py +588 -0
- dnakit/ops/_common.py +201 -0
- dnakit/ops/circular.py +193 -0
- dnakit/ops/concat.py +204 -0
- dnakit/ops/direction.py +27 -0
- dnakit/ops/edit.py +443 -0
- dnakit/ops/evolution.py +991 -0
- dnakit/ops/mutation.py +151 -0
- dnakit/ops/records.py +1012 -0
- dnakit/ops/translation.py +257 -0
- dnakit/patterns/__init__.py +81 -0
- dnakit/patterns/_shared.py +282 -0
- dnakit/patterns/coding.py +360 -0
- dnakit/patterns/crispr.py +337 -0
- dnakit/patterns/motif.py +721 -0
- dnakit/patterns/regions.py +341 -0
- dnakit/patterns/repeats.py +480 -0
- dnakit/patterns/restriction.py +281 -0
- dnakit/patterns/results.py +411 -0
- dnakit/py.typed +1 -0
- dnakit/references/__init__.py +27 -0
- dnakit/references/models.py +155 -0
- dnakit/references/ncbi.py +785 -0
- dnakit/representations/__init__.py +29 -0
- dnakit/representations/backends.py +928 -0
- dnakit/representations/checkpoints.py +356 -0
- dnakit/representations/extraction.py +220 -0
- dnakit/representations/models.py +395 -0
- dnakit/representations/results.py +79 -0
- dnakit/search/__init__.py +132 -0
- dnakit/search/_http.py +271 -0
- dnakit/search/_shared.py +108 -0
- dnakit/search/blast.py +516 -0
- dnakit/search/ena.py +245 -0
- dnakit/search/encode.py +102 -0
- dnakit/search/ensembl.py +874 -0
- dnakit/search/models.py +159 -0
- dnakit/search/ncbi.py +756 -0
- dnakit/search/ucsc.py +388 -0
- dnakit/secondary_structure/__init__.py +38 -0
- dnakit/secondary_structure/dotbracket.py +410 -0
- dnakit/secondary_structure/nupack.py +589 -0
- dnakit/secondary_structure/results.py +142 -0
- dnakit/similarity/__init__.py +125 -0
- dnakit/similarity/_shared.py +152 -0
- dnakit/similarity/approximate.py +258 -0
- dnakit/similarity/compare.py +162 -0
- dnakit/similarity/dashing.py +936 -0
- dnakit/similarity/distance.py +279 -0
- dnakit/similarity/index.py +365 -0
- dnakit/similarity/matrix.py +254 -0
- dnakit/similarity/results.py +676 -0
- dnakit/similarity/search.py +286 -0
- dnakit/similarity/sketch.py +96 -0
- dnakit/similarity/vector.py +662 -0
- dnakit/standardize/__init__.py +77 -0
- dnakit/standardize/_shared.py +100 -0
- dnakit/standardize/config.py +411 -0
- dnakit/standardize/gaps.py +1060 -0
- dnakit/standardize/normalize.py +565 -0
- dnakit/standardize/results.py +247 -0
- dnakit/standardize/validate.py +487 -0
- dnakit/structure3d/__init__.py +47 -0
- dnakit/structure3d/_shared.py +47 -0
- dnakit/structure3d/geometry.py +773 -0
- dnakit/structure3d/pdb.py +253 -0
- dnakit/structure3d/results.py +233 -0
- dnakit/structure3d/threedna.py +174 -0
- dnakit/thermodynamics/__init__.py +112 -0
- dnakit/thermodynamics/_shared.py +92 -0
- dnakit/thermodynamics/backends.py +653 -0
- dnakit/thermodynamics/calculations.py +837 -0
- dnakit/thermodynamics/config.py +175 -0
- dnakit/thermodynamics/equilibrium.py +340 -0
- dnakit/thermodynamics/optics.py +385 -0
- dnakit/thermodynamics/parameters.py +79 -0
- dnakit/thermodynamics/results.py +445 -0
- dnakit/visualization/__init__.py +43 -0
- dnakit/visualization/_svg.py +185 -0
- dnakit/visualization/advanced.py +314 -0
- dnakit/visualization/config.py +255 -0
- dnakit/visualization/export.py +349 -0
- dnakit/visualization/matrix.py +279 -0
- dnakit/visualization/report.py +431 -0
- dnakit/visualization/results.py +236 -0
- dnakit/visualization/sequence.py +459 -0
- dnakit/workflows/__init__.py +45 -0
- dnakit/workflows/manifest.py +365 -0
- dnakit/workflows/runner.py +1548 -0
- dnakit/workflows/schema.py +898 -0
- dnakit-0.1.0.dev0.dist-info/METADATA +126 -0
- dnakit-0.1.0.dev0.dist-info/RECORD +210 -0
- dnakit-0.1.0.dev0.dist-info/WHEEL +5 -0
- dnakit-0.1.0.dev0.dist-info/entry_points.txt +2 -0
- dnakit-0.1.0.dev0.dist-info/licenses/DISCLAIMER.md +26 -0
- dnakit-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
- dnakit-0.1.0.dev0.dist-info/licenses/THIRD_PARTY_NOTICES.md +73 -0
- dnakit-0.1.0.dev0.dist-info/top_level.txt +1 -0
dnakit/molbio/crispr.py
ADDED
|
@@ -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"]
|