Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Primer*Weights into Primer3Parameters, remove Primer3Input #112

Merged
merged 7 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions prymer/api/picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from prymer.model import Oligo
from prymer.model import PrimerPair
from prymer.model import Span
from prymer.primer3 import PrimerAndAmpliconWeights
from prymer.primer3 import PrimerParameters


def score(
Expand All @@ -40,7 +40,7 @@ def score(
amplicon_tm: float,
amplicon_sizes: MinOptMax[int],
amplicon_tms: MinOptMax[float],
weights: PrimerAndAmpliconWeights,
params: PrimerParameters,
) -> float:
"""Score the amplicon in a manner similar to Primer3

Expand All @@ -59,7 +59,7 @@ def score(
amplicon_tm: the melting temperature of the amplicon
amplicon_sizes: minimum, optimal, and maximum amplicon sizes (lengths)
amplicon_tms: minimum, optimal, and maximum amplicon Tms
weights: the set of penalty weights
params: the set of primer3 parameters

Returns:
the penalty for the whole amplicon.
Expand All @@ -73,9 +73,9 @@ def score(
if amplicon_sizes.opt == 0:
size_penalty = 0.0
elif amplicon.length > amplicon_sizes.opt:
size_penalty = (amplicon.length - amplicon_sizes.opt) * weights.product_size_gt
size_penalty = (amplicon.length - amplicon_sizes.opt) * params.amplicon_size_wt.gt
else:
size_penalty = (amplicon_sizes.opt - amplicon.length) * weights.product_size_lt
size_penalty = (amplicon_sizes.opt - amplicon.length) * params.amplicon_size_wt.lt

# The penalty for the amplicon melting temperature.
# The difference in melting temperature between the calculated and optimal is weighted by the
Expand All @@ -84,9 +84,9 @@ def score(
if amplicon_tms.opt == 0.0:
tm_penalty = 0.0
elif amplicon_tm > amplicon_tms.opt:
tm_penalty = (amplicon_tm - amplicon_tms.opt) * weights.product_tm_gt
tm_penalty = (amplicon_tm - amplicon_tms.opt) * params.amplicon_tm_wt.gt
else:
tm_penalty = (amplicon_tms.opt - amplicon_tm) * weights.product_tm_lt
tm_penalty = (amplicon_tms.opt - amplicon_tm) * params.amplicon_tm_wt.lt

# Put it all together
return left_primer.penalty + right_primer.penalty + size_penalty + tm_penalty
Expand All @@ -99,7 +99,7 @@ def build_primer_pairs( # noqa: C901
amplicon_sizes: MinOptMax[int],
amplicon_tms: MinOptMax[float],
max_heterodimer_tm: Optional[float],
weights: PrimerAndAmpliconWeights,
params: PrimerParameters,
fasta_path: Path,
thermo: Optional[Thermo] = None,
) -> Iterator[PrimerPair]:
Expand All @@ -116,11 +116,11 @@ def build_primer_pairs( # noqa: C901
amplicon_sizes: minimum, optimal, and maximum amplicon sizes (lengths)
amplicon_tms: minimum, optimal, and maximum amplicon Tms
max_heterodimer_tm: if supplied, heterodimer Tms will be calculated for primer pairs,
and those exceeding the maximum Tm will be discarded
weights: the set of penalty weights
and those exceeding the maximum Tm will be discarded
params: the set of penalty params
fasta_path: the path to the FASTA file from which the amplicon sequence will be retrieved.
thermo: a [`Thermo`][prymer.Thermo] instance for performing thermodynamic calculations
including amplicon tm; if not provided, a default Thermo instance will be created
including amplicon tm; if not provided, a default Thermo instance will be created

Returns:
An iterator over all the valid primer pairs, sorted by primer pair penalty.
Expand Down Expand Up @@ -197,7 +197,7 @@ def build_primer_pairs( # noqa: C901
amplicon_tm=amp_tm,
amplicon_sizes=amplicon_sizes,
amplicon_tms=amplicon_tms,
weights=weights,
params=params,
)

pairings.append((i, j, penalty, amp_tm))
Expand Down
42 changes: 42 additions & 0 deletions prymer/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,48 @@ def __str__(self) -> str:
return f"(min:{self.min}, opt:{self.opt}, max:{self.max})"


@dataclass(slots=True, frozen=True, init=True)
class Weights:
"""Stores a pair of penalty weights.

Weights are used when comparing a primer or probe property (e.g. primer length) to the optimal
parameterized value. If the value is less than, then the `lt` weight is used. If the value is
greater than, then the `gt` weight is used.

The two values can be either int or float values but must be of the same type within one
Range object (for example, `lt` cannot be a float while `gt` is an int).

Examples of interacting with the `Range` class

```python
>>> range = Weights(lt=1.0, gt=4.0)
>>> print(range)
(lt:1.0, gt:4.0)
>>> list(range)
[1.0, 4.0]

```

Attributes:
lt: the minimum value (inclusive)
gt: the maximum value (inclusive)

Raises:
ValueError: if lt and gt are not the same type
"""

lt: float
gt: float

def __iter__(self) -> Iterator[float]:
"""Returns an iterator of min and max"""
return iter([self.lt, self.gt])

def __str__(self) -> str:
"""Returns a string representation of min and max"""
return f"(lt:{self.lt}, gt:{self.gt})"


@unique
class Strand(StrEnum):
"""Represents the strand of a span to the genome."""
Expand Down
12 changes: 4 additions & 8 deletions prymer/primer3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,26 @@
from prymer.primer3.primer3 import Primer3Failure
from prymer.primer3.primer3 import Primer3Result
from prymer.primer3.primer3_failure_reason import Primer3FailureReason
from prymer.primer3.primer3_input import Primer3Input
from prymer.primer3.primer3_input_tag import Primer3InputTag
from prymer.primer3.primer3_parameters import PrimerAndAmpliconParameters
from prymer.primer3.primer3_parameters import Primer3Parameters
from prymer.primer3.primer3_parameters import PrimerParameters
from prymer.primer3.primer3_parameters import ProbeParameters
from prymer.primer3.primer3_task import DesignLeftPrimersTask
from prymer.primer3.primer3_task import DesignPrimerPairsTask
from prymer.primer3.primer3_task import DesignRightPrimersTask
from prymer.primer3.primer3_task import PickHybProbeOnly
from prymer.primer3.primer3_weights import PrimerAndAmpliconWeights
from prymer.primer3.primer3_weights import ProbeWeights

__all__ = [
"Primer3",
"Primer3Result",
"Primer3Failure",
"Primer3FailureReason",
"Primer3Input",
"Primer3InputTag",
"DesignLeftPrimersTask",
"DesignPrimerPairsTask",
"DesignRightPrimersTask",
"PickHybProbeOnly",
"PrimerAndAmpliconParameters",
"Primer3Parameters",
"PrimerParameters",
"ProbeParameters",
"ProbeWeights",
"PrimerAndAmpliconWeights",
]
Loading
Loading