seqtree 1.3.0__tar.gz → 1.3.2__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.
- {seqtree-1.3.0 → seqtree-1.3.2}/CHANGELOG.md +9 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/PKG-INFO +1 -1
- {seqtree-1.3.0 → seqtree-1.3.2}/pyproject.toml +1 -1
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/control.py +46 -17
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/gapblock.py +20 -14
- {seqtree-1.3.0 → seqtree-1.3.2}/.gitattributes +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/.gitignore +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/CMakeLists.txt +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/LICENSE +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/README.md +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/RELEASE_AUDIT.md +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/include/seqtree/kmer_index.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/include/seqtree/parallel.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/include/seqtree/seqtree.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/include/seqtree/text_index.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/include/seqtree/types.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/__init__.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/data/control_human_trb_aa.txt.gz +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/distance.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/evalue.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/layout.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/pairwise.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/pmhc.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/pmhc_evalue.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/py.typed +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/seeds.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/python/seqtree/text_evalue.py +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/_bindings.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/alignment_max.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/atomic_write.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/blosum45.inc +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/blosum62.inc +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/blosum80.inc +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/codec.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/count_unit.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/distance.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/engine_seqtm.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/engine_seqtrie.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/engines.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/gapblock.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/index.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/kmer_index.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/pairwise.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/pairwise_detail.hpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/pam100.inc +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/pam250.inc +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/positional_matrix.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/searcher.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/structural.inc +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/substitution_matrix.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/text_index.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/trie.cpp +0 -0
- {seqtree-1.3.0 → seqtree-1.3.2}/src/trie.hpp +0 -0
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
All notable changes to `seqtree`. Dates are release dates. From 1.0.0 the project follows semantic
|
|
4
4
|
versioning: breaking changes need a **major** bump.
|
|
5
5
|
|
|
6
|
+
## [1.3.1] — 2026-10-19
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Gap-prior lookup preparation evaluates pure callbacks only for input lengths,
|
|
11
|
+
avoiding cubic callback work for absent lengths while preserving exact scores,
|
|
12
|
+
count reductions and lane-specific priors. Equal-length/empty axes do not
|
|
13
|
+
evaluate unused priors. The dense native lookup layout is unchanged.
|
|
14
|
+
|
|
6
15
|
## [1.3.0] — 2026-10-19
|
|
7
16
|
|
|
8
17
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: seqtree
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: Fast fuzzy search over biological sequences (C++ core, Python bindings)
|
|
5
5
|
Keywords: sequence-search,fuzzy-matching,CDR3,immunology,bioinformatics,trie
|
|
6
6
|
Author-Email: ISALGO laboratory <mikhail.shugay@gmail.com>
|
|
@@ -122,24 +122,53 @@ def _prune_superseded(cache_dir, name, size, keep):
|
|
|
122
122
|
pass
|
|
123
123
|
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
taken. What the lock buys is work: without it, a cold-cache fan-out of N workers has every
|
|
131
|
-
worker build the same 250k-clonotype index (seconds of CPU, 45 MB written) and N-1 of those
|
|
132
|
-
are thrown away. With it, one builds and the rest wait and load.
|
|
133
|
-
|
|
134
|
-
seqtree has no runtime dependencies, so ``filelock`` is used only if something else already
|
|
135
|
-
put it there -- ``huggingface_hub`` pulls it in, and that is installed whenever a control is
|
|
136
|
-
downloaded rather than read from the bundled asset.
|
|
125
|
+
@contextlib.contextmanager
|
|
126
|
+
def _build_lock(cache, timeout=600):
|
|
127
|
+
"""Serialize cold builds without optional dependencies; process death releases the lock.
|
|
128
|
+
|
|
129
|
+
Keep the guard file: deleting it could let waiters lock different inodes.
|
|
137
130
|
"""
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
131
|
+
import errno
|
|
132
|
+
import time
|
|
133
|
+
|
|
134
|
+
with open(cache + ".lock", "a+b") as handle:
|
|
135
|
+
if os.name == "nt":
|
|
136
|
+
import msvcrt
|
|
137
|
+
if handle.seek(0, 2) == 0:
|
|
138
|
+
handle.write(b"\0")
|
|
139
|
+
handle.flush()
|
|
140
|
+
|
|
141
|
+
def acquire():
|
|
142
|
+
handle.seek(0)
|
|
143
|
+
msvcrt.locking(handle.fileno(), msvcrt.LK_NBLCK, 1)
|
|
144
|
+
|
|
145
|
+
def release():
|
|
146
|
+
handle.seek(0)
|
|
147
|
+
msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
|
|
148
|
+
else:
|
|
149
|
+
import fcntl
|
|
150
|
+
|
|
151
|
+
def acquire():
|
|
152
|
+
fcntl.flock(handle.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
153
|
+
|
|
154
|
+
def release():
|
|
155
|
+
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
|
|
156
|
+
|
|
157
|
+
deadline = time.monotonic() + timeout
|
|
158
|
+
while True:
|
|
159
|
+
try:
|
|
160
|
+
acquire()
|
|
161
|
+
break
|
|
162
|
+
except OSError as exc:
|
|
163
|
+
if exc.errno not in (errno.EACCES, errno.EAGAIN, errno.EDEADLK):
|
|
164
|
+
raise
|
|
165
|
+
if time.monotonic() >= deadline:
|
|
166
|
+
raise TimeoutError(f"Another control build owns {cache}.lock after {timeout:g}s") from exc
|
|
167
|
+
time.sleep(0.05)
|
|
168
|
+
try:
|
|
169
|
+
yield
|
|
170
|
+
finally:
|
|
171
|
+
release()
|
|
143
172
|
|
|
144
173
|
|
|
145
174
|
def _load_cached(cache):
|
|
@@ -499,19 +499,22 @@ def gapblock_score(
|
|
|
499
499
|
return best + gap_cost(d, gap_open, gap_extend), best_i
|
|
500
500
|
|
|
501
501
|
|
|
502
|
-
def _prior_cube(prior: GapPrior, width: int) -> list[int]:
|
|
502
|
+
def _prior_cube(prior: GapPrior, width: int, lengths=None) -> list[int]:
|
|
503
503
|
"""Flatten ``prior`` to ``[m][d][i]`` with stride ``width + 1``, for the C++ kernel.
|
|
504
504
|
|
|
505
505
|
Only reachable cells are filled: ``d >= 1`` (the prior never applies to equal lengths) and
|
|
506
506
|
``i <= m - d`` (the block start indexes the shorter sequence). The rest stay zero and are
|
|
507
|
-
never read.
|
|
507
|
+
never read. Input lengths restrict callback evaluation to possible full-length pairs;
|
|
508
|
+
the dense native layout is unchanged. None retains the full-width internal oracle.
|
|
508
509
|
"""
|
|
509
510
|
w1 = width + 1
|
|
510
511
|
cube = [0] * (w1 * w1 * w1)
|
|
511
|
-
|
|
512
|
-
|
|
512
|
+
lengths = range(w1) if lengths is None else sorted(set(lengths))
|
|
513
|
+
for row, m in enumerate(lengths):
|
|
514
|
+
for n in reversed(lengths[:row]):
|
|
515
|
+
d = m - n
|
|
513
516
|
base = (m * w1 + d) * w1
|
|
514
|
-
for i in range(
|
|
517
|
+
for i in range(n + 1):
|
|
515
518
|
v = int(prior(i, d, m))
|
|
516
519
|
if v < 0:
|
|
517
520
|
raise ValueError(f"gap_prior({i}, {d}, {m}) = {v} is negative")
|
|
@@ -561,7 +564,8 @@ def score_matrix(
|
|
|
561
564
|
leaving this at 1 with a real matrix makes gaps ~14x cheaper than substitutions.
|
|
562
565
|
gap_extend: Cost per additional gap column.
|
|
563
566
|
gap_prior: :data:`GapPrior`, materialized once into a lookup cube and then read from C++.
|
|
564
|
-
|
|
567
|
+
Evaluated only for unequal input lengths and valid block starts; it must be
|
|
568
|
+
a pure nonnegative function. ``None`` lets the score alone choose the block position.
|
|
565
569
|
alphabet: ``"aa"``, ``"nt"``, or ``"iupac"``. Symbols outside it raise.
|
|
566
570
|
threads: Worker threads; ``0`` means one per core. Rows are disjoint, so this scales.
|
|
567
571
|
|
|
@@ -586,8 +590,9 @@ def score_matrix(
|
|
|
586
590
|
gap_open = _default_gap_open(matrix)
|
|
587
591
|
if gap_open < 0 or gap_extend < 0:
|
|
588
592
|
raise ValueError("gap_open and gap_extend must be >= 0")
|
|
589
|
-
|
|
590
|
-
|
|
593
|
+
lengths = {len(s) for s in q + r}
|
|
594
|
+
width = max(lengths, default=0)
|
|
595
|
+
cube = _prior_cube(gap_prior, width, lengths) if gap_prior is not None else []
|
|
591
596
|
return _gapblock_matrix(q, r, alphabet, matrix, gap_open, gap_extend, cube, width, threads,
|
|
592
597
|
_position_weights(position_weights_by_length, q, r),
|
|
593
598
|
*_group_options(query_group_ids, reference_group_ids, group_distances, q, r),
|
|
@@ -611,8 +616,9 @@ def _batch_options(axes, matrix, gap_open, gap_extend, gap_prior, threads):
|
|
|
611
616
|
go = _batch_integer(_default_gap_open(matrix) if gap_open is None else gap_open, "gap_open")
|
|
612
617
|
ge = _batch_integer(gap_extend, "gap_extend")
|
|
613
618
|
threads = _batch_integer(threads, "threads")
|
|
614
|
-
|
|
615
|
-
|
|
619
|
+
lengths = {len(s) for axis in axes for s in axis}
|
|
620
|
+
width = max(lengths, default=0)
|
|
621
|
+
cube = _prior_cube(gap_prior, width, lengths) if gap_prior is not None else []
|
|
616
622
|
return go, ge, cube, width, threads
|
|
617
623
|
|
|
618
624
|
|
|
@@ -915,11 +921,11 @@ def _additional_geometry(geometry, features):
|
|
|
915
921
|
geometry.additional_features = terms
|
|
916
922
|
|
|
917
923
|
|
|
918
|
-
def _lane_prior(geometry, prior, width):
|
|
924
|
+
def _lane_prior(geometry, prior, width, queries, refs):
|
|
919
925
|
if prior is not None:
|
|
920
926
|
if not hasattr(geometry, "gap_prior"):
|
|
921
927
|
raise RuntimeError("lane priors require native grouped gap_prior support")
|
|
922
|
-
geometry.gap_prior = _prior_cube(prior, width)
|
|
928
|
+
geometry.gap_prior = _prior_cube(prior, width, {len(s) for s in queries + refs})
|
|
923
929
|
|
|
924
930
|
|
|
925
931
|
def _alignment_options(geometry, q, r, mode, scale, left, right, matrix, prior,
|
|
@@ -1099,8 +1105,8 @@ def grouped_paired_sum_count_batch(queries_a, queries_b, refs_a, refs_b, thresho
|
|
|
1099
1105
|
reference_position_weights_beta, feature_offsets_beta, score_quantum_beta)
|
|
1100
1106
|
_additional_geometry(alpha, additional_feature_offsets_alpha)
|
|
1101
1107
|
_additional_geometry(beta, additional_feature_offsets_beta)
|
|
1102
|
-
_lane_prior(alpha, gap_prior_alpha, width)
|
|
1103
|
-
_lane_prior(beta, gap_prior_beta, width)
|
|
1108
|
+
_lane_prior(alpha, gap_prior_alpha, width, qa, ra)
|
|
1109
|
+
_lane_prior(beta, gap_prior_beta, width, qb, rb)
|
|
1104
1110
|
_alignment_options(alpha, qa, ra, alignment_mode, alignment_scale, trim_left, trim_right,
|
|
1105
1111
|
matrix, gap_prior if gap_prior_alpha is None else gap_prior_alpha, position_weights_by_length_alpha, query_position_weights_alpha,
|
|
1106
1112
|
reference_position_weights_alpha)
|
|
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
|
|
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
|
|
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
|