pav3 3.0.0.dev17__tar.gz → 3.0.0.dev19__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.
- {pav3-3.0.0.dev17/src/pav3.egg-info → pav3-3.0.0.dev19}/PKG-INFO +4 -1
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/README.md +3 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/__init__.py +1 -1
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/features.py +1 -1
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/score.py +145 -66
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/trim.py +2 -2
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/workflow/rules/call.smk +49 -318
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/fig/tracks.py +2 -1
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/call.py +31 -12
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/interval.py +181 -22
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/struct.py +10 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/variant.py +166 -132
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/params.py +0 -8
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/vcf.py +62 -129
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/workflow/__pycache__/__init__.cpython-312.pyc +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/workflow/__pycache__/call.cpython-312.pyc +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/workflow/__pycache__/resource.cpython-312.pyc +0 -0
- pav3-3.0.0.dev19/src/pav3/workflow/call.py +476 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19/src/pav3.egg-info}/PKG-INFO +4 -1
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3.egg-info/SOURCES.txt +2 -1
- pav3-3.0.0.dev19/tests/test_score_dtype.py +24 -0
- pav3-3.0.0.dev17/src/pav3/workflow/call.py +0 -184
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/LICENSE +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/pyproject.toml +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/setup.cfg +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/__main__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/lcmodel/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/lcmodel/_lcmodel.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/lcmodel/_lcmodel_logistic.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/lcmodel/_lcmodel_null.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/lcmodel/_util.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/lift.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/op.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/records.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/align/tables.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/anno.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/call/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/call/expr.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/call/integrate.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/call/intra.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/call/util.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/cli/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/cli/_cli.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/cli/_common_opt.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/cli/_subcommand_batch.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/cli/_subcommand_call.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/cli/_subcommand_license.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/const.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/default/model.json +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/human_95_80/model.json +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/human_95_80/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/human_95_80/weights.npz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/human_98_80/model.json +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/human_98_80/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/human_98_80/weights.npz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/prim_90_50/model.json +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/prim_90_50/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/prim_90_50/weights.npz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/primdiv_85_50/model.json +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/primdiv_85_50/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/lcmodel/primdiv_85_50/weights.npz +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/tracks/alignment_track_fields.tsv +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/tracks/variant_track_fields.tsv +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/workflow/Snakefile +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/workflow/profiles/pav_default/config.yaml +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/workflow/rules/align.smk +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/workflow/rules/data.smk +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/workflow/rules/pipeline.smk +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/data/workflow/rules/tracks.smk +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/dev/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/dev/imports.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/dev/inter.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/dev/path.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/dev/sm.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/dev/tables.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/fig/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/fig/const.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/fig/density.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/fig/util.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/inv.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/io.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/kde.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/chain.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/io.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/region_kde.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/lgsv/resources.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/pipeline.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/region.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/schema.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/seq.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/util.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/workflow/__init__.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3/workflow/resource.py +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3.egg-info/dependency_links.txt +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3.egg-info/entry_points.txt +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3.egg-info/requires.txt +0 -0
- {pav3-3.0.0.dev17 → pav3-3.0.0.dev19}/src/pav3.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pav3
|
|
3
|
-
Version: 3.0.0.
|
|
3
|
+
Version: 3.0.0.dev19
|
|
4
4
|
Summary: Assembly-based variant discovery
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -31,6 +31,9 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
Variant caller for assembled genomes.
|
|
33
33
|
|
|
34
|
+
Please discuss with authors prior to publishing results using PAV 3. A paper describing PAV 3 is in
|
|
35
|
+
preparation and is expected mid-2026.
|
|
36
|
+
|
|
34
37
|
## Development release (Please read)
|
|
35
38
|
|
|
36
39
|
PAV 3 is currently a development release and may contain bugs. Please report problems you encounter.
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
Variant caller for assembled genomes.
|
|
8
8
|
|
|
9
|
+
Please discuss with authors prior to publishing results using PAV 3. A paper describing PAV 3 is in
|
|
10
|
+
preparation and is expected mid-2026.
|
|
11
|
+
|
|
9
12
|
## Development release (Please read)
|
|
10
13
|
|
|
11
14
|
PAV 3 is currently a development release and may contain bugs. Please report problems you encounter.
|
|
@@ -59,7 +59,7 @@ def feature(dtype: type[pl.DataType], name: str = None) -> Callable:
|
|
|
59
59
|
self,
|
|
60
60
|
df: pl.DataFrame,
|
|
61
61
|
df_qry_fai: Optional[pl.DataFrame] = None,
|
|
62
|
-
temp_features: Optional[set[str]] = None
|
|
62
|
+
temp_features: Optional[set[str]] = None,
|
|
63
63
|
) -> pl.DataFrame:
|
|
64
64
|
"""Wrap feature functions with a consistent signature.
|
|
65
65
|
|
|
@@ -12,7 +12,6 @@ __all__ = [
|
|
|
12
12
|
'get_affine_by_params',
|
|
13
13
|
]
|
|
14
14
|
|
|
15
|
-
from dataclasses import dataclass, field
|
|
16
15
|
from abc import ABC, abstractmethod
|
|
17
16
|
import re
|
|
18
17
|
from typing import Any, Optional
|
|
@@ -20,6 +19,8 @@ from typing import Any, Optional
|
|
|
20
19
|
import numpy as np
|
|
21
20
|
import polars as pl
|
|
22
21
|
|
|
22
|
+
from agglovar.meta.decorators import immutable
|
|
23
|
+
|
|
23
24
|
from . import op
|
|
24
25
|
|
|
25
26
|
AFFINE_SCORE_MATCH: float = 2.0
|
|
@@ -29,16 +30,29 @@ AFFINE_SCORE_MISMATCH: float = 4.0
|
|
|
29
30
|
"""Mismatch score (minimap2 default)"""
|
|
30
31
|
|
|
31
32
|
AFFINE_SCORE_GAP: tuple[tuple[float, float], ...] = ((4.0, 2.0), (24.0, 1.0))
|
|
32
|
-
"""Multi-affine
|
|
33
|
+
"""Multi-affine scores as a sequence of gap-open and gap-extend penalties (minimap2 defaults)."""
|
|
34
|
+
|
|
35
|
+
AFFINE_SCORE_TS = (1.0, 50)
|
|
36
|
+
"""Template switch score.
|
|
37
|
+
|
|
38
|
+
May be a single float (penalty of a template switch) or a tuple of a multiplier and a gap size
|
|
39
|
+
(e.g. "(1.0, 50)" sets the template switch score to the cost of a 50 bp gap).
|
|
40
|
+
"""
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
"""
|
|
42
|
+
AFFINE_OFF_VARIANT_PENALTY = 2.0
|
|
43
|
+
"""Multiplier for off-variant penalties when scoring.
|
|
44
|
+
|
|
45
|
+
For example, the gap score of query (inserted) bases for a deletion would be multiplied by this
|
|
46
|
+
factor. Used for picking the right variant call type for a set of alignment features.
|
|
47
|
+
"""
|
|
36
48
|
|
|
37
49
|
DEFAULT_ALIGN_SCORE_MODEL = (
|
|
38
50
|
f'affine::'
|
|
39
51
|
f'match={AFFINE_SCORE_MATCH},'
|
|
40
52
|
f'mismatch={AFFINE_SCORE_MISMATCH},'
|
|
41
|
-
f'gap={";".join([f"{gap_open}:{gap_extend}" for gap_open, gap_extend in AFFINE_SCORE_GAP])}'
|
|
53
|
+
f'gap={";".join([f"{gap_open}:{gap_extend}" for gap_open, gap_extend in AFFINE_SCORE_GAP])},'
|
|
54
|
+
f'ts={str(AFFINE_SCORE_TS).replace(',', ':')},'
|
|
55
|
+
f'off_var={AFFINE_OFF_VARIANT_PENALTY}'
|
|
42
56
|
)
|
|
43
57
|
"""Default alignment score model specification."""
|
|
44
58
|
|
|
@@ -54,7 +68,7 @@ class ScoreModel(ABC):
|
|
|
54
68
|
|
|
55
69
|
:returns: Match score.
|
|
56
70
|
"""
|
|
57
|
-
|
|
71
|
+
...
|
|
58
72
|
|
|
59
73
|
@abstractmethod
|
|
60
74
|
def mismatch(self, n: int = 1) -> float:
|
|
@@ -64,7 +78,7 @@ class ScoreModel(ABC):
|
|
|
64
78
|
|
|
65
79
|
:returns: Mismatch score.
|
|
66
80
|
"""
|
|
67
|
-
|
|
81
|
+
...
|
|
68
82
|
|
|
69
83
|
@abstractmethod
|
|
70
84
|
def gap(self, n: int = 1) -> float:
|
|
@@ -74,15 +88,26 @@ class ScoreModel(ABC):
|
|
|
74
88
|
|
|
75
89
|
:returns: Gap score for one operation.
|
|
76
90
|
"""
|
|
77
|
-
|
|
91
|
+
...
|
|
78
92
|
|
|
79
93
|
@abstractmethod
|
|
80
|
-
def template_switch(self) -> float:
|
|
94
|
+
def template_switch(self, n: int = 1) -> float:
|
|
81
95
|
"""Score a template switch.
|
|
82
96
|
|
|
83
97
|
:returns: Template switch score.
|
|
84
98
|
"""
|
|
85
|
-
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def off_variant(self) -> float:
|
|
103
|
+
"""Multiplier for off-variant features.
|
|
104
|
+
|
|
105
|
+
For scored features that are incompatible with a variant type, for example reference gaps
|
|
106
|
+
at an insertion site, multiply the gap score by this factor.
|
|
107
|
+
|
|
108
|
+
:returns: Multiplier for off-variant features.
|
|
109
|
+
"""
|
|
110
|
+
...
|
|
86
111
|
|
|
87
112
|
def score_op(self, op_code: int | str, op_len: int) -> float:
|
|
88
113
|
"""Score one alignment operation.
|
|
@@ -138,7 +163,7 @@ class ScoreModel(ABC):
|
|
|
138
163
|
.select(pl.col('align_ops'))
|
|
139
164
|
.to_series()
|
|
140
165
|
.map_elements(op.row_to_arr, return_dtype=pl.Object)
|
|
141
|
-
.map_elements(self.score_op_arr, return_dtype=pl.Float32)
|
|
166
|
+
.map_elements(lambda op_arr: np.float32(self.score_op_arr(op_arr)), return_dtype=pl.Float32)
|
|
142
167
|
)
|
|
143
168
|
|
|
144
169
|
# A pure polars implementation is slower:
|
|
@@ -163,7 +188,7 @@ class ScoreModel(ABC):
|
|
|
163
188
|
|
|
164
189
|
:returns A copy of this score model that does not penalize gaps.
|
|
165
190
|
"""
|
|
166
|
-
|
|
191
|
+
...
|
|
167
192
|
|
|
168
193
|
@abstractmethod
|
|
169
194
|
def model_param_string(self) -> str:
|
|
@@ -171,19 +196,19 @@ class ScoreModel(ABC):
|
|
|
171
196
|
|
|
172
197
|
:returns: A parameter string for this score model.
|
|
173
198
|
"""
|
|
174
|
-
|
|
199
|
+
...
|
|
175
200
|
|
|
176
201
|
@abstractmethod
|
|
177
202
|
def __eq__(self, other: 'ScoreModel') -> bool:
|
|
178
203
|
"""Check equality with another :class:`ScoreModel`."""
|
|
179
|
-
|
|
204
|
+
...
|
|
180
205
|
|
|
181
206
|
def __repr__(self) -> str:
|
|
182
207
|
"""Get a string representation of the model."""
|
|
183
208
|
return 'ScoreModel(Interface)'
|
|
184
209
|
|
|
185
210
|
|
|
186
|
-
@
|
|
211
|
+
@immutable
|
|
187
212
|
class AffineScoreModel(ScoreModel):
|
|
188
213
|
"""Affine score model with default values modeled on minimap2 (2.26) default parameters.
|
|
189
214
|
|
|
@@ -193,24 +218,42 @@ class AffineScoreModel(ScoreModel):
|
|
|
193
218
|
:param score_template_switch: Template switch penalty. If none, defaults to 2x the penalty of a 50 bp gap.
|
|
194
219
|
"""
|
|
195
220
|
|
|
196
|
-
score_match: float
|
|
197
|
-
score_mismatch: float
|
|
198
|
-
score_affine_gap: tuple[tuple[float, float], ...]
|
|
199
|
-
score_template_switch: float
|
|
200
|
-
|
|
201
|
-
def __post_init__(self):
|
|
202
|
-
"""Ensure penalties are set correctly."""
|
|
203
|
-
object.__setattr__(self, 'score_match', float(abs(self.score_match)))
|
|
204
|
-
object.__setattr__(self, 'score_mismatch', float(-abs(self.score_mismatch)))
|
|
205
|
-
object.__setattr__(self, 'score_affine_gap', tuple((
|
|
206
|
-
(float(-abs(gap_open)), float(-abs(gap_extend)))
|
|
207
|
-
for gap_open, gap_extend in self.score_affine_gap
|
|
208
|
-
)))
|
|
221
|
+
score_match: float
|
|
222
|
+
score_mismatch: float
|
|
223
|
+
score_affine_gap: tuple[tuple[float, float], ...]
|
|
224
|
+
score_template_switch: float
|
|
225
|
+
mul_off_variant: float
|
|
209
226
|
|
|
210
|
-
|
|
211
|
-
|
|
227
|
+
def __init__(
|
|
228
|
+
self,
|
|
229
|
+
score_match: float = AFFINE_SCORE_MATCH,
|
|
230
|
+
score_mismatch: float = AFFINE_SCORE_MISMATCH,
|
|
231
|
+
score_affine_gap: tuple[tuple[float, float], ...] = AFFINE_SCORE_GAP,
|
|
232
|
+
score_template_switch: float | tuple[float, int] = AFFINE_SCORE_TS,
|
|
233
|
+
mul_off_variant: float = AFFINE_OFF_VARIANT_PENALTY,
|
|
234
|
+
):
|
|
235
|
+
self.score_match = abs(float(score_match))
|
|
236
|
+
self.score_mismatch = -abs(float(score_mismatch))
|
|
237
|
+
self.score_affine_gap = tuple(
|
|
238
|
+
(-abs(float(gap_open)), -abs(float(gap_extend)))
|
|
239
|
+
for gap_open, gap_extend in score_affine_gap
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if isinstance(score_template_switch, tuple):
|
|
243
|
+
if len(score_template_switch) != 2:
|
|
244
|
+
raise ValueError(f'Template switch score tuple must be 2 elements (multiplier, gap size): {score_template_switch}')
|
|
245
|
+
|
|
246
|
+
self.score_template_switch = (
|
|
247
|
+
abs(float(score_template_switch[0]))
|
|
248
|
+
* self.gap(abs(int(score_template_switch[1])))
|
|
249
|
+
)
|
|
212
250
|
else:
|
|
213
|
-
|
|
251
|
+
self.score_template_switch = -abs(float(score_template_switch))
|
|
252
|
+
|
|
253
|
+
self.mul_off_variant = abs(float(mul_off_variant))
|
|
254
|
+
|
|
255
|
+
if self.mul_off_variant <= 1.0:
|
|
256
|
+
raise ValueError(f'Off-target variant multiplier must be > 1.0: {self.mul_off_variant}')
|
|
214
257
|
|
|
215
258
|
def match(self, n: int = 1) -> float:
|
|
216
259
|
"""Score match.
|
|
@@ -247,12 +290,24 @@ class AffineScoreModel(ScoreModel):
|
|
|
247
290
|
for gap_open, gap_extend in self.score_affine_gap
|
|
248
291
|
])
|
|
249
292
|
|
|
250
|
-
def template_switch(self) -> float:
|
|
293
|
+
def template_switch(self, n: int = 1) -> float:
|
|
251
294
|
"""Score a template switch.
|
|
252
295
|
|
|
296
|
+
:param n: Number of template switches.
|
|
297
|
+
|
|
253
298
|
:returns: Template switch score.
|
|
254
299
|
"""
|
|
255
|
-
return self.score_template_switch
|
|
300
|
+
return self.score_template_switch * n
|
|
301
|
+
|
|
302
|
+
def off_variant(self) -> float:
|
|
303
|
+
"""Multiplier for off-variant features.
|
|
304
|
+
|
|
305
|
+
For scored features that are incompatible with a variant type, for example reference gaps
|
|
306
|
+
at an insertion site, multiply the gap score by this factor.
|
|
307
|
+
|
|
308
|
+
:returns: Multiplier for off-variant features.
|
|
309
|
+
"""
|
|
310
|
+
return self.mul_off_variant
|
|
256
311
|
|
|
257
312
|
def mismatch_model(self) -> 'ScoreModel':
|
|
258
313
|
"""Create a version of this model that scores only mismatches (ignores gaps).
|
|
@@ -266,7 +321,8 @@ class AffineScoreModel(ScoreModel):
|
|
|
266
321
|
score_match=self.score_match,
|
|
267
322
|
score_mismatch=self.score_mismatch,
|
|
268
323
|
score_affine_gap=((0.0, 0.0),),
|
|
269
|
-
score_template_switch=
|
|
324
|
+
score_template_switch=self.score_template_switch,
|
|
325
|
+
mul_off_variant=self.mul_off_variant,
|
|
270
326
|
)
|
|
271
327
|
|
|
272
328
|
def score_operations(
|
|
@@ -292,10 +348,9 @@ class AffineScoreModel(ScoreModel):
|
|
|
292
348
|
gap_score[:, 0] = np.max(gap_score, axis=1)
|
|
293
349
|
|
|
294
350
|
return (
|
|
295
|
-
np.sum(op_arr[:, 1] * (op_arr[:, 0] == op.EQ) * self.score_match)
|
|
296
|
-
np.sum(op_arr[:, 1] * (op_arr[:, 0] == op.X) * self.score_mismatch)
|
|
297
|
-
# If no gap penalties (i.e. mismatch model), then gap_score is -inf (set to 0.0)
|
|
298
|
-
np.nan_to_num(gap_score[:, 0], neginf=0.0).sum()
|
|
351
|
+
np.sum(op_arr[:, 1] * (op_arr[:, 0] == op.EQ) * self.score_match)
|
|
352
|
+
+ np.sum(op_arr[:, 1] * (op_arr[:, 0] == op.X) * self.score_mismatch)
|
|
353
|
+
+ np.nan_to_num(gap_score[:, 0], neginf=0.0).sum() # If no gap penalties (i.e. mismatch model), then gap_score is -inf (set to 0.0)
|
|
299
354
|
)
|
|
300
355
|
|
|
301
356
|
def model_param_string(self) -> str:
|
|
@@ -305,7 +360,14 @@ class AffineScoreModel(ScoreModel):
|
|
|
305
360
|
"""
|
|
306
361
|
gap_str = ";".join([f"{gap_open}:{gap_extend}" for gap_open, gap_extend in self.score_affine_gap])
|
|
307
362
|
|
|
308
|
-
return
|
|
363
|
+
return (
|
|
364
|
+
f'affine::'
|
|
365
|
+
f'match={self.score_match},'
|
|
366
|
+
f'mismatch={self.score_mismatch},'
|
|
367
|
+
f'gap={gap_str},'
|
|
368
|
+
f'ts={self.score_template_switch},'
|
|
369
|
+
f'off_var={self.mul_off_variant}'
|
|
370
|
+
)
|
|
309
371
|
|
|
310
372
|
def __eq__(self, other: 'ScoreModel') -> bool:
|
|
311
373
|
"""Check equality with another :class:`ScoreModel`."""
|
|
@@ -370,14 +432,23 @@ def get_affine_by_params(param_string: str) -> AffineScoreModel:
|
|
|
370
432
|
:raises ValueError: If the string cannot be parsed.
|
|
371
433
|
"""
|
|
372
434
|
# Set defaults
|
|
373
|
-
params:
|
|
374
|
-
AFFINE_SCORE_MATCH,
|
|
375
|
-
AFFINE_SCORE_MISMATCH,
|
|
376
|
-
AFFINE_SCORE_GAP,
|
|
377
|
-
AFFINE_SCORE_TS
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
435
|
+
params: dict[str, Any] = {
|
|
436
|
+
'score_match': AFFINE_SCORE_MATCH,
|
|
437
|
+
'score_mismatch': AFFINE_SCORE_MISMATCH,
|
|
438
|
+
'score_affine_gap': AFFINE_SCORE_GAP,
|
|
439
|
+
'score_template_switch': AFFINE_SCORE_TS,
|
|
440
|
+
'mul_off_variant': AFFINE_OFF_VARIANT_PENALTY,
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
key_to_param = {
|
|
444
|
+
'match': 'score_match',
|
|
445
|
+
'mismatch': 'score_mismatch',
|
|
446
|
+
'gap': 'score_affine_gap',
|
|
447
|
+
'ts': 'score_template_switch',
|
|
448
|
+
'off_var': 'mul_off_variant',
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
keys = list(key_to_param.keys())
|
|
381
452
|
|
|
382
453
|
# Sanitize parameter string
|
|
383
454
|
if param_string is not None:
|
|
@@ -422,19 +493,15 @@ def get_affine_by_params(param_string: str) -> AffineScoreModel:
|
|
|
422
493
|
|
|
423
494
|
param_pos += 1
|
|
424
495
|
|
|
425
|
-
if
|
|
426
|
-
|
|
427
|
-
val = abs(float(val))
|
|
428
|
-
except ValueError as e:
|
|
429
|
-
raise ValueError(f'Invalid numeric value for parameter "{key}": {val}') from e
|
|
430
|
-
|
|
431
|
-
if key == 'match':
|
|
432
|
-
params[0] = val
|
|
496
|
+
if not val:
|
|
497
|
+
continue
|
|
433
498
|
|
|
434
|
-
|
|
435
|
-
|
|
499
|
+
try:
|
|
500
|
+
param_name = key_to_param[key]
|
|
501
|
+
except KeyError:
|
|
502
|
+
raise ValueError(f'Unrecognized parameter key: {key}')
|
|
436
503
|
|
|
437
|
-
|
|
504
|
+
if key == 'gap':
|
|
438
505
|
gap_list = []
|
|
439
506
|
|
|
440
507
|
for gap_pair in val.split(';'):
|
|
@@ -454,18 +521,30 @@ def get_affine_by_params(param_string: str) -> AffineScoreModel:
|
|
|
454
521
|
|
|
455
522
|
gap_list.append((gap_open, gap_extend))
|
|
456
523
|
|
|
457
|
-
|
|
524
|
+
val = tuple(gap_list)
|
|
458
525
|
|
|
459
|
-
elif key == 'ts':
|
|
460
|
-
|
|
526
|
+
elif key == 'ts' and val[0] == '(' and val[-1] == ')':
|
|
527
|
+
ts_tuple = val[1:-1].split(':')
|
|
528
|
+
|
|
529
|
+
if len(ts_tuple) != 2:
|
|
530
|
+
raise ValueError(f'Key "ts" requires a set value or a tuple[float, int]: Found {val}')
|
|
531
|
+
|
|
532
|
+
val = (
|
|
533
|
+
float(ts_tuple[0]),
|
|
534
|
+
int(ts_tuple[1]),
|
|
535
|
+
)
|
|
461
536
|
|
|
462
537
|
else:
|
|
463
|
-
|
|
538
|
+
try:
|
|
539
|
+
val = abs(float(val))
|
|
540
|
+
except ValueError as e:
|
|
541
|
+
raise ValueError(f'Invalid numeric value for parameter "{key}": {val}') from e
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
params[param_name] = val
|
|
545
|
+
|
|
464
546
|
|
|
465
547
|
# Return alignment object
|
|
466
548
|
return AffineScoreModel(
|
|
467
|
-
|
|
468
|
-
score_mismatch=params[1],
|
|
469
|
-
score_affine_gap=params[2],
|
|
470
|
-
score_template_switch=params[3]
|
|
549
|
+
**params
|
|
471
550
|
)
|
|
@@ -83,7 +83,7 @@ TRIM_DESC = {
|
|
|
83
83
|
}
|
|
84
84
|
"""Trim mode description."""
|
|
85
85
|
|
|
86
|
-
DEFAULT_MIN_TRIM_QRY_LEN =
|
|
86
|
+
DEFAULT_MIN_TRIM_QRY_LEN = 1
|
|
87
87
|
"""Default minimum query length. Alignments with fewer query bases are removed."""
|
|
88
88
|
|
|
89
89
|
|
|
@@ -111,7 +111,7 @@ def trim_alignments_qry(
|
|
|
111
111
|
raise ValueError('Query FAI dataframe is None.')
|
|
112
112
|
|
|
113
113
|
if min_trim_qry_len < 1:
|
|
114
|
-
|
|
114
|
+
min_trim_qry_len = 1
|
|
115
115
|
|
|
116
116
|
score_model = get_score_model(score_model)
|
|
117
117
|
|