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,247 @@
1
+ """Structured result objects returned by standardization and validation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING, Any, Literal, cast
7
+
8
+ from dnakit.core._json import to_json_compatible
9
+ from dnakit.core.gap import Gap
10
+ from dnakit.core.issues import Issue
11
+ from dnakit.core.provenance import Provenance
12
+ from dnakit.core.sequence import DNASequence
13
+
14
+ if TYPE_CHECKING:
15
+ from .config import DatasetValidationConfig, NormalizationConfig, ValidationConfig
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class InputPosition:
20
+ """A zero-based character position in the original, possibly multipart, input."""
21
+
22
+ part_index: int
23
+ offset: int
24
+ absolute_offset: int
25
+
26
+
27
+ @dataclass(frozen=True, slots=True)
28
+ class NormalizationChange:
29
+ """One auditable edit tied to an original input coordinate."""
30
+
31
+ operation: str
32
+ position: InputPosition
33
+ before: str
34
+ after: str
35
+ normalized_offset: int
36
+ reason: str
37
+
38
+
39
+ @dataclass(frozen=True, slots=True)
40
+ class NormalizationStep:
41
+ """Summary of one deterministic step in the normalization pipeline."""
42
+
43
+ name: str
44
+ status: Literal["applied", "no-op", "failed"]
45
+ change_count: int
46
+ parameters: tuple[tuple[str, str], ...] = ()
47
+
48
+
49
+ @dataclass(frozen=True, slots=True)
50
+ class RawInputSnapshot:
51
+ """Full in-memory input snapshot plus a persistence-safe digest."""
52
+
53
+ input_type: Literal["str", "bytes", "parts", "DNASequence"]
54
+ content: str | bytes | tuple[str | Gap, ...] | DNASequence
55
+ sha256: str
56
+ character_count: int
57
+
58
+ def to_dict(self, *, include_content: bool = False) -> dict[str, Any]:
59
+ """Return metadata and include sensitive raw content only when requested."""
60
+ result: dict[str, Any] = {
61
+ "input_type": self.input_type,
62
+ "sha256": self.sha256,
63
+ "character_count": self.character_count,
64
+ }
65
+ if include_content:
66
+ result["content"] = _raw_content_to_json(self.content)
67
+ return result
68
+
69
+
70
+ @dataclass(frozen=True, slots=True)
71
+ class SymbolOccurrence:
72
+ """A symbol occurrence in normalized sequence coordinates."""
73
+
74
+ symbol: str
75
+ symbol_offset: int
76
+ part_index: int
77
+ part_offset: int
78
+
79
+
80
+ @dataclass(frozen=True, slots=True)
81
+ class SymbolCount:
82
+ """Count and positions for one symbol."""
83
+
84
+ symbol: str
85
+ count: int
86
+ positions: tuple[int, ...]
87
+
88
+
89
+ @dataclass(frozen=True, slots=True)
90
+ class ProbabilityResolution:
91
+ """Per-position base probabilities for an IUPAC ambiguity symbol."""
92
+
93
+ symbol: str
94
+ symbol_offset: int
95
+ probabilities: tuple[tuple[str, float], ...]
96
+
97
+
98
+ @dataclass(frozen=True, slots=True)
99
+ class AmbiguityReport:
100
+ """Counts, fraction, positions and optional probability resolutions."""
101
+
102
+ total_count: int
103
+ denominator: int | None
104
+ fraction: float | None
105
+ by_symbol: tuple[SymbolCount, ...]
106
+ occurrences: tuple[SymbolOccurrence, ...]
107
+ probability_resolutions: tuple[ProbabilityResolution, ...] = ()
108
+
109
+ def count(self, symbol: str) -> int:
110
+ """Return the count for ``symbol`` without exposing mutable mappings."""
111
+ upper = symbol.upper()
112
+ return next((item.count for item in self.by_symbol if item.symbol == upper), 0)
113
+
114
+
115
+ @dataclass(frozen=True, slots=True)
116
+ class InvalidSymbol:
117
+ """An invalid Unicode code point and every original occurrence."""
118
+
119
+ symbol: str
120
+ codepoint: str
121
+ positions: tuple[InputPosition, ...]
122
+
123
+
124
+ @dataclass(frozen=True, slots=True)
125
+ class NormalizationResult:
126
+ """Complete, non-mutating normalization output and audit trail."""
127
+
128
+ raw_input: RawInputSnapshot
129
+ sequence: DNASequence | None
130
+ config: NormalizationConfig
131
+ algorithm_version: str
132
+ provenance: Provenance
133
+ normalized_parts: tuple[str | Gap, ...]
134
+ steps: tuple[NormalizationStep, ...]
135
+ changes: tuple[NormalizationChange, ...]
136
+ issues: tuple[Issue, ...]
137
+ ambiguity: AmbiguityReport
138
+ invalid_symbols: tuple[InvalidSymbol, ...]
139
+ u_positions: tuple[InputPosition, ...]
140
+
141
+ @property
142
+ def is_valid(self) -> bool:
143
+ """Whether a valid :class:`DNASequence` was produced."""
144
+ return self.sequence is not None and not _has_error(self.issues)
145
+
146
+ @property
147
+ def was_modified(self) -> bool:
148
+ """Whether at least one input character was edited or removed."""
149
+ return bool(self.changes)
150
+
151
+ def to_dict(self, *, include_raw_content: bool = False) -> dict[str, Any]:
152
+ """Serialize safely; raw sequence content is opt-in."""
153
+ result = cast(dict[str, Any], to_json_compatible(self))
154
+ result["raw_input"] = self.raw_input.to_dict(include_content=include_raw_content)
155
+ return result
156
+
157
+
158
+ @dataclass(frozen=True, slots=True)
159
+ class QualitySummary:
160
+ """Basic numeric summary for a PHRED quality annotation."""
161
+
162
+ count: int
163
+ minimum: float | None
164
+ maximum: float | None
165
+ mean: float | None
166
+
167
+
168
+ @dataclass(frozen=True, slots=True)
169
+ class ValidationReport:
170
+ """Validation and QC findings for one sequence or record."""
171
+
172
+ record_id: str | None
173
+ sequence: DNASequence
174
+ config: ValidationConfig
175
+ algorithm_version: str
176
+ provenance: Provenance
177
+ is_valid: bool
178
+ symbol_length: int
179
+ coordinate_span: int | None
180
+ ambiguity: AmbiguityReport
181
+ quality: QualitySummary | None
182
+ issues: tuple[Issue, ...]
183
+
184
+ def to_dict(self) -> dict[str, Any]:
185
+ """Return a JSON-compatible representation."""
186
+ return cast(dict[str, Any], to_json_compatible(self))
187
+
188
+
189
+ @dataclass(frozen=True, slots=True)
190
+ class DuplicateID:
191
+ """A repeated record identifier and its stable input indices."""
192
+
193
+ id: str
194
+ indices: tuple[int, ...]
195
+
196
+
197
+ @dataclass(frozen=True, slots=True)
198
+ class DatasetValidationReport:
199
+ """Collection-level validation outcome."""
200
+
201
+ record_count: int
202
+ config: DatasetValidationConfig
203
+ algorithm_version: str
204
+ provenance: Provenance
205
+ ids_unique: bool
206
+ duplicate_ids: tuple[DuplicateID, ...]
207
+ is_valid: bool
208
+ issues: tuple[Issue, ...]
209
+ record_reports: tuple[ValidationReport, ...] | None
210
+
211
+ def to_dict(self) -> dict[str, Any]:
212
+ """Return a JSON-compatible representation."""
213
+ return cast(dict[str, Any], to_json_compatible(self))
214
+
215
+
216
+ def _has_error(issues: tuple[Issue, ...]) -> bool:
217
+ return any(getattr(issue.severity, "value", issue.severity) == "error" for issue in issues)
218
+
219
+
220
+ def _raw_content_to_json(content: object) -> object:
221
+ if isinstance(content, bytes):
222
+ return {"encoding": "hex", "data": content.hex()}
223
+ if isinstance(content, DNASequence):
224
+ return _parts_to_json(content.parts)
225
+ if isinstance(content, tuple):
226
+ return _parts_to_json(content)
227
+ return content
228
+
229
+
230
+ def _parts_to_json(parts: tuple[str | Gap, ...]) -> list[object]:
231
+ result: list[object] = []
232
+ for part in parts:
233
+ if isinstance(part, str):
234
+ result.append(part)
235
+ else:
236
+ result.append(
237
+ {
238
+ "gap": {
239
+ "length": part.length,
240
+ "kind": getattr(part.kind, "value", part.kind),
241
+ "crossable": part.crossable,
242
+ "evidence": list(part.evidence),
243
+ "metadata": to_json_compatible(part.metadata),
244
+ }
245
+ }
246
+ )
247
+ return result