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

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from

Conversation

nh13
Copy link
Member

@nh13 nh13 commented Jan 15, 2025

  1. merges the Primer*Weights classes into their corresponding amplicon and probe parameter classes (reduces 4 classes to two). The attributes have the _wt suffix in their name. The primer3.primer3.primer3_weights module is gone.
  2. moves the Primer3Input functionality into Primer3 directly. primer3.primer3.primer3_input is now gone.
  3. Adds in WeightRange for the weights when a value is out of range (either less than, or greater than). Could use a better name?
  4. Primer3.design now explicitly takes in the design task and target, in addition to the params. It checks to make sure that the task (e.g. design left primers) matches the params (e.g. amplicons).
  5. Simplify Primer3Task (remove methods no longer used)

@nh13 nh13 requested a review from tfenne as a code owner January 15, 2025 23:10
prymer/primer3/primer3_parameters.py Outdated Show resolved Hide resolved
Comment on lines 138 to 141
product_size_lt: weight for products shorter than
`PrimerAndAmpliconParameters.amplicon_sizes.opt`
product_size_gt: weight for products longer than
`PrimerAndAmpliconParameters.amplicon_sizes.opt`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The frequency with which these come in pairs makes me think maybe we'd want to have a WeightPair (better name needed) class like MinOptMax so that you could do:

amplicon_size_wts = Weights(0.5, 1.5)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also two other thoughts:

  • I think the weights should match naming with above, so e.g. where we've said "amplicon_tms" we should use "amplicon" instead of "product" in the weights (or vice versa).
  • I think for clarity all weights should either have a wt_ prefix or _wt suffix; some do and some don't here.

Comment on lines 170 to 171
target: Span
task: Primer3TaskType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are in the super class, do you need to repeat them here?

Comment on lines 81 to 91
def as_amplicon_params(self) -> "AmpliconParameters":
"""Use this method when you want to treat these parameters as amplicon parameters."""
if isinstance(self, AmpliconParameters):
return typing.cast(AmpliconParameters, self)
raise Exception("The parameters are not amplicon parameters")

def as_probe_params(self) -> "ProbeParameters":
"""Use this method when you want to treat these parameters as probe parameters."""
if isinstance(self, ProbeParameters):
return typing.cast(ProbeParameters, self)
raise Exception("The parameters are not amplicon parameters")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels out of place to me - why not just check and cast where that's needed rather than having it in the superclass?

): # if the right primer has too many dinucleotide bases, fail it
dinuc_pair_failures.append(primer_pair.right_primer)
valid = False
if valid: # if neither failed, append the pair to a list of valid designs
valid_primer_pair_designs.append(primer_pair)
return valid_primer_pair_designs, dinuc_pair_failures

def design(self, design_input: Primer3Input) -> Primer3Result: # noqa: C901
def design(self, design_input: Primer3Parameters) -> Primer3Result: # noqa: C901
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would need to take:

amplicon_parameters: AmpliconParameters | None, probe_parameter: ProbeParameters | None

What you have works for the current set of use cases, but one primer3 task we haven't implemented yet supports designing primer pairs and probes concurrently, and then you need both sets of parameters.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave it for now, and we can decide later if we want to have two parameters to design, or to create a third parameters class:

class AmpliconAndProbeParameters(Primer3Parameters):
    amplicon_params: AmpliconParams
    probe_params: ProbeParams

@nh13 nh13 requested a review from tfenne January 16, 2025 02:18
Copy link

codecov bot commented Jan 16, 2025

Codecov Report

Attention: Patch coverage is 85.33333% with 11 lines in your changes missing coverage. Please review.

Project coverage is 96.83%. Comparing base (4348446) to head (116ea39).

Files with missing lines Patch % Lines
prymer/primer3/primer3.py 71.42% 3 Missing and 3 partials ⚠️
prymer/primer3/primer3_parameters.py 91.42% 2 Missing and 1 partial ⚠️
prymer/model.py 83.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #112      +/-   ##
==========================================
- Coverage   97.37%   96.83%   -0.54%     
==========================================
  Files          20       18       -2     
  Lines        1635     1579      -56     
  Branches      188      185       -3     
==========================================
- Hits         1592     1529      -63     
- Misses         24       28       +4     
- Partials       19       22       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@nh13 nh13 changed the title WIP: merge weights with params Merge Primer*Weights into Primer3Parameters, remove Primer3Input Jan 16, 2025
@@ -731,29 +754,3 @@ def _check_design_results(design_input: Primer3Input, design_results: dict[str,
count: int = int(maybe_count)

return count


def _has_acceptable_dinuc_run(design_input: Primer3Input, oligo_design: Oligo) -> bool:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I inlined this

Copy link
Member

@tfenne tfenne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nit-picking on naming but happy to merge once that's addressed!

@@ -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 AmpliconParameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to rename it from PrimerAndAmplicon I'd vote for PrimerParameters as it feels like the majority of the parameters are around primers. Also, can be used for designing just left primers (no amplicon), just right primers (no amplicon) or both (and therefore an amplicon).

@@ -83,6 +83,55 @@ def __str__(self) -> str:
return f"(min:{self.min}, opt:{self.opt}, max:{self.max})"


@dataclass(slots=True, frozen=True, init=True)
class WeightRange(Generic[Numeric]):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this, but a few thoughts:

  • It's not really a range, more of a pair. I.e. I can have a higher penalty weight for "primer tm too low" than "primer tm too high" - they're independent values. Perhaps it's just Weights?
  • I think in p3 weights are always floats because they are scaling factors, so i think we could un-generify it and juse always have the values be floats

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants