bioforge 2.3.0__py3-none-win_amd64.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.
- bioforge-2.3.0.data/purelib/bioforge/__init__.py +74 -0
- bioforge-2.3.0.data/purelib/bioforge/aligner.py +953 -0
- bioforge-2.3.0.data/purelib/bioforge/analyze.py +385 -0
- bioforge-2.3.0.data/purelib/bioforge/bgzf.py +119 -0
- bioforge-2.3.0.data/purelib/bioforge/biocore.py +2092 -0
- bioforge-2.3.0.data/purelib/bioforge/engine/__init__.py +1 -0
- bioforge-2.3.0.data/purelib/bioforge/engine/_loader.py +543 -0
- bioforge-2.3.0.data/purelib/bioforge/engine/build.py +231 -0
- bioforge-2.3.0.data/purelib/bioforge/engine/engine.c +1538 -0
- bioforge-2.3.0.data/purelib/bioforge/engine/engine.dll +0 -0
- bioforge-2.3.0.data/purelib/bioforge/qcreport.py +298 -0
- bioforge-2.3.0.data/purelib/bioforge/smart_translator.py +601 -0
- bioforge-2.3.0.dist-info/METADATA +736 -0
- bioforge-2.3.0.dist-info/RECORD +18 -0
- bioforge-2.3.0.dist-info/WHEEL +5 -0
- bioforge-2.3.0.dist-info/entry_points.txt +4 -0
- bioforge-2.3.0.dist-info/licenses/LICENSE +280 -0
- bioforge-2.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# engine package — motor C de alto rendimiento
|
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
"""
|
|
2
|
+
engine/_loader.py — Carga el motor C y expone funciones Python.
|
|
3
|
+
|
|
4
|
+
Si el DLL no está compilado, C_AVAILABLE = False y los módulos
|
|
5
|
+
usan el fallback NumPy automáticamente.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import ctypes
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
_ENGINE_DIR = Path(__file__).parent
|
|
15
|
+
_DLL_PATH = _ENGINE_DIR / ("engine.dll" if sys.platform == "win32" else "engine.so")
|
|
16
|
+
|
|
17
|
+
# ── Tipos ctypes frecuentes ────────────────────────────────────────────────────
|
|
18
|
+
_U8P = ctypes.POINTER(ctypes.c_uint8)
|
|
19
|
+
_I32P = ctypes.POINTER(ctypes.c_int32)
|
|
20
|
+
_I32 = ctypes.c_int32
|
|
21
|
+
_I64 = ctypes.c_int64
|
|
22
|
+
_CHARP = ctypes.c_char_p
|
|
23
|
+
|
|
24
|
+
# ── Carga del DLL ──────────────────────────────────────────────────────────────
|
|
25
|
+
_lib: ctypes.CDLL | None = None
|
|
26
|
+
C_AVAILABLE: bool = False
|
|
27
|
+
|
|
28
|
+
def _load() -> bool:
|
|
29
|
+
global _lib, C_AVAILABLE
|
|
30
|
+
if not _DLL_PATH.exists():
|
|
31
|
+
return False
|
|
32
|
+
try:
|
|
33
|
+
_lib = ctypes.CDLL(str(_DLL_PATH))
|
|
34
|
+
_setup_signatures()
|
|
35
|
+
C_AVAILABLE = True
|
|
36
|
+
return True
|
|
37
|
+
except Exception:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _setup_signatures() -> None:
|
|
42
|
+
assert _lib is not None
|
|
43
|
+
|
|
44
|
+
# ── bio_getitem5 ───────────────────────────────────────────────────────────
|
|
45
|
+
_lib.bio_getitem5.restype = ctypes.c_uint8
|
|
46
|
+
_lib.bio_getitem5.argtypes = [_U8P, _I32]
|
|
47
|
+
|
|
48
|
+
# ── bio_pack5 ─────────────────────────────────────────────────────────────
|
|
49
|
+
_lib.bio_pack5.restype = None
|
|
50
|
+
_lib.bio_pack5.argtypes = [_U8P, _I32, _U8P]
|
|
51
|
+
|
|
52
|
+
# ── bio_unpack5 ───────────────────────────────────────────────────────────
|
|
53
|
+
_lib.bio_unpack5.restype = None
|
|
54
|
+
_lib.bio_unpack5.argtypes = [_U8P, _I32, _U8P]
|
|
55
|
+
|
|
56
|
+
# ── bio_find_atg ──────────────────────────────────────────────────────────
|
|
57
|
+
_lib.bio_find_atg.restype = _I32
|
|
58
|
+
_lib.bio_find_atg.argtypes = [_U8P, _I32]
|
|
59
|
+
|
|
60
|
+
# ── bio_translate ─────────────────────────────────────────────────────────
|
|
61
|
+
_lib.bio_translate.restype = None
|
|
62
|
+
_lib.bio_translate.argtypes = [_U8P, _U8P, _I32, _U8P]
|
|
63
|
+
|
|
64
|
+
# ── nw_global / nw_semiglobal (misma firma) ────────────────────────────────
|
|
65
|
+
_nw_args = [
|
|
66
|
+
_U8P, _I32, # codes_a, m
|
|
67
|
+
_U8P, _I32, # codes_b, n
|
|
68
|
+
_CHARP, # decode (32 bytes)
|
|
69
|
+
_I32, _I32, _I32, # match, mismatch, gap
|
|
70
|
+
_CHARP, _CHARP, # out_a, out_b
|
|
71
|
+
_I32P, _I32P, _I32P, _I32P, # score, matches, mismatches, gaps
|
|
72
|
+
]
|
|
73
|
+
_lib.nw_global.restype = _I32
|
|
74
|
+
_lib.nw_global.argtypes = _nw_args
|
|
75
|
+
_lib.nw_semiglobal.restype = _I32
|
|
76
|
+
_lib.nw_semiglobal.argtypes = _nw_args
|
|
77
|
+
|
|
78
|
+
# ── sw_align (Smith-Waterman, misma firma que nw) ──────────────────────────
|
|
79
|
+
_lib.sw_align.restype = _I32
|
|
80
|
+
_lib.sw_align.argtypes = _nw_args
|
|
81
|
+
|
|
82
|
+
# ── nw_banded / nw_banded_semiglobal (band extra) ─────────────────────────
|
|
83
|
+
_nw_banded_args = [
|
|
84
|
+
_U8P, _I32, # codes_a, m
|
|
85
|
+
_U8P, _I32, # codes_b, n
|
|
86
|
+
_CHARP, # decode
|
|
87
|
+
_I32, _I32, _I32, _I32, # match, mismatch, gap, band
|
|
88
|
+
_CHARP, _CHARP, # out_a, out_b
|
|
89
|
+
_I32P, _I32P, _I32P, _I32P,
|
|
90
|
+
]
|
|
91
|
+
_lib.nw_banded.restype = _I32
|
|
92
|
+
_lib.nw_banded.argtypes = _nw_banded_args
|
|
93
|
+
_lib.nw_banded_semiglobal.restype = _I32
|
|
94
|
+
_lib.nw_banded_semiglobal.argtypes = _nw_banded_args
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
_load()
|
|
98
|
+
|
|
99
|
+
# ── Verificar si el motor tiene las funciones del parser (requiere recompilación) ─
|
|
100
|
+
C_PARSER_AVAILABLE: bool = False
|
|
101
|
+
|
|
102
|
+
def _check_parser() -> None:
|
|
103
|
+
global C_PARSER_AVAILABLE
|
|
104
|
+
if not C_AVAILABLE or _lib is None:
|
|
105
|
+
return
|
|
106
|
+
try:
|
|
107
|
+
# in_dll fuerza la resolución del símbolo en el DLL ahora mismo.
|
|
108
|
+
# Si el DLL es antiguo (sin bio_parser_open), lanza OSError aquí
|
|
109
|
+
# en vez de colapsar más tarde al llamar la función.
|
|
110
|
+
ctypes.c_void_p.in_dll(_lib, "bio_parser_open")
|
|
111
|
+
_lib.bio_parser_open.restype = ctypes.c_void_p
|
|
112
|
+
_lib.bio_parser_open.argtypes = [ctypes.c_char_p]
|
|
113
|
+
|
|
114
|
+
_lib.bio_parser_next.restype = _I32
|
|
115
|
+
_lib.bio_parser_next.argtypes = [
|
|
116
|
+
ctypes.c_void_p, # handle
|
|
117
|
+
ctypes.c_char_p, _I32, # hdr, hdr_max
|
|
118
|
+
_U8P, _I32, _I32P, # codes, codes_max, n_out
|
|
119
|
+
_I32, # force_type (-1 auto | 0 nuc | 1 prot)
|
|
120
|
+
_I32P, # type_out
|
|
121
|
+
_U8P, _I32P, # qual, qual_out (NULL para FASTA)
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
_lib.bio_parser_close.restype = None
|
|
125
|
+
_lib.bio_parser_close.argtypes = [ctypes.c_void_p]
|
|
126
|
+
|
|
127
|
+
C_PARSER_AVAILABLE = True
|
|
128
|
+
except (AttributeError, OSError):
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
C_BATCH_AVAILABLE: bool = False
|
|
133
|
+
|
|
134
|
+
def _check_batch() -> None:
|
|
135
|
+
"""El parser por lotes es opcional: DLLs antiguos solo tienen next()."""
|
|
136
|
+
global C_BATCH_AVAILABLE
|
|
137
|
+
if not C_PARSER_AVAILABLE or _lib is None:
|
|
138
|
+
return
|
|
139
|
+
try:
|
|
140
|
+
ctypes.c_void_p.in_dll(_lib, "bio_parser_next_batch")
|
|
141
|
+
_lib.bio_parser_next_batch.restype = _I32
|
|
142
|
+
_lib.bio_parser_next_batch.argtypes = [
|
|
143
|
+
ctypes.c_void_p, _I32, _I32, # handle, max_records, force_type
|
|
144
|
+
ctypes.c_char_p, _I32, _I32P, # hdr_buf, hdr_buf_max, hdr_off
|
|
145
|
+
_U8P, _I32, _I32P, # pack_buf, pack_buf_max, pack_off
|
|
146
|
+
_I32P, _I32P, # n_syms, types
|
|
147
|
+
_U8P, _I32, _I32P, # qual_buf, qual_buf_max, qual_off
|
|
148
|
+
]
|
|
149
|
+
C_BATCH_AVAILABLE = True
|
|
150
|
+
except (AttributeError, OSError):
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
C_PARALLEL_AVAILABLE: bool = False
|
|
155
|
+
|
|
156
|
+
def _check_parallel() -> None:
|
|
157
|
+
"""Parser paralelo en memoria (OpenMP). Opcional."""
|
|
158
|
+
global C_PARALLEL_AVAILABLE
|
|
159
|
+
if not C_BATCH_AVAILABLE or _lib is None:
|
|
160
|
+
return
|
|
161
|
+
try:
|
|
162
|
+
ctypes.c_void_p.in_dll(_lib, "bio_parse_mem_parallel")
|
|
163
|
+
_lib.bio_parse_mem_parallel.restype = _I32
|
|
164
|
+
_lib.bio_parse_mem_parallel.argtypes = [
|
|
165
|
+
_U8P, _I64, _I32, _I32, _I32, # data, len, fmt, n_threads, force_type
|
|
166
|
+
ctypes.c_char_p, _I32, _I32P, # hdr_buf, hdr_buf_max, hdr_off
|
|
167
|
+
_U8P, _I64, _I32P, # pack_buf, pack_buf_max, pack_off
|
|
168
|
+
_I32P, _I32P, # n_syms, types
|
|
169
|
+
_U8P, _I64, _I32P, # qual_buf, qual_buf_max, qual_off
|
|
170
|
+
_I32, # max_records
|
|
171
|
+
]
|
|
172
|
+
C_PARALLEL_AVAILABLE = True
|
|
173
|
+
except (AttributeError, OSError):
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
C_LIBDEFLATE_AVAILABLE: bool = False
|
|
178
|
+
|
|
179
|
+
def _check_libdeflate() -> None:
|
|
180
|
+
"""Descompresor gzip rápido (libdeflate). Opcional."""
|
|
181
|
+
global C_LIBDEFLATE_AVAILABLE
|
|
182
|
+
if not C_AVAILABLE or _lib is None:
|
|
183
|
+
return
|
|
184
|
+
try:
|
|
185
|
+
ctypes.c_void_p.in_dll(_lib, "bio_has_libdeflate")
|
|
186
|
+
_lib.bio_has_libdeflate.restype = ctypes.c_int
|
|
187
|
+
_lib.bio_has_libdeflate.argtypes = []
|
|
188
|
+
_lib.bio_gzip_decompress.restype = _I64
|
|
189
|
+
_lib.bio_gzip_decompress.argtypes = [_U8P, _I64, _U8P, _I64]
|
|
190
|
+
_lib.bio_is_bgzf.restype = ctypes.c_int
|
|
191
|
+
_lib.bio_is_bgzf.argtypes = [_U8P, _I64]
|
|
192
|
+
_lib.bio_bgzf_usize.restype = _I64
|
|
193
|
+
_lib.bio_bgzf_usize.argtypes = [_U8P, _I64]
|
|
194
|
+
_lib.bio_bgzf_decompress_parallel.restype = _I64
|
|
195
|
+
_lib.bio_bgzf_decompress_parallel.argtypes = [_U8P, _I64, _U8P, _I64, _I32]
|
|
196
|
+
_lib.bio_bgzf_compress.restype = _I64
|
|
197
|
+
_lib.bio_bgzf_compress.argtypes = [_U8P, _I64, _U8P, _I64, _I32, _I32]
|
|
198
|
+
C_LIBDEFLATE_AVAILABLE = bool(_lib.bio_has_libdeflate())
|
|
199
|
+
except (AttributeError, OSError):
|
|
200
|
+
pass
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
_check_parser()
|
|
204
|
+
_check_batch()
|
|
205
|
+
_check_parallel()
|
|
206
|
+
_check_libdeflate()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# ── Wrappers Python ────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
def c_getitem5(packed: np.ndarray, i: int) -> int:
|
|
212
|
+
return int(_lib.bio_getitem5(
|
|
213
|
+
packed.ctypes.data_as(_U8P),
|
|
214
|
+
_I32(i),
|
|
215
|
+
))
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def c_pack5(codes: np.ndarray) -> np.ndarray:
|
|
219
|
+
n = len(codes)
|
|
220
|
+
out_len = (n * 5 + 7) // 8 + 1 # +1 para lecturas seguras
|
|
221
|
+
out = np.zeros(out_len, dtype=np.uint8)
|
|
222
|
+
_lib.bio_pack5(
|
|
223
|
+
codes.ctypes.data_as(_U8P),
|
|
224
|
+
_I32(n),
|
|
225
|
+
out.ctypes.data_as(_U8P),
|
|
226
|
+
)
|
|
227
|
+
return out[:out_len - 1] # recortar el byte extra
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def c_unpack5(packed: np.ndarray, n: int) -> np.ndarray:
|
|
231
|
+
# bio_unpack5 es seguro en los límites → no hace falta copiar un byte extra.
|
|
232
|
+
# ascontiguousarray no copia si ya es uint8 contiguo (el caso normal).
|
|
233
|
+
safe = np.ascontiguousarray(packed, dtype=np.uint8)
|
|
234
|
+
out = np.empty(n, dtype=np.uint8)
|
|
235
|
+
_lib.bio_unpack5(
|
|
236
|
+
safe.ctypes.data_as(_U8P),
|
|
237
|
+
_I32(n),
|
|
238
|
+
out.ctypes.data_as(_U8P),
|
|
239
|
+
)
|
|
240
|
+
return out
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def c_find_atg(codes: np.ndarray) -> int:
|
|
244
|
+
"""Devuelve el indice del primer ATG en codes, o -1 si no existe."""
|
|
245
|
+
safe = np.ascontiguousarray(codes, dtype=np.uint8)
|
|
246
|
+
return int(_lib.bio_find_atg(safe.ctypes.data_as(_U8P), _I32(len(safe))))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def c_translate(codon_lut: np.ndarray, nuc_codes: np.ndarray, n_codons: int) -> np.ndarray:
|
|
250
|
+
"""Traduce n_codons codones usando el LUT; devuelve array uint8 de AAs."""
|
|
251
|
+
lut = np.ascontiguousarray(codon_lut, dtype=np.uint8)
|
|
252
|
+
safe = np.ascontiguousarray(nuc_codes[:n_codons * 3], dtype=np.uint8)
|
|
253
|
+
out = np.empty(n_codons, dtype=np.uint8)
|
|
254
|
+
_lib.bio_translate(
|
|
255
|
+
lut.ctypes.data_as(_U8P),
|
|
256
|
+
safe.ctypes.data_as(_U8P),
|
|
257
|
+
_I32(n_codons),
|
|
258
|
+
out.ctypes.data_as(_U8P),
|
|
259
|
+
)
|
|
260
|
+
return out
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def c_sw_align(
|
|
264
|
+
codes_a: np.ndarray,
|
|
265
|
+
codes_b: np.ndarray,
|
|
266
|
+
decode_bytes: bytes,
|
|
267
|
+
match: int, mismatch: int, gap: int,
|
|
268
|
+
) -> tuple[str, str, int, int, int, int]:
|
|
269
|
+
"""Smith-Waterman local alignment en C."""
|
|
270
|
+
m, n = len(codes_a), len(codes_b)
|
|
271
|
+
buf_size = m + n + 2
|
|
272
|
+
out_a = ctypes.create_string_buffer(buf_size)
|
|
273
|
+
out_b = ctypes.create_string_buffer(buf_size)
|
|
274
|
+
score = _I32(0); nm = _I32(0); nmi = _I32(0); ng = _I32(0)
|
|
275
|
+
ca = np.ascontiguousarray(codes_a, dtype=np.uint8)
|
|
276
|
+
cb = np.ascontiguousarray(codes_b, dtype=np.uint8)
|
|
277
|
+
aln_len = _lib.sw_align(
|
|
278
|
+
ca.ctypes.data_as(_U8P), _I32(m),
|
|
279
|
+
cb.ctypes.data_as(_U8P), _I32(n),
|
|
280
|
+
decode_bytes,
|
|
281
|
+
_I32(match), _I32(mismatch), _I32(gap),
|
|
282
|
+
out_a, out_b,
|
|
283
|
+
ctypes.byref(score), ctypes.byref(nm),
|
|
284
|
+
ctypes.byref(nmi), ctypes.byref(ng),
|
|
285
|
+
)
|
|
286
|
+
if aln_len < 0:
|
|
287
|
+
raise MemoryError("Motor C: fallo de memoria en SW")
|
|
288
|
+
return (
|
|
289
|
+
out_a.value.decode("ascii"), out_b.value.decode("ascii"),
|
|
290
|
+
score.value, nm.value, nmi.value, ng.value,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def c_nw_banded(
|
|
295
|
+
codes_a: np.ndarray,
|
|
296
|
+
codes_b: np.ndarray,
|
|
297
|
+
decode_bytes: bytes,
|
|
298
|
+
match: int, mismatch: int, gap: int,
|
|
299
|
+
band: int, mode: str,
|
|
300
|
+
) -> tuple[str, str, int, int, int, int]:
|
|
301
|
+
"""Banded NW en C. Memoria O(m*band)."""
|
|
302
|
+
m, n = len(codes_a), len(codes_b)
|
|
303
|
+
buf_size = m + n + 2
|
|
304
|
+
out_a = ctypes.create_string_buffer(buf_size)
|
|
305
|
+
out_b = ctypes.create_string_buffer(buf_size)
|
|
306
|
+
score = _I32(0); nm = _I32(0); nmi = _I32(0); ng = _I32(0)
|
|
307
|
+
ca = np.ascontiguousarray(codes_a, dtype=np.uint8)
|
|
308
|
+
cb = np.ascontiguousarray(codes_b, dtype=np.uint8)
|
|
309
|
+
fn = _lib.nw_banded_semiglobal if mode == "semi-global" else _lib.nw_banded
|
|
310
|
+
aln_len = fn(
|
|
311
|
+
ca.ctypes.data_as(_U8P), _I32(m),
|
|
312
|
+
cb.ctypes.data_as(_U8P), _I32(n),
|
|
313
|
+
decode_bytes,
|
|
314
|
+
_I32(match), _I32(mismatch), _I32(gap), _I32(band),
|
|
315
|
+
out_a, out_b,
|
|
316
|
+
ctypes.byref(score), ctypes.byref(nm),
|
|
317
|
+
ctypes.byref(nmi), ctypes.byref(ng),
|
|
318
|
+
)
|
|
319
|
+
if aln_len < 0:
|
|
320
|
+
raise MemoryError("Motor C: fallo de memoria en NW banded")
|
|
321
|
+
return (
|
|
322
|
+
out_a.value.decode("ascii"), out_b.value.decode("ascii"),
|
|
323
|
+
score.value, nm.value, nmi.value, ng.value,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def c_parser_open(path: str) -> int:
|
|
328
|
+
"""Abre un archivo FASTA/FASTQ y devuelve un handle opaco (c_void_p)."""
|
|
329
|
+
raw = path.encode("utf-8") if isinstance(path, str) else path
|
|
330
|
+
return _lib.bio_parser_open(raw) # devuelve c_void_p (int en Python)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def c_parser_next(
|
|
334
|
+
handle: int,
|
|
335
|
+
hdr_buf: "ctypes.Array",
|
|
336
|
+
codes_buf: np.ndarray,
|
|
337
|
+
force_type: int,
|
|
338
|
+
) -> "tuple[int, int, int]":
|
|
339
|
+
"""Lee el siguiente registro FASTA.
|
|
340
|
+
Retorna (ret, n_symbols, seq_type): ret 1=OK 0=EOF -1=error -2=overflow."""
|
|
341
|
+
n_out = _I32(0)
|
|
342
|
+
type_out = _I32(0)
|
|
343
|
+
ret = _lib.bio_parser_next(
|
|
344
|
+
ctypes.c_void_p(handle),
|
|
345
|
+
hdr_buf, _I32(len(hdr_buf)),
|
|
346
|
+
codes_buf.ctypes.data_as(_U8P), _I32(len(codes_buf)),
|
|
347
|
+
ctypes.byref(n_out),
|
|
348
|
+
_I32(force_type),
|
|
349
|
+
ctypes.byref(type_out),
|
|
350
|
+
None, None, # sin calidades — FASTA
|
|
351
|
+
)
|
|
352
|
+
return ret, n_out.value, type_out.value
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def c_parser_next_fastq(
|
|
356
|
+
handle: int,
|
|
357
|
+
hdr_buf: "ctypes.Array",
|
|
358
|
+
codes_buf: np.ndarray,
|
|
359
|
+
qual_buf: np.ndarray,
|
|
360
|
+
) -> "tuple[int, int, int]":
|
|
361
|
+
"""Lee el siguiente registro FASTQ.
|
|
362
|
+
Retorna (ret, n_symbols, n_qual): ret 1=OK 0=EOF -1=error -2=overflow."""
|
|
363
|
+
n_out = _I32(0)
|
|
364
|
+
type_out = _I32(0)
|
|
365
|
+
q_out = _I32(0)
|
|
366
|
+
ret = _lib.bio_parser_next(
|
|
367
|
+
ctypes.c_void_p(handle),
|
|
368
|
+
hdr_buf, _I32(len(hdr_buf)),
|
|
369
|
+
codes_buf.ctypes.data_as(_U8P), _I32(len(codes_buf)),
|
|
370
|
+
ctypes.byref(n_out),
|
|
371
|
+
_I32(0), # FASTQ siempre nucleótido
|
|
372
|
+
ctypes.byref(type_out),
|
|
373
|
+
qual_buf.ctypes.data_as(_U8P), ctypes.byref(q_out),
|
|
374
|
+
)
|
|
375
|
+
return ret, n_out.value, q_out.value
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def c_parser_next_batch(
|
|
379
|
+
handle: int,
|
|
380
|
+
max_records: int,
|
|
381
|
+
force_type: int,
|
|
382
|
+
hdr_buf: "ctypes.Array",
|
|
383
|
+
hdr_off: np.ndarray,
|
|
384
|
+
pack_buf: np.ndarray,
|
|
385
|
+
pack_off: np.ndarray,
|
|
386
|
+
n_syms: np.ndarray,
|
|
387
|
+
types: np.ndarray,
|
|
388
|
+
qual_buf: "np.ndarray | None" = None,
|
|
389
|
+
qual_off: "np.ndarray | None" = None,
|
|
390
|
+
) -> int:
|
|
391
|
+
"""Parsea hasta ``max_records`` registros en una sola llamada.
|
|
392
|
+
|
|
393
|
+
Empaqueta cada secuencia a 5-bit dentro de C. Rellena los arrays de salida
|
|
394
|
+
(que el llamante reutiliza entre lotes). Retorna el nº de registros
|
|
395
|
+
parseados (>=0), 0 = EOF, o negativo en error (-2 = registro demasiado
|
|
396
|
+
grande para el buffer).
|
|
397
|
+
"""
|
|
398
|
+
q_ptr = qual_buf.ctypes.data_as(_U8P) if qual_buf is not None else None
|
|
399
|
+
q_max = _I32(len(qual_buf)) if qual_buf is not None else _I32(0)
|
|
400
|
+
q_off_ptr = qual_off.ctypes.data_as(_I32P) if qual_off is not None else None
|
|
401
|
+
return _lib.bio_parser_next_batch(
|
|
402
|
+
ctypes.c_void_p(handle), _I32(max_records), _I32(force_type),
|
|
403
|
+
hdr_buf, _I32(len(hdr_buf)), hdr_off.ctypes.data_as(_I32P),
|
|
404
|
+
pack_buf.ctypes.data_as(_U8P), _I32(len(pack_buf)),
|
|
405
|
+
pack_off.ctypes.data_as(_I32P),
|
|
406
|
+
n_syms.ctypes.data_as(_I32P), types.ctypes.data_as(_I32P),
|
|
407
|
+
q_ptr, q_max, q_off_ptr,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def c_parse_mem_parallel(
|
|
412
|
+
data: np.ndarray,
|
|
413
|
+
fmt: int,
|
|
414
|
+
n_threads: int,
|
|
415
|
+
force_type: int,
|
|
416
|
+
hdr_buf: "ctypes.Array",
|
|
417
|
+
hdr_off: np.ndarray,
|
|
418
|
+
pack_buf: np.ndarray,
|
|
419
|
+
pack_off: np.ndarray,
|
|
420
|
+
n_syms: np.ndarray,
|
|
421
|
+
types: np.ndarray,
|
|
422
|
+
qual_buf: "np.ndarray | None",
|
|
423
|
+
qual_off: "np.ndarray | None",
|
|
424
|
+
max_records: int,
|
|
425
|
+
) -> int:
|
|
426
|
+
"""Parsea un bloque de memoria (solo registros completos) en paralelo.
|
|
427
|
+
|
|
428
|
+
``data`` es un array uint8 contiguo. Rellena los buffers de salida (que el
|
|
429
|
+
llamante reutiliza). Devuelve nº de registros (>=0), o negativo en error.
|
|
430
|
+
"""
|
|
431
|
+
q_ptr = qual_buf.ctypes.data_as(_U8P) if qual_buf is not None else None
|
|
432
|
+
q_max = _I64(len(qual_buf)) if qual_buf is not None else _I64(0)
|
|
433
|
+
q_off = qual_off.ctypes.data_as(_I32P) if qual_off is not None else None
|
|
434
|
+
return _lib.bio_parse_mem_parallel(
|
|
435
|
+
data.ctypes.data_as(_U8P), _I64(len(data)),
|
|
436
|
+
_I32(fmt), _I32(n_threads), _I32(force_type),
|
|
437
|
+
hdr_buf, _I32(len(hdr_buf)), hdr_off.ctypes.data_as(_I32P),
|
|
438
|
+
pack_buf.ctypes.data_as(_U8P), _I64(len(pack_buf)),
|
|
439
|
+
pack_off.ctypes.data_as(_I32P),
|
|
440
|
+
n_syms.ctypes.data_as(_I32P), types.ctypes.data_as(_I32P),
|
|
441
|
+
q_ptr, q_max, q_off,
|
|
442
|
+
_I32(max_records),
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def c_gzip_decompress(cbuf: np.ndarray, obuf: np.ndarray) -> int:
|
|
447
|
+
"""Descomprime gzip ``cbuf`` (uint8) en ``obuf`` (uint8) con libdeflate.
|
|
448
|
+
|
|
449
|
+
Devuelve nº de bytes descomprimidos, o -1 si no caben o hay error.
|
|
450
|
+
"""
|
|
451
|
+
return int(_lib.bio_gzip_decompress(
|
|
452
|
+
cbuf.ctypes.data_as(_U8P), _I64(len(cbuf)),
|
|
453
|
+
obuf.ctypes.data_as(_U8P), _I64(len(obuf)),
|
|
454
|
+
))
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def c_is_bgzf(cbuf: np.ndarray) -> bool:
|
|
458
|
+
"""True si el buffer comprimido tiene formato BGZF (gzip por bloques)."""
|
|
459
|
+
return bool(_lib.bio_is_bgzf(cbuf.ctypes.data_as(_U8P), _I64(len(cbuf))))
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def c_bgzf_usize(cbuf: np.ndarray) -> int:
|
|
463
|
+
"""Tamaño total descomprimido de un BGZF, o -1 si malformado."""
|
|
464
|
+
return int(_lib.bio_bgzf_usize(cbuf.ctypes.data_as(_U8P), _I64(len(cbuf))))
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def c_bgzf_decompress_parallel(cbuf: np.ndarray, obuf: np.ndarray,
|
|
468
|
+
n_threads: int) -> int:
|
|
469
|
+
"""Descomprime un BGZF en paralelo (bloques independientes).
|
|
470
|
+
|
|
471
|
+
Devuelve nº de bytes descomprimidos, o -1 en error.
|
|
472
|
+
"""
|
|
473
|
+
return int(_lib.bio_bgzf_decompress_parallel(
|
|
474
|
+
cbuf.ctypes.data_as(_U8P), _I64(len(cbuf)),
|
|
475
|
+
obuf.ctypes.data_as(_U8P), _I64(len(obuf)), _I32(n_threads),
|
|
476
|
+
))
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def c_bgzf_compress(inbuf: np.ndarray, obuf: np.ndarray,
|
|
480
|
+
level: int, n_threads: int) -> int:
|
|
481
|
+
"""Comprime ``inbuf`` a BGZF en paralelo. Devuelve bytes comprimidos o -1."""
|
|
482
|
+
return int(_lib.bio_bgzf_compress(
|
|
483
|
+
inbuf.ctypes.data_as(_U8P), _I64(len(inbuf)),
|
|
484
|
+
obuf.ctypes.data_as(_U8P), _I64(len(obuf)),
|
|
485
|
+
_I32(level), _I32(n_threads),
|
|
486
|
+
))
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def c_parser_close(handle: int) -> None:
|
|
490
|
+
"""Libera el handle del parser y cierra el archivo."""
|
|
491
|
+
_lib.bio_parser_close(ctypes.c_void_p(handle))
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def c_nw_align(
|
|
495
|
+
codes_a: np.ndarray,
|
|
496
|
+
codes_b: np.ndarray,
|
|
497
|
+
decode_bytes: bytes,
|
|
498
|
+
match: int, mismatch: int, gap: int,
|
|
499
|
+
mode: str,
|
|
500
|
+
) -> tuple[str, str, int, int, int, int]:
|
|
501
|
+
"""
|
|
502
|
+
Alineamiento NW completo en C.
|
|
503
|
+
Devuelve (aligned_a, aligned_b, score, n_matches, n_mismatches, n_gaps).
|
|
504
|
+
"""
|
|
505
|
+
m, n = len(codes_a), len(codes_b)
|
|
506
|
+
buf_size = m + n + 2
|
|
507
|
+
|
|
508
|
+
out_a = ctypes.create_string_buffer(buf_size)
|
|
509
|
+
out_b = ctypes.create_string_buffer(buf_size)
|
|
510
|
+
score = _I32(0)
|
|
511
|
+
nm = _I32(0)
|
|
512
|
+
nmi = _I32(0)
|
|
513
|
+
ng = _I32(0)
|
|
514
|
+
|
|
515
|
+
# Asegurar que los arrays son C-contiguos uint8
|
|
516
|
+
ca = np.ascontiguousarray(codes_a, dtype=np.uint8)
|
|
517
|
+
cb = np.ascontiguousarray(codes_b, dtype=np.uint8)
|
|
518
|
+
|
|
519
|
+
fn = _lib.nw_semiglobal if mode == "semi-global" else _lib.nw_global
|
|
520
|
+
|
|
521
|
+
aln_len = fn(
|
|
522
|
+
ca.ctypes.data_as(_U8P), _I32(m),
|
|
523
|
+
cb.ctypes.data_as(_U8P), _I32(n),
|
|
524
|
+
decode_bytes,
|
|
525
|
+
_I32(match), _I32(mismatch), _I32(gap),
|
|
526
|
+
out_a, out_b,
|
|
527
|
+
ctypes.byref(score),
|
|
528
|
+
ctypes.byref(nm),
|
|
529
|
+
ctypes.byref(nmi),
|
|
530
|
+
ctypes.byref(ng),
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
if aln_len < 0:
|
|
534
|
+
raise MemoryError("Motor C: fallo de asignación de memoria en NW")
|
|
535
|
+
|
|
536
|
+
return (
|
|
537
|
+
out_a.value.decode("ascii"),
|
|
538
|
+
out_b.value.decode("ascii"),
|
|
539
|
+
score.value,
|
|
540
|
+
nm.value,
|
|
541
|
+
nmi.value,
|
|
542
|
+
ng.value,
|
|
543
|
+
)
|