From 4433b1b9fb1d1221fc3a00f03a67454a05730380 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Mon, 30 Oct 2017 15:24:27 -0700 Subject: [PATCH 1/9] CHK: added test for primer dimer problem that fails --- src/AmyrisBio/primercore2.fs | 16 ++++++++++++ tests/AmyrisBio.Tests/testPrimer.fs | 38 ++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/AmyrisBio/primercore2.fs b/src/AmyrisBio/primercore2.fs index a361954..5fe8c5f 100644 --- a/src/AmyrisBio/primercore2.fs +++ b/src/AmyrisBio/primercore2.fs @@ -237,6 +237,22 @@ module primercore = g3Final > -9.0 + // find longest tail to tail pair that is self complementary + // 5' tgtCTTTAAAG 3' + // 3' GAAATTTCtgtg 5' + let longestTailTailOverlap (s1:char array) (s2:char array) = + let suffix (d:char array) l = d.[d.Length-l..] + + let maxOverlap = min s1.Length s2.Length + seq { + for i in 0..maxOverlap do + let suffix1 = suffix s1 i + let suffix2 = suffix s2 i + if suffix1 = Amyris.Bio.biolib.revComp suffix2 then + yield i + } |> Seq.fold (max) 0 + + /// Test if an oligo is self complementary let selfComp (oligo:char array) length = let rec checkOne l r = diff --git a/tests/AmyrisBio.Tests/testPrimer.fs b/tests/AmyrisBio.Tests/testPrimer.fs index 67c20ad..a8c5ab7 100644 --- a/tests/AmyrisBio.Tests/testPrimer.fs +++ b/tests/AmyrisBio.Tests/testPrimer.fs @@ -3,7 +3,6 @@ open Amyris.Bio.primercore open NUnit.Framework - [] type TestPrimer() = class do @@ -67,4 +66,41 @@ type TestPrimer() = class let p = oligoDesignWithCompromise false pen task Assert.IsTrue(p.IsSome) + + [] + /// Test that we don't form a primer dimer under tempting circumstances + member __.TestPrimerDimer() = + let template ="CGGTTGGGCTTAACTTTAAAGAAAAAAGTTGAGATTAGATTTATTGTGTT" + let pen= { + tmPenalty = 1.0; + tmMaxDifference = 5.0 ; + positionPenalty = 5.0 ; + lengthPenalty = 3.0 ; + polyLengthThreshold = 4; + polyPenalty = 10. ; + threePrimeUnstablePenalty = 5.0 ; + ATPenalty=3.0 ; + targetLength=20 ; + maxLength = 60 ; + minLength = 20 ; + monovalentConc = mM2M 50.0; + primerConc = uM2M 0.25 ; + divalentConc = mM2M 1.5 ; + templateConc = uM2M 0.01 ; + dNTPConc = uM2M 0.0 ;} + + let task = { tag = "PR"; + temp = (template.ToCharArray()) + align = LEFT; + strand = TOP; + offset = 0; + targetTemp = 60.0; + sequencePenalties = None} + + match oligoDesign false pen task with + | None -> Assert.Fail "TestPrimerDimer failed to make a primer" + | Some design -> + let tail = Amyris.Bio.primercore.longestTailTailOverlap design.oligo design.oligo + Assert.IsTrue(tail<4) + end From 67f80923ef7c8d4852bd79b8a075d347940eb931 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Mon, 30 Oct 2017 17:35:52 -0700 Subject: [PATCH 2/9] ENH: primercore checks for primer dimer ends when designing left anchored primers --- src/AmyrisBio/primercore2.fs | 54 ++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/AmyrisBio/primercore2.fs b/src/AmyrisBio/primercore2.fs index 5fe8c5f..28c9605 100644 --- a/src/AmyrisBio/primercore2.fs +++ b/src/AmyrisBio/primercore2.fs @@ -379,10 +379,54 @@ module primercore = let _,_,x = _temp p oligo N x + /// disrupt self primer dimers + /// Tweak the stopping point of a primer if it would help avoid a + /// situation where last N bases of the primer are self complementary and + /// can lead to formation of a primer dimer + /// 5'-------CTTTAAAG-3' + /// 3'GAAATTTC--------5' + let disruptPrimerDimers (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) f t offset = + let debug = true + if debug then + printfn "disruptPrimerDimers: checking for problems" + let comp (b:char) = match b with | 'G' -> 'C' | 'C' -> 'G' | 'A' -> 'T' | 'T' -> 'A' | x -> x + + /// check longest self complementary tail for + /// oligo defined by f'->t' inclusive + let localLongestTailTailOverlap f' t' = + let l = t'-f'+1 + seq { + for i in 1..l-1 do + if seq { 0..i-1 } |> Seq.forall (fun j -> s.[t'-i+j] = comp (s.[t'-j])) then + yield (i+1) + } |> Seq.fold (max) 0 + + let initialTail = localLongestTailTailOverlap f t + if debug then printfn "disruptPrimerDimers: initialTail of length %d for %s" initialTail (arr2seq s.[f..t]) + // no-op + if initialTail < 3 then + { tag = ""; oligo = s.[f..t] ; temp = existingTemp ; offset = f+offset } + else + printfn "disruptPrimerDimers: found problematic initialTail of length %d initial f=%d t=%d" initialTail f t + seq { for i in [1 ; -1 ; 2 ; -2 ; 3 ; -3 ; 4 ; -3] do + let t' = t+i + let len' = t'-f+1 + if debug then printfn "disruptPrimerDimers: considering f=%d t=%d len=%d min=%d max=%d templateLen=%d" f t' len' p.minLength p.maxLength s.Length + if len' >= p.minLength && len' <= p.maxLength && t' < s.Length && t' > f then + let tail' = localLongestTailTailOverlap f t' + if debug then printfn "disruptPrimerDimers: considering tail of length %d for %d" tail' t' + yield tail',t' + } + |> Seq.fold (min) (initialTail,t) + |> fun (finalTail,finalT) -> + printfn "disruptPrimerDimers: finalTail of length %d deltaT=%d f=%d finalT=%d" finalTail (finalT-t) f finalT + let finalTemp = temp p (s.[f..finalT]) (finalT-f+1) + { tag = ""; oligo = s.[f..finalT] ; temp = finalTemp; offset = f+offset } + + /// Cut out the region from fr -> to and extend // Given oligo array from to gcInit offset , return oligo, temp, offset - let cutToGC (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) f t _ (*startingGC*) offset = - //let startingN = t-f + 1 + let cutToGC (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) f t _startingGC offset = let rec findGCFwd t' (*gc' *) = if t' < 0 || t' >= s.Length then failwithf "ERROR: primercord findGC array bounds exception t'=%d\n" t' @@ -477,7 +521,11 @@ module primercore = if debug then printfn "designLeft, stopping at nextBase-1=%d thisTemp=%f" (nextBase-1) (thisTemp/1.0) Some(cutToGC debug lastTemp p s 0 (nextBase-1) gcCount offset) - designLeftInternal sInitUpper nextBase gcCount (0.0) + match designLeftInternal sInitUpper nextBase gcCount (0.0) with + | None -> None + | Some prePDCheck -> + // possible final tweaks if we have made a primer-dimer with the tail + Some ( disruptPrimerDimers debug prePDCheck.temp p sInitUpper 0 (prePDCheck.oligo.Length-1) offset) /// Check for the presence of mono- or dinucleotide repeats longer than N let hasPolyrun n (s:char[]) = From fa0920d341dc3a390101fbcfa915c209b26a35e5 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Thu, 2 Nov 2017 15:10:22 -0700 Subject: [PATCH 3/9] BUG,TST: added more tests for primer dimer and gc clamp checking. GC clamping could produce suboptimal result --- src/AmyrisBio/primercore2.fs | 102 ++++++++++++------ tests/AmyrisBio.Tests/testPrimer.fs | 157 +++++++++++++++++++++++++--- 2 files changed, 214 insertions(+), 45 deletions(-) diff --git a/src/AmyrisBio/primercore2.fs b/src/AmyrisBio/primercore2.fs index 28c9605..ff8ac8d 100644 --- a/src/AmyrisBio/primercore2.fs +++ b/src/AmyrisBio/primercore2.fs @@ -386,7 +386,6 @@ module primercore = /// 5'-------CTTTAAAG-3' /// 3'GAAATTTC--------5' let disruptPrimerDimers (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) f t offset = - let debug = true if debug then printfn "disruptPrimerDimers: checking for problems" let comp (b:char) = match b with | 'G' -> 'C' | 'C' -> 'G' | 'A' -> 'T' | 'T' -> 'A' | x -> x @@ -396,10 +395,11 @@ module primercore = let localLongestTailTailOverlap f' t' = let l = t'-f'+1 seq { - for i in 1..l-1 do + for i in l-1..-1..1 do if seq { 0..i-1 } |> Seq.forall (fun j -> s.[t'-i+j] = comp (s.[t'-j])) then yield (i+1) - } |> Seq.fold (max) 0 + } |> Seq.tryFind(fun x -> x<> 0) + |> function | None -> 0 | Some v -> v let initialTail = localLongestTailTailOverlap f t if debug then printfn "disruptPrimerDimers: initialTail of length %d for %s" initialTail (arr2seq s.[f..t]) @@ -408,7 +408,7 @@ module primercore = { tag = ""; oligo = s.[f..t] ; temp = existingTemp ; offset = f+offset } else printfn "disruptPrimerDimers: found problematic initialTail of length %d initial f=%d t=%d" initialTail f t - seq { for i in [1 ; -1 ; 2 ; -2 ; 3 ; -3 ; 4 ; -3] do + seq { for i in [1 ; -1 ; 2 ; -2 ; 3 ; -3 ; 4 ; -4; 5; -5 ; 6 ; -6] do let t' = t+i let len' = t'-f+1 if debug then printfn "disruptPrimerDimers: considering f=%d t=%d len=%d min=%d max=%d templateLen=%d" f t' len' p.minLength p.maxLength s.Length @@ -425,56 +425,94 @@ module primercore = /// Cut out the region from fr -> to and extend - // Given oligo array from to gcInit offset , return oligo, temp, offset + /// Given oligo array from to gcInit offset , return oligo, temp, offset let cutToGC (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) f t _startingGC offset = + if debug then + printfn "cutToGC: starting design f=%d t=%d oligo=%s" + f + t + (arr2seq s.[f..t]) let rec findGCFwd t' (*gc' *) = if t' < 0 || t' >= s.Length then failwithf "ERROR: primercord findGC array bounds exception t'=%d\n" t' match s.[t'] with |'G' | 'g' | 'C' | 'c' when threePrimeStable false (s.[..t'])-> - (t', temp p (s.[f..t']) (t'-f+1)) // end on G/C + Some (t', temp p (s.[f..t']) (t'-f+1)) // end on G/C | _ when t' < (s.Length-1) && t'-f+1 < p.maxLength -> findGCFwd (t'+1) - | _ -> (t, (temp p s.[f..t] (t-f+1))) // fall back on original oligo if we run out of S without finding G + | _ -> None let rec findGCRev t' = if t' < 0 || t' >= s.Length then failwithf "ERROR: primercord findGC array bounds exception t'=%d\n" t' match s.[t'] with |'G' | 'g' | 'C' | 'c' when threePrimeStable false (s.[..t']) -> - (t', temp p (s.[f..t']) (t'-f+1)) // end on G/C + Some (t', temp p (s.[f..t']) (t'-f+1)) | _ when t' > 1 && t'-f+1 > p.minLength -> findGCRev (t'-1) - | _ -> (t, (temp p s.[f..t] (t-f+1))) // fall back on original oligo if we run out of S without finding G + | _ -> None if p.ATPenalty < 0.0001 then // No GC optimization - if debug then printfn "cutToGC: no atPenalty, done" + if debug then printfn "cutToGC: no atPenalty, done len=%d" (t-f+1) { tag = ""; oligo = s.[f..t] ; temp = temp p (s.[f..t]) (t-f+1); offset = f+offset } else // Is there an alternative starting point that would end on a G or C that's not too far away? - let altTFwd,altTempFwd = findGCFwd t - let altTRev,altTempRev = findGCRev t - - assert(altTempFwd > 10.0) - assert(altTempRev > 10.0) - if debug then - printfn "cutToGC: altTempFwd=%A altTFwd=%d altTempRev=%A altRFwd=%d" - altTempFwd altTFwd altTempRev altTRev - - // Choose the direction to a GC that was least perturbative - let altT,altTemp = - if abs (altTempFwd-existingTemp) < abs(altTempRev-existingTemp) then - altTFwd,altTempFwd - else altTRev,altTempRev - - if abs (altTemp-existingTemp) <= p.ATPenalty then - if debug then printfn "cutToGC: using altGC option temp=%A f=%d t=%d" altTemp f altT + let altBest = + match findGCFwd t,findGCRev t with + | None,None -> None // no better option + | Some (altPos,altTemp),None -> + if debug then + printfn "cutToGC: altTempFwd=%A altTFwd=%d altRev=None" + altTemp altPos + Some (altPos,altTemp) // only one worked + | None, Some (altPos,altTemp) -> + if debug then + printfn "cutToGC: altTempRev=%A altTRev=%d altFwd=None" + altTemp + altPos + Some (altPos,altTemp) // only one worked + | Some (altPosFwd,altTempFwd),Some(altPosRev,altTempRev) -> + if debug then + printfn "cutToGC: altTempFwd=%A alTFwd=%d altTempRev=%A altTRev=%d" + altTempFwd altPosFwd altTempRev altPosRev + Some( + if abs (altTempFwd-existingTemp) < abs(altTempRev-existingTemp) then + altPosFwd,altTempFwd + else altPosRev,altTempRev + ) + + match altBest with + | Some (_,temperature) -> assert (temperature > 10.0) // ensure selected temp isn't crazy + | None -> () + + match altBest with + | None -> + if debug then + printfn "cutToGC: no better altGC version, going with original f=%d t=%d oligo=%s" + f + t + (arr2seq s.[f..t]) + { tag = ""; oligo = s.[f..t] ; temp = temp p (s.[f..t]) (t-f+1); offset = f+offset } - { tag = ""; oligo = s.[f..altT] ; temp = altTemp ; offset = f+offset} - else - if debug then printfn "cutToGC: ignore altGC option temp=%A f=%d t=%d" temp f t + | Some(_,altTemp) when (altTemp-existingTemp) > p.ATPenalty -> + // stick with original design, compromise too big + if debug then + printfn "cutToGC: ignore altGC option (compromise not worth it) temp=%A f=%d t=%d oligo=%s" + temp + f + t + (arr2seq s.[f..t]) { tag = ""; oligo = s.[f..t] ; temp = temp p (s.[f..t]) (t-f+1); offset = f+offset } + | Some(altPos,altTemp) -> + if debug then + printfn "cutToGC: using altGC option temp=%A f=%d t=%d oligo=%s" + altTemp + f + altPos + (arr2seq s.[f..altPos]) + + { tag = ""; oligo = s.[f..altPos] ; temp = altTemp ; offset = f+offset} let upper (s:char array) = [| for x in s -> @@ -719,7 +757,9 @@ module primercore = printf "No oligo in required length, so make max allowable max=%d templateLen=%d\n" pen.maxLength s'.Length let oligo = s'.[..(min s'.Length pen.maxLength)-1] // longest allowed - Some( { tag=o.tag; oligo = oligo ; temp = temp pen oligo pen.maxLength; offset = o.offset } ) // calc temperature and offset is 0 plus their supplied offset + let oligoTemp = temp pen oligo oligo.Length + let oligo' = disruptPrimerDimers debug oligoTemp pen s' 0 (oligo.Length-1) o.offset + Some( { oligo' with tag=o.tag ; temp = temp pen oligo pen.maxLength; offset = o.offset } ) // calc temperature and offset is 0 plus their supplied offset | x -> x // | RIGHT -> failwith "should not get right here" diff --git a/tests/AmyrisBio.Tests/testPrimer.fs b/tests/AmyrisBio.Tests/testPrimer.fs index a8c5ab7..f416fce 100644 --- a/tests/AmyrisBio.Tests/testPrimer.fs +++ b/tests/AmyrisBio.Tests/testPrimer.fs @@ -68,9 +68,131 @@ type TestPrimer() = class Assert.IsTrue(p.IsSome) [] + /// Test that GC clamp finding is behaving sensibly by tempting designer with long AT rich regions + member __.TestGCClamp() = + let templates = + [ + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATA" ; + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "ATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "CTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "ATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "GATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "ATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "ATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + ] + let pen= { + tmPenalty = 1.0; + tmMaxDifference = 5.0 ; + positionPenalty = 5.0 ; + lengthPenalty = 3.0 ; + polyLengthThreshold = 4; + polyPenalty = 10. ; + threePrimeUnstablePenalty = 5.0 ; + ATPenalty=3.0 ; + targetLength=20 ; + maxLength = 80 ; + minLength = 20 ; + monovalentConc = mM2M 50.0; + primerConc = uM2M 0.25 ; + divalentConc = mM2M 1.5 ; + templateConc = uM2M 0.01 ; + dNTPConc = uM2M 0.0 ;} + + for template in templates do + let task = { tag = "PR"; + temp = (template.ToCharArray()) + align = LEFT; + strand = TOP; + offset = 0; + targetTemp = 60.0; + sequencePenalties = None} + + match oligoDesign false pen task with + | None -> + Assert.Fail (sprintf "TestGClamp failed to make a primer for %s" template) + | Some design -> + match design.oligo.[design.oligo.Length-1] with + | 'G' | 'C' -> () // good + | x -> Assert.Fail( // bad + sprintf "TestGCClamp yielded oligo ending in '%c' not G or C for template %s oligo=%s length=%d temp=%A" + x + template + (Amyris.Bio.utils.arr2seq design.oligo) + design.oligo.Length + design.temp + ) + [] /// Test that we don't form a primer dimer under tempting circumstances member __.TestPrimerDimer() = - let template ="CGGTTGGGCTTAACTTTAAAGAAAAAAGTTGAGATTAGATTTATTGTGTT" + let verbose = false + let templates = + [ + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATA" ; + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATTATATATATATATATATATAATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATTATATATATATATATAATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "ATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "CTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "ATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "GATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "ATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "ATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "CGGTTGGGCTTAACTTTAAAGAAAAAAGTTGAGATTAGATTTATTGTGTT" + ] let pen= { tmPenalty = 1.0; tmMaxDifference = 5.0 ; @@ -81,7 +203,7 @@ type TestPrimer() = class threePrimeUnstablePenalty = 5.0 ; ATPenalty=3.0 ; targetLength=20 ; - maxLength = 60 ; + maxLength = 80 ; minLength = 20 ; monovalentConc = mM2M 50.0; primerConc = uM2M 0.25 ; @@ -89,18 +211,25 @@ type TestPrimer() = class templateConc = uM2M 0.01 ; dNTPConc = uM2M 0.0 ;} - let task = { tag = "PR"; - temp = (template.ToCharArray()) - align = LEFT; - strand = TOP; - offset = 0; - targetTemp = 60.0; - sequencePenalties = None} + for template in templates do + if verbose then printfn "Testing %s for primer dimer avoidance" template + let task = { tag = "PR"; + temp = (template.ToCharArray()) + align = LEFT; + strand = TOP; + offset = 0; + targetTemp = 60.0; + sequencePenalties = None} - match oligoDesign false pen task with - | None -> Assert.Fail "TestPrimerDimer failed to make a primer" - | Some design -> - let tail = Amyris.Bio.primercore.longestTailTailOverlap design.oligo design.oligo - Assert.IsTrue(tail<4) + if verbose then printfn "designing oligo for %s " template + match oligoDesign false pen task with + | None -> + if verbose then printfn "failed design for %s " template + Assert.Fail "TestPrimerDimer failed to make a primer" + | Some design -> + if verbose then printfn "succeeded design %A %s for %s " design.temp (Amyris.Bio.utils.arr2seq design.oligo) template + let tail = Amyris.Bio.primercore.longestTailTailOverlap design.oligo design.oligo + if verbose then printfn "tail is %d for %s" tail template + Assert.IsTrue(tail<4) end From 0e2ecd92f2f8c2e1560ebcfc5cfe5f4cccce13f1 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Thu, 2 Nov 2017 15:34:34 -0700 Subject: [PATCH 4/9] ENH: added primerdimer check to the floating CENTER primer design --- src/AmyrisBio/primercore2.fs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/AmyrisBio/primercore2.fs b/src/AmyrisBio/primercore2.fs index ff8ac8d..56588d3 100644 --- a/src/AmyrisBio/primercore2.fs +++ b/src/AmyrisBio/primercore2.fs @@ -640,12 +640,16 @@ module primercore = let threeP = if r-l+1 > 5 then threePrimeStable false (s.[l..r]) else false // get average of all nucleotide specific penalties let seqP = Array.average seqPen.[l..r] + // check ability to self dimerize + let primerDimerPotential = longestTailTailOverlap (s.[l..r]) (s.[l..r]) let p = posCloseness + tempCloseness + lenCloseness + (if threeP then 0.0 else pen.threePrimeUnstablePenalty) + (if hasPoly then pen.polyPenalty else 0.0) + - seqP + seqP + + (if primerDimerPotential > 2 then (float primerDimerPotential)*3.0 else 0.0) + if (abs(t - tTemp) <= pen.tmMaxDifference) && (not polyC) then yield {l = l ; @@ -764,11 +768,6 @@ module primercore = | RIGHT -> failwith "should not get right here" | CENTERLEFT -> designCenter debug pen s' seqPen offFn ( (float o.targetTemp)*1.0) - - (*with - | Some(a,b,c,d) -> Some( { tag=o.tag; oligo = a ; temp = b ; offset = c} ) - | None -> None *) - | CENTERRIGHT -> failwith "should not get centerright here" (* From 9380bec6f93951ec9ae904521d2b4983c16fac15 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Thu, 2 Nov 2017 16:27:17 -0700 Subject: [PATCH 5/9] MIN: some debugging messages escaping into production --- src/AmyrisBio/primercore2.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AmyrisBio/primercore2.fs b/src/AmyrisBio/primercore2.fs index 56588d3..cef5db7 100644 --- a/src/AmyrisBio/primercore2.fs +++ b/src/AmyrisBio/primercore2.fs @@ -407,7 +407,7 @@ module primercore = if initialTail < 3 then { tag = ""; oligo = s.[f..t] ; temp = existingTemp ; offset = f+offset } else - printfn "disruptPrimerDimers: found problematic initialTail of length %d initial f=%d t=%d" initialTail f t + if debug then printfn "disruptPrimerDimers: found problematic initialTail of length %d initial f=%d t=%d" initialTail f t seq { for i in [1 ; -1 ; 2 ; -2 ; 3 ; -3 ; 4 ; -4; 5; -5 ; 6 ; -6] do let t' = t+i let len' = t'-f+1 @@ -419,7 +419,7 @@ module primercore = } |> Seq.fold (min) (initialTail,t) |> fun (finalTail,finalT) -> - printfn "disruptPrimerDimers: finalTail of length %d deltaT=%d f=%d finalT=%d" finalTail (finalT-t) f finalT + if debug then printfn "disruptPrimerDimers: finalTail of length %d deltaT=%d f=%d finalT=%d" finalTail (finalT-t) f finalT let finalTemp = temp p (s.[f..finalT]) (finalT-f+1) { tag = ""; oligo = s.[f..finalT] ; temp = finalTemp; offset = f+offset } From f74e7cf608b9f64c463529cc32c002ab343e7610 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Fri, 3 Nov 2017 12:13:23 -0700 Subject: [PATCH 6/9] BUG: gc clamp was unnecessarily moving when it already had one --- src/AmyrisBio/primercore2.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/AmyrisBio/primercore2.fs b/src/AmyrisBio/primercore2.fs index cef5db7..105e08e 100644 --- a/src/AmyrisBio/primercore2.fs +++ b/src/AmyrisBio/primercore2.fs @@ -452,9 +452,11 @@ module primercore = | _ when t' > 1 && t'-f+1 > p.minLength -> findGCRev (t'-1) | _ -> None - if p.ATPenalty < 0.0001 then + // did we already end on a G or a C + let endsWithGC = s.[t] |> base2GC = 1 + if endsWithGC || p.ATPenalty < 0.0001 then // No GC optimization - if debug then printfn "cutToGC: no atPenalty, done len=%d" (t-f+1) + if debug then printfn "cutToGC: no atPenalty or endsWithGC already, done len=%d" (t-f+1) { tag = ""; oligo = s.[f..t] ; temp = temp p (s.[f..t]) (t-f+1); offset = f+offset } else // Is there an alternative starting point that would end on a G or C that's not too far away? From a859b20b76998f71725ede2109edbbe76efdc680 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Fri, 3 Nov 2017 12:34:45 -0700 Subject: [PATCH 7/9] OOPS: missing update to the tests for the primer fixes --- tests/AmyrisBio.Tests/testPrimer.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/AmyrisBio.Tests/testPrimer.fs b/tests/AmyrisBio.Tests/testPrimer.fs index f416fce..b8ce476 100644 --- a/tests/AmyrisBio.Tests/testPrimer.fs +++ b/tests/AmyrisBio.Tests/testPrimer.fs @@ -71,7 +71,9 @@ type TestPrimer() = class /// Test that GC clamp finding is behaving sensibly by tempting designer with long AT rich regions member __.TestGCClamp() = let templates = - [ + [ "GAAATCTGTACCAACCGTATAGGTGAAAGAGACCCTTTATGG" ; + // "ATGCCATTCGTTGTTCCTAGAAGAAACCGTTCTTTGT" ; // interesting case, but dimer check overrides GC clamp so will fail + "ATGGTCATTGCTGAAGTTCCTAAATTAGCCTCTGCC" ; "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATA" ; "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATACATATATAAAG" ; "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; @@ -133,6 +135,7 @@ type TestPrimer() = class targetTemp = 60.0; sequencePenalties = None} + // set false->true for diagnostics debugging test cases match oligoDesign false pen task with | None -> Assert.Fail (sprintf "TestGClamp failed to make a primer for %s" template) From 8ed207715416a3018f9d52bc750986970f8d68cd Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Fri, 17 Nov 2017 16:43:46 -0800 Subject: [PATCH 8/9] LNT: response to feedback on PR --- src/AmyrisBio/AmyrisBio.fsproj | 2 +- src/AmyrisBio/primercore2.fs | 158 +++++++++--------- tests/AmyrisBio.Tests/testPrimer.fs | 250 ++++++++++------------------ 3 files changed, 168 insertions(+), 242 deletions(-) diff --git a/src/AmyrisBio/AmyrisBio.fsproj b/src/AmyrisBio/AmyrisBio.fsproj index ddbdb85..c375ff1 100644 --- a/src/AmyrisBio/AmyrisBio.fsproj +++ b/src/AmyrisBio/AmyrisBio.fsproj @@ -8,7 +8,7 @@ Library Amyris.Bio Amyris.Bio - v4.5 + v4.6.1 4.3.0.0 Amyris.Bio diff --git a/src/AmyrisBio/primercore2.fs b/src/AmyrisBio/primercore2.fs index 105e08e..d3fcaa2 100644 --- a/src/AmyrisBio/primercore2.fs +++ b/src/AmyrisBio/primercore2.fs @@ -245,12 +245,12 @@ module primercore = let maxOverlap = min s1.Length s2.Length seq { - for i in 0..maxOverlap do - let suffix1 = suffix s1 i - let suffix2 = suffix s2 i - if suffix1 = Amyris.Bio.biolib.revComp suffix2 then - yield i - } |> Seq.fold (max) 0 + for i in 0..maxOverlap do + let suffix1 = suffix s1 i + let suffix2 = suffix s2 i + if suffix1 = Amyris.Bio.biolib.revComp suffix2 then + yield i + } |> Seq.fold (max) 0 /// Test if an oligo is self complementary @@ -379,89 +379,90 @@ module primercore = let _,_,x = _temp p oligo N x + /// check longest self complementary tail for + /// oligo defined by f'->t' inclusive + let localLongestTailTailOverlap (s:char []) primerFrom primerTo = + let l = primerTo-primerFrom+1 + seq { + for i in l-1..-1..1 do + if seq { 0..i-1 } |> Seq.forall (fun j -> s.[primerTo-i+j] = biolib.rcBase (s.[primerTo-j])) then + yield (i+1) + } + |> Seq.tryFind(fun x -> x<> 0) + |> fun x -> defaultArg x 0 + /// disrupt self primer dimers /// Tweak the stopping point of a primer if it would help avoid a /// situation where last N bases of the primer are self complementary and /// can lead to formation of a primer dimer /// 5'-------CTTTAAAG-3' /// 3'GAAATTTC--------5' - let disruptPrimerDimers (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) f t offset = + /// primerLeft and primerRight delineate the range of bases in the underlying template s that are currently in the primer + let disruptPrimerDimers (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) primerLeft primerRight offset = if debug then printfn "disruptPrimerDimers: checking for problems" - let comp (b:char) = match b with | 'G' -> 'C' | 'C' -> 'G' | 'A' -> 'T' | 'T' -> 'A' | x -> x - - /// check longest self complementary tail for - /// oligo defined by f'->t' inclusive - let localLongestTailTailOverlap f' t' = - let l = t'-f'+1 - seq { - for i in l-1..-1..1 do - if seq { 0..i-1 } |> Seq.forall (fun j -> s.[t'-i+j] = comp (s.[t'-j])) then - yield (i+1) - } |> Seq.tryFind(fun x -> x<> 0) - |> function | None -> 0 | Some v -> v - - let initialTail = localLongestTailTailOverlap f t - if debug then printfn "disruptPrimerDimers: initialTail of length %d for %s" initialTail (arr2seq s.[f..t]) + + let initialTail = localLongestTailTailOverlap s primerLeft primerRight + if debug then printfn "disruptPrimerDimers: initialTail of length %d for %s" initialTail (arr2seq s.[primerLeft..primerRight]) // no-op if initialTail < 3 then - { tag = ""; oligo = s.[f..t] ; temp = existingTemp ; offset = f+offset } + { tag = ""; oligo = s.[primerLeft..primerRight] ; temp = existingTemp ; offset = primerLeft+offset } else - if debug then printfn "disruptPrimerDimers: found problematic initialTail of length %d initial f=%d t=%d" initialTail f t + if debug then printfn "disruptPrimerDimers: found problematic initialTail of length %d initial f=%d t=%d" initialTail primerLeft primerRight seq { for i in [1 ; -1 ; 2 ; -2 ; 3 ; -3 ; 4 ; -4; 5; -5 ; 6 ; -6] do - let t' = t+i - let len' = t'-f+1 - if debug then printfn "disruptPrimerDimers: considering f=%d t=%d len=%d min=%d max=%d templateLen=%d" f t' len' p.minLength p.maxLength s.Length - if len' >= p.minLength && len' <= p.maxLength && t' < s.Length && t' > f then - let tail' = localLongestTailTailOverlap f t' - if debug then printfn "disruptPrimerDimers: considering tail of length %d for %d" tail' t' - yield tail',t' + let primerRightNew = primerRight+i + let primerLenNew = primerRightNew-primerLeft+1 + if debug then printfn "disruptPrimerDimers: considering f=%d t=%d len=%d min=%d max=%d templateLen=%d" primerLeft primerRightNew primerLenNew p.minLength p.maxLength s.Length + if primerLenNew >= p.minLength && primerLenNew <= p.maxLength && primerRightNew < s.Length && primerRightNew > primerLeft then + let tail' = localLongestTailTailOverlap s primerLeft primerRightNew + if debug then printfn "disruptPrimerDimers: considering tail of length %d for %d" tail' primerRightNew + yield tail',primerRightNew } - |> Seq.fold (min) (initialTail,t) + |> Seq.fold (min) (initialTail,primerRight) |> fun (finalTail,finalT) -> - if debug then printfn "disruptPrimerDimers: finalTail of length %d deltaT=%d f=%d finalT=%d" finalTail (finalT-t) f finalT - let finalTemp = temp p (s.[f..finalT]) (finalT-f+1) - { tag = ""; oligo = s.[f..finalT] ; temp = finalTemp; offset = f+offset } + if debug then printfn "disruptPrimerDimers: finalTail of length %d deltaT=%d f=%d finalT=%d" finalTail (finalT-primerRight) primerLeft finalT + let finalTemp = temp p (s.[primerLeft..finalT]) (finalT-primerLeft+1) + { tag = ""; oligo = s.[primerLeft..finalT] ; temp = finalTemp; offset = primerLeft+offset } /// Cut out the region from fr -> to and extend /// Given oligo array from to gcInit offset , return oligo, temp, offset - let cutToGC (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) f t _startingGC offset = + let cutToGC (debug:bool) (existingTemp : float) (p:PrimerParams) (s:char[]) primerFrom primerTo _startingGC offset = if debug then printfn "cutToGC: starting design f=%d t=%d oligo=%s" - f - t - (arr2seq s.[f..t]) - let rec findGCFwd t' (*gc' *) = - if t' < 0 || t' >= s.Length then - failwithf "ERROR: primercord findGC array bounds exception t'=%d\n" t' - match s.[t'] with - |'G' | 'g' | 'C' | 'c' when threePrimeStable false (s.[..t'])-> - Some (t', temp p (s.[f..t']) (t'-f+1)) // end on G/C + primerFrom + primerTo + (arr2seq s.[primerFrom..primerTo]) + let rec findGCFwd newRight = + if newRight < 0 || newRight >= s.Length then + failwithf "ERROR: primercord findGC array bounds exception t'=%d\n" newRight + match s.[newRight] with + |'G' | 'g' | 'C' | 'c' when threePrimeStable false (s.[..newRight])-> + Some (newRight, temp p (s.[primerFrom..newRight]) (newRight-primerFrom+1)) // end on G/C - | _ when t' < (s.Length-1) && t'-f+1 < p.maxLength -> findGCFwd (t'+1) + | _ when newRight < (s.Length-1) && newRight-primerFrom+1 < p.maxLength -> findGCFwd (newRight+1) | _ -> None - let rec findGCRev t' = - if t' < 0 || t' >= s.Length then - failwithf "ERROR: primercord findGC array bounds exception t'=%d\n" t' - match s.[t'] with - |'G' | 'g' | 'C' | 'c' when threePrimeStable false (s.[..t']) -> - Some (t', temp p (s.[f..t']) (t'-f+1)) + let rec findGCRev newRight = + if newRight < 0 || newRight >= s.Length then + failwithf "ERROR: primercord findGC array bounds exception newRight=%d\n" newRight + match s.[newRight] with + |'G' | 'g' | 'C' | 'c' when threePrimeStable false (s.[..newRight]) -> + Some (newRight, temp p (s.[primerFrom..newRight]) (newRight-primerFrom+1)) - | _ when t' > 1 && t'-f+1 > p.minLength -> findGCRev (t'-1) + | _ when newRight > 1 && newRight-primerFrom+1 > p.minLength -> findGCRev (newRight-1) | _ -> None // did we already end on a G or a C - let endsWithGC = s.[t] |> base2GC = 1 + let endsWithGC = s.[primerTo] |> base2GC = 1 if endsWithGC || p.ATPenalty < 0.0001 then // No GC optimization - if debug then printfn "cutToGC: no atPenalty or endsWithGC already, done len=%d" (t-f+1) - { tag = ""; oligo = s.[f..t] ; temp = temp p (s.[f..t]) (t-f+1); offset = f+offset } + if debug then printfn "cutToGC: no atPenalty or endsWithGC already, done len=%d" (primerTo-primerFrom+1) + { tag = ""; oligo = s.[primerFrom..primerTo] ; temp = temp p (s.[primerFrom..primerTo]) (primerTo-primerFrom+1); offset = primerFrom+offset } else // Is there an alternative starting point that would end on a G or C that's not too far away? let altBest = - match findGCFwd t,findGCRev t with + match findGCFwd primerTo,findGCRev primerTo with | None,None -> None // no better option | Some (altPos,altTemp),None -> if debug then @@ -478,43 +479,44 @@ module primercore = if debug then printfn "cutToGC: altTempFwd=%A alTFwd=%d altTempRev=%A altTRev=%d" altTempFwd altPosFwd altTempRev altPosRev - Some( - if abs (altTempFwd-existingTemp) < abs(altTempRev-existingTemp) then - altPosFwd,altTempFwd - else altPosRev,altTempRev - ) + if abs (altTempFwd-existingTemp) < abs(altTempRev-existingTemp) then + altPosFwd,altTempFwd + else altPosRev,altTempRev + |> Some match altBest with - | Some (_,temperature) -> assert (temperature > 10.0) // ensure selected temp isn't crazy + | Some (_,temperature) -> + if temperature < 10.0 then + failwithf "wikitemp1000: fail design temperature of %A < 10C" temperature // ensure selected temp isn't crazy | None -> () match altBest with | None -> if debug then printfn "cutToGC: no better altGC version, going with original f=%d t=%d oligo=%s" - f - t - (arr2seq s.[f..t]) - { tag = ""; oligo = s.[f..t] ; temp = temp p (s.[f..t]) (t-f+1); offset = f+offset } + primerFrom + primerTo + (arr2seq s.[primerFrom..primerTo]) + { tag = ""; oligo = s.[primerFrom..primerTo] ; temp = temp p (s.[primerFrom..primerTo]) (primerTo-primerFrom+1); offset = primerFrom+offset } | Some(_,altTemp) when (altTemp-existingTemp) > p.ATPenalty -> // stick with original design, compromise too big if debug then printfn "cutToGC: ignore altGC option (compromise not worth it) temp=%A f=%d t=%d oligo=%s" temp - f - t - (arr2seq s.[f..t]) - { tag = ""; oligo = s.[f..t] ; temp = temp p (s.[f..t]) (t-f+1); offset = f+offset } + primerFrom + primerTo + (arr2seq s.[primerFrom..primerTo]) + { tag = ""; oligo = s.[primerFrom..primerTo] ; temp = temp p (s.[primerFrom..primerTo]) (primerTo-primerFrom+1); offset = primerFrom+offset } | Some(altPos,altTemp) -> if debug then printfn "cutToGC: using altGC option temp=%A f=%d t=%d oligo=%s" altTemp - f + primerFrom altPos - (arr2seq s.[f..altPos]) + (arr2seq s.[primerFrom..altPos]) - { tag = ""; oligo = s.[f..altPos] ; temp = altTemp ; offset = f+offset} + { tag = ""; oligo = s.[primerFrom..altPos] ; temp = altTemp ; offset = primerFrom+offset} let upper (s:char array) = [| for x in s -> @@ -561,11 +563,11 @@ module primercore = if debug then printfn "designLeft, stopping at nextBase-1=%d thisTemp=%f" (nextBase-1) (thisTemp/1.0) Some(cutToGC debug lastTemp p s 0 (nextBase-1) gcCount offset) - match designLeftInternal sInitUpper nextBase gcCount (0.0) with - | None -> None - | Some prePDCheck -> + designLeftInternal sInitUpper nextBase gcCount (0.0) + |> Option.map (fun prePDCheck -> // possible final tweaks if we have made a primer-dimer with the tail - Some ( disruptPrimerDimers debug prePDCheck.temp p sInitUpper 0 (prePDCheck.oligo.Length-1) offset) + disruptPrimerDimers debug prePDCheck.temp p sInitUpper 0 (prePDCheck.oligo.Length-1) offset + ) /// Check for the presence of mono- or dinucleotide repeats longer than N let hasPolyrun n (s:char[]) = diff --git a/tests/AmyrisBio.Tests/testPrimer.fs b/tests/AmyrisBio.Tests/testPrimer.fs index b8ce476..aaff2ab 100644 --- a/tests/AmyrisBio.Tests/testPrimer.fs +++ b/tests/AmyrisBio.Tests/testPrimer.fs @@ -5,6 +5,87 @@ open NUnit.Framework [] type TestPrimer() = class + let common = [ + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "ATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "CTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "ATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "TAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "GATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "ATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "ATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "TAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; + ] + + let templates1 = + [ "GAAATCTGTACCAACCGTATAGGTGAAAGAGACCCTTTATGG" ; + // "ATGCCATTCGTTGTTCCTAGAAGAAACCGTTCTTTGT" ; // interesting case, but dimer check overrides GC clamp so will fail + "ATGGTCATTGCTGAAGTTCCTAAATTAGCCTCTGCC" ; + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATA" ; + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATACATATATAAAG" ; + ]@common + + let templates2 = + [ + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATA" ; + "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATTATATATATATATATATATAATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATTATATATATATATATAATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; + "CGGTTGGGCTTAACTTTAAAGAAAAAAGTTGAGATTAGATTTATTGTGTT" + ]@common + + let makeTask (template:string) = + {tag = "PR"; + temp = (template.ToCharArray()) + align = LEFT; + strand = TOP; + offset = 0; + targetTemp = 60.0; + sequencePenalties = None} + + let testPenalty = { + tmPenalty = 1.0; + tmMaxDifference = 5.0 ; + positionPenalty = 5.0 ; + lengthPenalty = 3.0 ; + polyLengthThreshold = 4; + polyPenalty = 10. ; + threePrimeUnstablePenalty = 5.0 ; + ATPenalty=3.0 ; + targetLength=20 ; + maxLength = 80 ; + minLength = 20 ; + monovalentConc = mM2M 50.0; + primerConc = uM2M 0.25 ; + divalentConc = mM2M 1.5 ; + templateConc = uM2M 0.01 ; + dNTPConc = uM2M 0.0 ;} + do () [] @@ -38,105 +119,16 @@ type TestPrimer() = class [] /// Test GC rich template can make minimum length primer member __.TestGCRich() = - let template="CTGCCGGCGACGTGGAGCGTCCGATTGTGACGCGCCTGAGCAACCCGGGCACGGTGCTGCGCGAGTCGTGCGACGCCTCACTGCTGGTGCAGGCCATCATCGACGCCATCGTCGACCTGGCCGTGCCCCTGACGGCCGCGTACAACGACGT".ToCharArray() - let pen={lengthPenalty = 3.0; - tmPenalty = 1.0; - tmMaxDifference = 5.0; - positionPenalty = 5.0; - polyLengthThreshold = 4; - polyPenalty = 10.0; - threePrimeUnstablePenalty = 5.0; - ATPenalty = 3.0; - maxLength = 60; - minLength = 20; - targetLength = 20; - monovalentConc = 0.05; - divalentConc = 0.0015; - primerConc = 2.5e-07; - templateConc = 1e-08; - dNTPConc = 0.0} - - let task = { tag = "PR"; - temp = template - align = CENTERLEFT; - strand = TOP; - offset = 0; - targetTemp = 60.0; - sequencePenalties = None} - - let p = oligoDesignWithCompromise false pen task + let template="CTGCCGGCGACGTGGAGCGTCCGATTGTGACGCGCCTGAGCAACCCGGGCACGGTGCTGCGCGAGTCGTGCGACGCCTCACTGCTGGTGCAGGCCATCATCGACGCCATCGTCGACCTGGCCGTGCCCCTGACGGCCGCGTACAACGACGT" + let p = oligoDesignWithCompromise false testPenalty (makeTask template) Assert.IsTrue(p.IsSome) [] /// Test that GC clamp finding is behaving sensibly by tempting designer with long AT rich regions member __.TestGCClamp() = - let templates = - [ "GAAATCTGTACCAACCGTATAGGTGAAAGAGACCCTTTATGG" ; - // "ATGCCATTCGTTGTTCCTAGAAGAAACCGTTCTTTGT" ; // interesting case, but dimer check overrides GC clamp so will fail - "ATGGTCATTGCTGAAGTTCCTAAATTAGCCTCTGCC" ; - "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATA" ; - "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "ATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "CTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "ATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "GATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "ATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "AATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "ATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "AAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - ] - let pen= { - tmPenalty = 1.0; - tmMaxDifference = 5.0 ; - positionPenalty = 5.0 ; - lengthPenalty = 3.0 ; - polyLengthThreshold = 4; - polyPenalty = 10. ; - threePrimeUnstablePenalty = 5.0 ; - ATPenalty=3.0 ; - targetLength=20 ; - maxLength = 80 ; - minLength = 20 ; - monovalentConc = mM2M 50.0; - primerConc = uM2M 0.25 ; - divalentConc = mM2M 1.5 ; - templateConc = uM2M 0.01 ; - dNTPConc = uM2M 0.0 ;} - - for template in templates do - let task = { tag = "PR"; - temp = (template.ToCharArray()) - align = LEFT; - strand = TOP; - offset = 0; - targetTemp = 60.0; - sequencePenalties = None} - + for template in templates1 do // set false->true for diagnostics debugging test cases - match oligoDesign false pen task with + match oligoDesign false testPenalty (makeTask template) with | None -> Assert.Fail (sprintf "TestGClamp failed to make a primer for %s" template) | Some design -> @@ -154,78 +146,10 @@ type TestPrimer() = class /// Test that we don't form a primer dimer under tempting circumstances member __.TestPrimerDimer() = let verbose = false - let templates = - [ - "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATA" ; - "AAAAAAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATAATATATATATATTATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATTATATATATATATATATATAATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATTATATATATATATATAATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "ATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "CTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "ATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "TAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "AGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATATATATATATATACATATATAAAG" ; - "GATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "ATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "AATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "ATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "TAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "AAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "AAAAAATCTTAATAGATTAATTTAAACAGTATATGTACAGTTTTATATATATATATATATATATATACATATATAAAG" ; - "CGGTTGGGCTTAACTTTAAAGAAAAAAGTTGAGATTAGATTTATTGTGTT" - ] - let pen= { - tmPenalty = 1.0; - tmMaxDifference = 5.0 ; - positionPenalty = 5.0 ; - lengthPenalty = 3.0 ; - polyLengthThreshold = 4; - polyPenalty = 10. ; - threePrimeUnstablePenalty = 5.0 ; - ATPenalty=3.0 ; - targetLength=20 ; - maxLength = 80 ; - minLength = 20 ; - monovalentConc = mM2M 50.0; - primerConc = uM2M 0.25 ; - divalentConc = mM2M 1.5 ; - templateConc = uM2M 0.01 ; - dNTPConc = uM2M 0.0 ;} - - for template in templates do + for template in templates2 do if verbose then printfn "Testing %s for primer dimer avoidance" template - let task = { tag = "PR"; - temp = (template.ToCharArray()) - align = LEFT; - strand = TOP; - offset = 0; - targetTemp = 60.0; - sequencePenalties = None} - if verbose then printfn "designing oligo for %s " template - match oligoDesign false pen task with + match oligoDesign false testPenalty (makeTask template) with | None -> if verbose then printfn "failed design for %s " template Assert.Fail "TestPrimerDimer failed to make a primer" From 705a7304d6a207fccec631b8a89ac6c95f99d874 Mon Sep 17 00:00:00 2001 From: Darren Platt Date: Fri, 17 Nov 2017 19:09:34 -0800 Subject: [PATCH 9/9] MIN: reverting project to .net 4.5 --- src/AmyrisBio/AmyrisBio.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AmyrisBio/AmyrisBio.fsproj b/src/AmyrisBio/AmyrisBio.fsproj index c375ff1..ddbdb85 100644 --- a/src/AmyrisBio/AmyrisBio.fsproj +++ b/src/AmyrisBio/AmyrisBio.fsproj @@ -8,7 +8,7 @@ Library Amyris.Bio Amyris.Bio - v4.6.1 + v4.5 4.3.0.0 Amyris.Bio