Multiple combinations of primers, but just a single template
Primers in their own mix, to increase scale without wasting more other reagents.
The reaction should be setup like this:
Make primer mix
Make master mix with everything but the primers.
Combine the two mixes.
This is not possible with the current Reactions implementation, because it has the baked-in assumption that there is a linear chain of mixes, where each adds to the previous. A more general implementation would allow the mixes to form a tree. Of course, this also complicates a lot of things. Below are some thoughts on how to make this work:
Big picture
Start with combos.
Create pipet graph:
each node: a reagent or a mix
each edge: the number of pipetting steps that would be needed to mix the attached nodes in the given order, with an attribute indicating if the
reagents would be in the same master mix or not.
Create mix tree:
minimum spanning arborescence (directed tree) of pipet graph.
merge reagents with "split=False" edges.
Post-processing
Pretty similar to before, I just need to translate "beginning of list" to "leaf nodes" and "end of list" to "root node". In some places I have to replace for-loops with breadth-first searches or similar. But in general, the logic is pretty transferable.
One thing I'm not sure about: which branch gets the missing reagents?
Details
_init_from_user
User provides set of mixes. These are incorporated into the mix graph using the same algorithm as if only combos were given.
How to do automixes?
The pipet graph needs a way to have multi-reagent nodes. Basically saying: these reagents go together, treat them as a unit.
Two kinds of multi-reagent node:
Mix: contains tree
AutoMix: contains set of reagents; tree needs to be calculated
These don't necessarily need to be the Mix/AutoMix classes that are exposed to the user, but I think they could be.
First pass: convert all AutoMix nodes into Mix nodes, since I'll need the tree to calculate weights for all the edges connecting to those nodes.
Scoring edges to Mix nodes will be more complicated than what I currently have, but still possible. Basically I'll need a function that can count up the pipetting steps for a whole tree, instead of just two reagents back to back. It'll be recursive but straight-forward.
In principle, automixes could contain other automixes, but I'll forbid this to start with.
For primer-mixes in PCR, I need to specify that the mix has to be a leaf. (The whole point is to create a mix that doesn't have any of the valuable reagents in it.) This can be done by not allowing incoming edges.
I think the graph needs to contain an empty Mix node, to represent the idea that multiple master mix can be combined in the final step. This node would not allow any outgoing edges, so it could only be the root.
_init_from_combos:
input graph same as before
use the implementation of Edmonds' algorithm provided by networkx
Interestingly, this algorithm is O(VE), which is way faster than the traveling salesman algorithm I'm currently using. That'll at least make me feel better.
return tree instead of list
_add_missing_reagents:
which branch to add these to?
whatever best preserves reagent order?
The branch would have to allow incoming edges. For PCR, the primer mix would not, and so the missing reagents would have to be added to a different branch.
I don't think there's a right answer, so I'll need a (i) a way for the user to specify and (ii) a consistent default, like "pick" the branch with the earliest reagent. Initially I can probably ignore this.
_add_solvent/_iter_volume_groups
volume tree instead of volume group
should be pretty similar, but with breadth-first search instead of simple iteration.
_merge_trivial_mixes:
look for leaves with 1 reagent; seems pretty easy
_set_mix_reactions:
iterate through all children, not just pairwise.
might be a little more complicated to put the upstream mixes into the reaction in the right order, but surely possible.
Test cases:
It won't be hard to use the test cases I've already written. Right now the mixes are specified as lists, but (i) that's usually fine and (ii) I can add an optional argument to specify tree edges in terms of list indices.
Conclusion
I think this is very doable. It'll be a big change, but the framework that I have will mostly stay intact.
Consider the following PCR reaction:
The reaction should be setup like this:
This is not possible with the current
Reactions
implementation, because it has the baked-in assumption that there is a linear chain of mixes, where each adds to the previous. A more general implementation would allow the mixes to form a tree. Of course, this also complicates a lot of things. Below are some thoughts on how to make this work:Big picture
Start with combos.
Create pipet graph:
Create mix tree:
Post-processing
Pretty similar to before, I just need to translate "beginning of list" to "leaf nodes" and "end of list" to "root node". In some places I have to replace for-loops with breadth-first searches or similar. But in general, the logic is pretty transferable.
One thing I'm not sure about: which branch gets the missing reagents?
Details
_init_from_user
User provides set of mixes. These are incorporated into the mix graph using the same algorithm as if only combos were given.
How to do automixes?
The pipet graph needs a way to have multi-reagent nodes. Basically saying: these reagents go together, treat them as a unit.
Two kinds of multi-reagent node:
These don't necessarily need to be the Mix/AutoMix classes that are exposed to the user, but I think they could be.
First pass: convert all AutoMix nodes into Mix nodes, since I'll need the tree to calculate weights for all the edges connecting to those nodes.
Scoring edges to Mix nodes will be more complicated than what I currently have, but still possible. Basically I'll need a function that can count up the pipetting steps for a whole tree, instead of just two reagents back to back. It'll be recursive but straight-forward.
In principle, automixes could contain other automixes, but I'll forbid this to start with.
For primer-mixes in PCR, I need to specify that the mix has to be a leaf. (The whole point is to create a mix that doesn't have any of the valuable reagents in it.) This can be done by not allowing incoming edges.
I think the graph needs to contain an empty Mix node, to represent the idea that multiple master mix can be combined in the final step. This node would not allow any outgoing edges, so it could only be the root.
_init_from_combos
:_add_missing_reagents
:_add_solvent
/_iter_volume_groups
_merge_trivial_mixes
:_set_mix_reactions
:Test cases:
Conclusion
I think this is very doable. It'll be a big change, but the framework that I have will mostly stay intact.