Skip to content

Commit a6231e4

Browse files
committed
feat: add penalty weights for pick_hyb_probe task
1 parent 70f6840 commit a6231e4

File tree

3 files changed

+63
-16
lines changed

3 files changed

+63
-16
lines changed

prymer/primer3/primer3_input.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
The module uses:
99
1010
1. [`Primer3Parameters`][prymer.primer3.primer3_parameters.Primer3Parameters]
11-
to specify user-specified criteria for primer design
12-
2. [`Primer3Weights`][prymer.primer3.primer3_weights.Primer3Weights] to establish penalties
13-
based on those criteria
14-
3. [`Primer3Task`][prymer.primer3.primer3_task.Primer3Task] to organize task-specific
11+
to specify user-specified criteria for primer design
12+
2. [`PrimerAndAmpliconWeights`][prymer.primer3.primer3_weights.PrimerAndAmpliconWeights]
13+
to establish penalties based on those criteria
14+
3. [`ProbeWeights`][prymer.primer3.primer3_weights.ProbeWeights] to specify penalties based on probe
15+
design criteria
16+
4. [`Primer3Task`][prymer.primer3.primer3_task.Primer3Task] to organize task-specific
1517
logic.
16-
4. [`Span`](index.md#prymer.api.span.Span] to specify the target region.
18+
5. [`Span`](index.md#prymer.api.span.Span] to specify the target region.
1719
1820
The `Primer3Input.to_input_tags(]` method
1921
The main purpose of this class is to generate the
@@ -81,12 +83,14 @@
8183

8284
from dataclasses import dataclass
8385
from typing import Any
86+
from typing import Optional
8487

8588
from prymer.api.span import Span
8689
from prymer.primer3.primer3_input_tag import Primer3InputTag
8790
from prymer.primer3.primer3_parameters import Primer3Parameters
8891
from prymer.primer3.primer3_task import Primer3TaskType
89-
from prymer.primer3.primer3_weights import Primer3Weights
92+
from prymer.primer3.primer3_weights import PrimerAndAmpliconWeights
93+
from prymer.primer3.primer3_weights import ProbeWeights
9094

9195

9296
@dataclass(frozen=True, init=True, slots=True)
@@ -96,7 +100,8 @@ class Primer3Input:
96100
target: Span
97101
task: Primer3TaskType
98102
params: Primer3Parameters
99-
weights: Primer3Weights = Primer3Weights()
103+
primer_weights: Optional[PrimerAndAmpliconWeights] = PrimerAndAmpliconWeights()
104+
probe_weights: Optional[ProbeWeights] = None
100105

101106
def to_input_tags(self, design_region: Span) -> dict[Primer3InputTag, Any]:
102107
"""Assembles `Primer3InputTag` and values for input to `Primer3`
@@ -116,6 +121,7 @@ def to_input_tags(self, design_region: Span) -> dict[Primer3InputTag, Any]:
116121
assembled_tags = {
117122
**primer3_task_params,
118123
**self.params.to_input_tags(),
119-
**self.weights.to_input_tags(),
124+
**self.primer_weights.to_input_tags(),
125+
**(self.probe_weights.to_input_tags() if self.probe_weights is not None else {}),
120126
}
121127
return assembled_tags

prymer/primer3/primer3_weights.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
1616
1717
```python
18-
>>> Primer3Weights(product_size_lt=1, product_size_gt=1)
18+
>>> PrimerAndAmpliconWeights(product_size_lt=1, product_size_gt=1)
1919
Primer3Weights(product_size_lt=1, product_size_gt=1, ...)
20-
>>> Primer3Weights(product_size_lt=5, product_size_gt=1)
20+
>>> PrimerAndAmpliconWeights(product_size_lt=5, product_size_gt=1)
2121
Primer3Weights(product_size_lt=5, product_size_gt=1, ...)
2222
2323
```
@@ -30,22 +30,23 @@
3030

3131

3232
@dataclass(frozen=True, init=True, slots=True)
33-
class Primer3Weights:
33+
class PrimerAndAmpliconWeights:
3434
"""Holds the weights that Primer3 uses to adjust penalties
3535
that originate from the designed primer(s).
3636
3737
The weights that Primer3 uses when a parameter is less than optimal are labeled with "_lt".
3838
"_gt" weights are penalties applied when a parameter is greater than optimal.
3939
40+
Some of these settings depart from the default settings enumerated in the Primer3 manual.
4041
Please see the Primer3 manual for additional details:
4142
https://primer3.org/manual.html#globalTags
4243
4344
Example:
44-
>>> Primer3Weights() #default implementation
45-
Primer3Weights(product_size_lt=1, product_size_gt=1, product_tm_lt=0.0, product_tm_gt=0.0, primer_end_stability=0.25, primer_gc_lt=0.25, primer_gc_gt=0.25, primer_self_any=0.1, primer_self_end=0.1, primer_size_lt=0.5, primer_size_gt=0.1, primer_tm_lt=1.0, primer_tm_gt=1.0)
45+
>>> PrimerAndAmpliconWeights() #default implementation
46+
PrimerAndAmpliconWeights(product_size_lt=1, product_size_gt=1, product_tm_lt=0.0, product_tm_gt=0.0, primer_end_stability=0.25, primer_gc_lt=0.25, primer_gc_gt=0.25, primer_self_any=0.1, primer_self_end=0.1, primer_size_lt=0.5, primer_size_gt=0.1, primer_tm_lt=1.0, primer_tm_gt=1.0, probe_size_lt=0.25, probe_size_gt=0.25, probe_tm_lt=1.0, probe_tm_gt=1.0, probe_gc_lt=0.5, probe_gc_gt=0.5, probe_self_any=1.0, probe_self_end=1.0)
4647
47-
>>> Primer3Weights(product_size_lt=5)
48-
Primer3Weights(product_size_lt=5, product_size_gt=1, product_tm_lt=0.0, product_tm_gt=0.0, primer_end_stability=0.25, primer_gc_lt=0.25, primer_gc_gt=0.25, primer_self_any=0.1, primer_self_end=0.1, primer_size_lt=0.5, primer_size_gt=0.1, primer_tm_lt=1.0, primer_tm_gt=1.0)
48+
>>> PrimerAndAmpliconWeights(product_size_lt=5)
49+
PrimerAndAmpliconWeights(product_size_lt=5, product_size_gt=1, product_tm_lt=0.0, product_tm_gt=0.0, primer_end_stability=0.25, primer_gc_lt=0.25, primer_gc_gt=0.25, primer_self_any=0.1, primer_self_end=0.1, primer_size_lt=0.5, primer_size_gt=0.1, primer_tm_lt=1.0, primer_tm_gt=1.0, probe_size_lt=0.25, probe_size_gt=0.25, probe_tm_lt=1.0, probe_tm_gt=1.0, probe_gc_lt=0.5, probe_gc_gt=0.5, probe_self_any=1.0, probe_self_end=1.0)
4950
""" # noqa: E501
5051

5152
product_size_lt: int = 1
@@ -80,3 +81,34 @@ def to_input_tags(self) -> dict[Primer3InputTag, Any]:
8081
Primer3InputTag.PRIMER_WT_TM_GT: self.primer_tm_gt,
8182
}
8283
return mapped_dict
84+
85+
86+
@dataclass(frozen=True, init=True, slots=True)
87+
class ProbeWeights:
88+
"""Holds the weights that Primer3 uses to adjust penalties
89+
that originate from the designed internal probe(s)."""
90+
91+
probe_size_lt: float = 0.25
92+
probe_size_gt: float = 0.25
93+
probe_tm_lt: float = 1.0
94+
probe_tm_gt: float = 1.0
95+
probe_gc_lt: float = 0.5
96+
probe_gc_gt: float = 0.5
97+
probe_self_any: float = 1.0
98+
probe_self_end: float = 1.0
99+
probe_hairpin_th: float = 1.0
100+
101+
def to_input_tags(self) -> dict[Primer3InputTag, Any]:
102+
"""Maps weights to Primer3InputTag to feed directly into Primer3."""
103+
mapped_dict = {
104+
Primer3InputTag.PRIMER_INTERNAL_WT_SIZE_LT: self.probe_size_lt,
105+
Primer3InputTag.PRIMER_INTERNAL_WT_SIZE_GT: self.probe_size_gt,
106+
Primer3InputTag.PRIMER_INTERNAL_WT_TM_LT: self.probe_tm_lt,
107+
Primer3InputTag.PRIMER_INTERNAL_WT_TM_GT: self.probe_tm_gt,
108+
Primer3InputTag.PRIMER_INTERNAL_WT_GC_PERCENT_LT: self.probe_gc_lt,
109+
Primer3InputTag.PRIMER_INTERNAL_WT_GC_PERCENT_GT: self.probe_gc_gt,
110+
Primer3InputTag.PRIMER_INTERNAL_WT_SELF_ANY: self.probe_self_any,
111+
Primer3InputTag.PRIMER_INTERNAL_WT_SELF_END: self.probe_self_end,
112+
Primer3InputTag.PRIMER_INTERNAL_WT_HAIRPIN_TH: self.probe_hairpin_th,
113+
}
114+
return mapped_dict

tests/primer3/test_primer3_weights.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,16 @@ def test_primer_weights_valid() -> None:
1919
assert test_dict[Primer3InputTag.PRIMER_WT_SIZE_GT] == 0.1
2020
assert test_dict[Primer3InputTag.PRIMER_WT_TM_LT] == 1.0
2121
assert test_dict[Primer3InputTag.PRIMER_WT_TM_GT] == 1.0
22-
assert len((test_dict.values())) == 13
22+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_SIZE_LT] == 0.25
23+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_SIZE_GT] == 0.25
24+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_TM_LT] == 1.0
25+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_TM_GT] == 1.0
26+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_GC_PERCENT_LT] == 0.5
27+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_GC_PERCENT_GT] == 0.5
28+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_SELF_ANY] == 1.0
29+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_SELF_END] == 1.0
30+
assert test_dict[Primer3InputTag.PRIMER_INTERNAL_WT_HAIRPIN_TH] == 1.0
31+
assert len((test_dict.values())) == 22
2332

2433

2534
def test_primer_weights_to_input_tags() -> None:

0 commit comments

Comments
 (0)