bioforge 3.1.0__tar.gz → 3.2.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 (38) hide show
  1. {bioforge-3.1.0 → bioforge-3.2.0}/PKG-INFO +1 -1
  2. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/__init__.py +1 -1
  3. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/genomemap.py +45 -0
  4. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge.egg-info/PKG-INFO +1 -1
  5. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_genomemap.py +17 -0
  6. {bioforge-3.1.0 → bioforge-3.2.0}/LICENSE +0 -0
  7. {bioforge-3.1.0 → bioforge-3.2.0}/README.md +0 -0
  8. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/aligner.py +0 -0
  9. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/analyze.py +0 -0
  10. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/bgzf.py +0 -0
  11. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/biocore.py +0 -0
  12. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/engine/__init__.py +0 -0
  13. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/engine/_loader.py +0 -0
  14. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/engine/build.py +0 -0
  15. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/engine/engine.c +0 -0
  16. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/engine/engine.dll +0 -0
  17. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/minimizers.py +0 -0
  18. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/qcreport.py +0 -0
  19. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/refindex.py +0 -0
  20. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge/smart_translator.py +0 -0
  21. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge.egg-info/SOURCES.txt +0 -0
  22. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge.egg-info/dependency_links.txt +0 -0
  23. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge.egg-info/entry_points.txt +0 -0
  24. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge.egg-info/requires.txt +0 -0
  25. {bioforge-3.1.0 → bioforge-3.2.0}/bioforge.egg-info/top_level.txt +0 -0
  26. {bioforge-3.1.0 → bioforge-3.2.0}/pyproject.toml +0 -0
  27. {bioforge-3.1.0 → bioforge-3.2.0}/setup.cfg +0 -0
  28. {bioforge-3.1.0 → bioforge-3.2.0}/setup.py +0 -0
  29. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_aligner.py +0 -0
  30. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_analyze.py +0 -0
  31. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_bgzf.py +0 -0
  32. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_biocore.py +0 -0
  33. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_errors.py +0 -0
  34. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_minimizers.py +0 -0
  35. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_qcreport.py +0 -0
  36. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_refindex.py +0 -0
  37. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_streaming.py +0 -0
  38. {bioforge-3.1.0 → bioforge-3.2.0}/tests/test_translator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bioforge
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: High-performance bioinformatics engine for Edge Computing — 5-bit encoding, vectorised NumPy core, optional C backend
5
5
  Author-email: Aarón Aranda Torrijos <noe9601@gmail.com>
6
6
  License: # PolyForm Noncommercial License 1.0.0
@@ -36,7 +36,7 @@ from .biocore import (
36
36
  from .genomemap import GenomeAligner, Mapping
37
37
  from .smart_translator import SmartTranslator
38
38
 
39
- __version__ = "3.1.0"
39
+ __version__ = "3.2.0"
40
40
  __author__ = "Aarón Aranda Torrijos"
41
41
 
42
42
  __all__ = [
@@ -31,6 +31,8 @@ en Python ya es correcto y rápido para el nº de anclas habitual.
31
31
 
32
32
  from __future__ import annotations
33
33
 
34
+ import os
35
+ from multiprocessing import Pool
34
36
  from typing import NamedTuple, Optional
35
37
 
36
38
  import numpy as np
@@ -337,6 +339,21 @@ def _mapq(chains: list[Chain], i: int) -> int:
337
339
  return int(max(0, min(60, round(60 * (1.0 - ratio)))))
338
340
 
339
341
 
342
+ # ── Workers de multiprocessing (a nivel de módulo → picklables) ─────────────────
343
+ _MP_ALIGNER: "Optional[GenomeAligner]" = None
344
+
345
+
346
+ def _mp_init(aligner: "GenomeAligner") -> None:
347
+ """Inicializador de cada proceso: guarda el índice (se pasa una vez)."""
348
+ global _MP_ALIGNER
349
+ _MP_ALIGNER = aligner
350
+
351
+
352
+ def _mp_worker(args) -> "list[Mapping]":
353
+ read, min_chain_score, max_hits = args
354
+ return _MP_ALIGNER.map(read, min_chain_score, max_hits)
355
+
356
+
340
357
  class GenomeAligner:
341
358
  """Mapeador de reads contra una referencia (seed-chain-align)."""
342
359
 
@@ -363,3 +380,31 @@ class GenomeAligner:
363
380
  if mp is not None:
364
381
  mappings.append(mp._replace(mapq=_mapq(chains, i)))
365
382
  return mappings
383
+
384
+ def map_batch(self, reads, n_processes: int = 0,
385
+ min_chain_score: float = 40.0,
386
+ max_hits: int = 5) -> list[list[Mapping]]:
387
+ """Mapea muchos reads en PARALELO → lista de listas de Mapping.
388
+
389
+ Los reads son independientes (paralelismo perfecto). Usa procesos
390
+ (multiprocessing) porque el GIL impide que los hilos escalen: el trabajo
391
+ por read es mayoritariamente Python. El índice se pasa una vez a cada
392
+ proceso; el orden de salida coincide con el de ``reads``.
393
+
394
+ ``n_processes``: 0 = todos los núcleos · 1 = secuencial · N = N procesos.
395
+
396
+ Nota (Windows/macOS): el script que lo llame debe estar protegido por
397
+ ``if __name__ == "__main__":`` (requisito de multiprocessing). Si el
398
+ arranque de procesos falla, cae a secuencial con gracia.
399
+ """
400
+ reads = list(reads)
401
+ if n_processes == 1 or len(reads) <= 1:
402
+ return [self.map(r, min_chain_score, max_hits) for r in reads]
403
+ nt = (os.cpu_count() or 1) if n_processes <= 0 else n_processes
404
+ args = [(r, min_chain_score, max_hits) for r in reads]
405
+ chunk = max(1, len(reads) // (nt * 4))
406
+ try:
407
+ with Pool(processes=nt, initializer=_mp_init, initargs=(self,)) as pool:
408
+ return pool.map(_mp_worker, args, chunksize=chunk)
409
+ except Exception: # noqa: BLE001 — fallback secuencial
410
+ return [self.map(r, min_chain_score, max_hits) for r in reads]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bioforge
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: High-performance bioinformatics engine for Edge Computing — 5-bit encoding, vectorised NumPy core, optional C backend
5
5
  Author-email: Aarón Aranda Torrijos <noe9601@gmail.com>
6
6
  License: # PolyForm Noncommercial License 1.0.0
@@ -129,6 +129,23 @@ def test_copias_multiples():
129
129
 
130
130
  # ── Salida PAF ──────────────────────────────────────────────────────────────────
131
131
 
132
+ def test_map_batch_igual_que_secuencial():
133
+ genoma = _rng_seq(30_000, 20)
134
+ ga = GenomeAligner(genoma, k=15, w=10)
135
+ reads = [genoma[o : o + 400] for o in (1000, 5000, 12000, 20000)]
136
+ seq = [ga.map(r) for r in reads]
137
+
138
+ # ruta secuencial de la API (n_processes=1)
139
+ b1 = ga.map_batch(reads, n_processes=1)
140
+ assert [len(x) for x in b1] == [len(x) for x in seq]
141
+
142
+ # ruta paralela (procesos): mismo resultado y mismo orden
143
+ b2 = ga.map_batch(reads, n_processes=2)
144
+ assert len(b2) == len(reads)
145
+ for got, exp in zip(b2, seq, strict=True):
146
+ assert [m.target_start for m in got] == [m.target_start for m in exp]
147
+
148
+
132
149
  def test_formato_paf():
133
150
  genoma = _rng_seq(30_000, 16)
134
151
  ga = GenomeAligner(genoma, k=15, w=10)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes