Skip to content

Commit

Permalink
add fragment iteration (#102)
Browse files Browse the repository at this point in the history
* Previously, RecursiveFragment added the flanks even to the first iteration of the DNA, which is undesirable. This fixes that.
  • Loading branch information
Koeng101 authored Oct 8, 2024
1 parent ebe8596 commit 01b1ed4
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Fixes RecursiveFragment to not add flanks to the initial input [#102](https://github.com/Koeng101/dnadesign/pull/102)
- Fixes add flank bug, releases new version of python lib [#101](https://github.com/Koeng101/dnadesign/pull/101)
- Adds feature for adding flanks to RecursiveFragment. [#100](https://github.com/Koeng101/dnadesign/pull/100)
- Adds cloning and recursion functions to python. [#96](https://github.com/Koeng101/dnadesign/pull/96)
Expand Down
18 changes: 14 additions & 4 deletions lib/synthesis/fragment/fragment.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,14 @@ type Assembly struct {
// The forwardFlank and reverseFlank are for preparing the sequences for
// recursive assembly. Generally, this involves appending a certain sequence
// to each oligo, and also to the edges of each subassembly. Do not add these
// to the maxCodingSizeOligo: that is done within the function.
// to the maxCodingSizeOligo: that is done within the function. The flanks are
// NOT added to the first iteration of the sequence: if they are desired there,
// add them manually.
func RecursiveFragment(sequence string, maxCodingSizeOligo int, assemblyPattern []int, excludeOverhangs []string, includeOverhangs []string, forwardFlank string, reverseFlank string) (Assembly, error) {
return recursiveFragmentIteration(sequence, maxCodingSizeOligo, assemblyPattern, excludeOverhangs, includeOverhangs, forwardFlank, reverseFlank, 0)
}

func recursiveFragmentIteration(sequence string, maxCodingSizeOligo int, assemblyPattern []int, excludeOverhangs []string, includeOverhangs []string, forwardFlank string, reverseFlank string, iteration int) (Assembly, error) {
/*
Ok, so this is a note for you hackers out there: this algorithm can be
greatly improved. The optimal way to do this would be to do a continuous
Expand Down Expand Up @@ -286,8 +292,12 @@ func RecursiveFragment(sequence string, maxCodingSizeOligo int, assemblyPattern
}
sizes[i] = sizes[i-1]*assemblyPattern[i] - smallestMinFragmentSizeSubtraction // subtract approx 60bp to give room for finding overhangs
}
targetSequence := sequence
if iteration != 0 {
targetSequence = forwardFlank + sequence + reverseFlank
}
if sequenceLen <= sizes[0] {
fragments, efficiency, err := FragmentWithOverhangs(forwardFlank+sequence+reverseFlank, maxCodingSizeOligo-60, maxCodingSizeOligo, excludeOverhangs, includeOverhangs)
fragments, efficiency, err := FragmentWithOverhangs(targetSequence, maxCodingSizeOligo-60, maxCodingSizeOligo, excludeOverhangs, includeOverhangs)
if err != nil {
return assembly, err
}
Expand All @@ -296,14 +306,14 @@ func RecursiveFragment(sequence string, maxCodingSizeOligo int, assemblyPattern
// After the smallest possible block, begin iterating for each size.
for i, size := range sizes[1:] {
if sequenceLen <= size {
fragments, efficiency, err := FragmentWithOverhangs(forwardFlank+sequence+reverseFlank, sizes[i]-minFragmentSizeSubtraction, sizes[i], excludeOverhangs, includeOverhangs)
fragments, efficiency, err := FragmentWithOverhangs(targetSequence, sizes[i]-minFragmentSizeSubtraction, sizes[i], excludeOverhangs, includeOverhangs)
if err != nil {
return assembly, err
}
// Now we need to get the derived fragments from this overall construction
var subAssemblies []Assembly
for _, fragment := range fragments {
subAssembly, err := RecursiveFragment(fragment, maxCodingSizeOligo, assemblyPattern, excludeOverhangs, includeOverhangs, forwardFlank, reverseFlank)
subAssembly, err := recursiveFragmentIteration(fragment, maxCodingSizeOligo, assemblyPattern, excludeOverhangs, includeOverhangs, forwardFlank, reverseFlank, iteration+1)
if err != nil {
return subAssembly, err
}
Expand Down
24 changes: 24 additions & 0 deletions lib/synthesis/fragment/fragment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fragment_test

import (
_ "embed"
"reflect"
"strings"
"testing"

Expand Down Expand Up @@ -117,3 +118,26 @@ func TestRecursiveFragment(t *testing.T) {
t.Errorf("Failed to RecursiveFragment blue1. Got error: %s", err)
}
}

func TestRecursiveFragmentPy(t *testing.T) {
// These are the 46 possible overhangs I personally use, plus the two identity overhangs CGAG+GTCT
defaultOverhangs := []string{"GGGG", "AAAA", "AACT", "AATG", "ATCC", "CGCT", "TTCT", "AAGC", "ATAG", "ATTA", "ATGT", "ACTC", "ACGA", "TATC", "TAGG", "TACA", "TTAC", "TTGA", "TGGA", "GAAG", "GACC", "GCCG", "TCTG", "GTTG", "GTGC", "TGCC", "CTGG", "TAAA", "TGAG", "AAGA", "AGGT", "TTCG", "ACTA", "TTAG", "TCTC", "TCGG", "ATAA", "ATCA", "TTGC", "CACG", "AATA", "ACAA", "ATGG", "TATG", "AAAT", "TCAC"}
excludeOverhangs := []string{"CGAG", "GTCT"} // These are the recursive BsaI definitions, and must be excluded from all builds.
gene := "ATGACCATGATTACGCCAAGCTTGCATGCCTGCAGGTCGACTCTAGAGGATCCCCGGGTACCGAGCTCGAATTCACTGGCCGTCGTTTTACAACGTCGTGACTGGGAAAACCCTGGCGTTACCCAACTTAATCGCCTTGCAGCACATCCCCCTTTCGCCAGCTGGCGTAATAGCGAAGAGGCCCGCACCGATCGCCCTTCCCAACAGTTGCGCAGCCTGAATGGCGAATGGCGCCTGATGCGGTATTTTCTCCTTACGCATCTGTGCGGTATTTCACACCGCATATGGTGCACTCTCAGTACAATCTGCTCTGATGCCGCATAG"
maxOligoLen := 174 // for Agilent oligo pools
assemblyPattern := []int{5, 4, 4, 5} // seems reasonable enough
result, err := fragment.RecursiveFragment(gene, maxOligoLen, assemblyPattern, excludeOverhangs, defaultOverhangs, "GTCTCT", "CGAG")
if err != nil {
t.Errorf("Failed to RecursiveFragment blue1. Got error: %s", err)
}

// Add more specific assertions based on the expected structure of the result
expectedFragments := []string{
"ATGACCATGATTACGCCAAGCTTGCATGCCTGCAGGTCGACTCTAGAGGATCCCCGGGTACCGAGCTCGAATTCACTGGCCGTCGTTTTACAACGTCGTGACTGGGAAAACCCTGGCGTTACCCAACTTAATCGCCTTGCAGCACATCCCCCTTTCGCCAG",
"CCAGCTGGCGTAATAGCGAAGAGGCCCGCACCGATCGCCCTTCCCAACAGTTGCGCAGCCTGAATGGCGAATGGCGCCTGATGCGGTATTTTCTCCTTACGCATCTGTGCGGTATTTCACACCGCATATGGTGCACTCTCAGTACAATCTGCTCTGATGCCGCATAG",
}

if !reflect.DeepEqual(result.Fragments, expectedFragments) {
t.Errorf("Unexpected fragments. Got %v, want %v", result.Fragments, expectedFragments)
}
}
2 changes: 1 addition & 1 deletion py/tests/test_fragment.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ def test_recursive_fragment():
result = recursive_fragment(gene, max_oligo_len, assembly_pattern, exclude_overhangs, default_overhangs, "GTCTCT", "CGAG")
assert result is not None, "RecursiveFragment failed"
# Add more specific assertions based on the expected structure of the result
assert result.fragments == ['GTCTCTATGACCATGATTACGCCAAGCTTGCATGCCTGCAGGTCGACTCTAGAGGATCCCCGGGTACCGAGCTCGAATTCACTGGCCGTCGTTTTACAACGTCGTGACTGGGAAAACCCTGGCGTTACCCAACTTAATCGCCTTGCAGCACATCCCCCTTTCGCCAG', 'CCAGCTGGCGTAATAGCGAAGAGGCCCGCACCGATCGCCCTTCCCAACAGTTGCGCAGCCTGAATGGCGAATGGCGCCTGATGCGGTATTTTCTCCTTACGCATCTGTGCGGTATTTCACACCGCATATGGTGCACTCTCAGTACAATCTGCTCTGATGCCGCATAGCGAG']
assert result.fragments == ['ATGACCATGATTACGCCAAGCTTGCATGCCTGCAGGTCGACTCTAGAGGATCCCCGGGTACCGAGCTCGAATTCACTGGCCGTCGTTTTACAACGTCGTGACTGGGAAAACCCTGGCGTTACCCAACTTAATCGCCTTGCAGCACATCCCCCTTTCGCCAG', 'CCAGCTGGCGTAATAGCGAAGAGGCCCGCACCGATCGCCCTTCCCAACAGTTGCGCAGCCTGAATGGCGAATGGCGCCTGATGCGGTATTTTCTCCTTACGCATCTGTGCGGTATTTCACACCGCATATGGTGCACTCTCAGTACAATCTGCTCTGATGCCGCATAG']

0 comments on commit 01b1ed4

Please sign in to comment.