packages feed

elynx-seq 0.3.0 → 0.3.1

raw patch · 21 files changed

+207/−159 lines, 21 filesdep +attoparsecdep −hspec-megaparsecdep −megaparsec

Dependencies added: attoparsec

Dependencies removed: hspec-megaparsec, megaparsec

Files

ChangeLog.md view
@@ -2,7 +2,14 @@ # Changelog for ELynx  -## Version 0.3.1; in development+## Version 0.3.2; in development+++## Version 0.3.1++-   Use Attoparsec.+-   Use ByteString consistently.+-   Remove elynx-tools dependency from libaries.   ## Version 0.3.0
README.md view
@@ -2,7 +2,7 @@  # The ELynx Suite -Version: 0.3.0.+Version: 0.3.1. Reproducible evolution made easy.  <p align="center"><img src="https://travis-ci.org/dschrempf/elynx.svg?branch=master"/></p>@@ -90,9 +90,9 @@      slynx --help | head -n -16 -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME]                   [-f|--force] COMMAND@@ -136,9 +136,9 @@      slynx concatenate --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx concatenate (-a|--alphabet NAME) INPUT-FILE       Concatenate sequences found in input files.@@ -157,9 +157,9 @@      slynx examine --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx examine (-a|--alphabet NAME) INPUT-FILE [--per-site]       Examine sequences. If data is a multi sequence alignment, additionally analyze columns.@@ -179,9 +179,9 @@      slynx filter-rows --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx filter-rows (-a|--alphabet NAME) INPUT-FILE [--longer-than LENGTH]                               [--shorter-than LENGTH] [--standard-characters]@@ -202,9 +202,9 @@      slynx filter-columns --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx filter-columns (-a|--alphabet NAME) INPUT-FILE                                  [--standard-chars DOUBLE]@@ -226,9 +226,9 @@      slynx simulate --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx simulate (-t|--tree-file Name) [-s|--substitution-model MODEL]                            [-m|--mixture-model MODEL] [-e|--edm-file NAME] @@ -305,9 +305,9 @@      slynx sub-sample --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx sub-sample (-a|--alphabet NAME) INPUT-FILE                             (-n|--number-of-sites INT)@@ -335,9 +335,9 @@      slynx translate --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: slynx translate (-a|--alphabet NAME) INPUT-FILE (-r|--reading-frame INT)                            (-u|--universal-code CODE)@@ -360,9 +360,9 @@      tlynx --help | head -n -16 -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: tlynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME]                   [-f|--force] COMMAND@@ -399,9 +399,9 @@      tlynx compare --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: tlynx compare [-n|--normalize] [-b|--bipartitions] [-t|--intersect]                           [-f|--newick-format FORMAT] NAMES@@ -428,9 +428,9 @@      tlynx examine --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: tlynx examine INPUT-FILE [-f|--newick-format FORMAT]       Compute summary statistics of phylogenetic trees.@@ -452,9 +452,9 @@      tlynx simulate --help -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: tlynx simulate [-t|--nTrees INT] [-n|--nLeaves INT] [-H|--height DOUBLE]                            [-M|--condition-on-mrca] [-l|--lambda DOUBLE] @@ -491,9 +491,9 @@      elynx --help | head -n -16 -    ELynx Suite version 0.3.0.+    ELynx Suite version 0.3.1.     Developed by Dominik Schrempf.-    Compiled on July 30, 2020, at 12:40 pm, UTC.+    Compiled on August 3, 2020, at 12:12 pm, UTC.          Usage: elynx COMMAND       Validate and redo past ELynx analyses
elynx-seq.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: afdff7b344d4b63f734a6f18db88ef39b1f45ed84c64e63045b57edfb839684f+-- hash: 656a2c088bc0e776325be75f926d33618cc4a11940ba39f35f50bb90e6636b7d  name:           elynx-seq-version:        0.3.0+version:        0.3.1 synopsis:       Handle molecular sequences description:    Examine, modify, and simulate molecular sequences in a reproducible way. Please see the README on GitHub at <https://github.com/dschrempf/elynx>. category:       Bioinformatics@@ -56,12 +56,11 @@   ghc-options: -Wall -fllvm   build-depends:       aeson+    , attoparsec     , base >=4.7 && <5     , bytestring     , containers-    , elynx-tools     , matrices-    , megaparsec     , mwc-random     , parallel     , primitive@@ -90,7 +89,6 @@     , elynx-seq     , elynx-tools     , hspec-    , hspec-megaparsec     , matrices     , vector   default-language: Haskell2010
src/ELynx/Data/Alphabet/Character.hs view
@@ -26,11 +26,11 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import qualified Data.Vector.Unboxed as V import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | Alphabet characters; abstracted so that representation can be changed at -- some point.
src/ELynx/Data/Alphabet/DistributionDiversity.hs view
@@ -33,8 +33,17 @@ import qualified Data.Vector.Generic as V import ELynx.Data.Alphabet.Alphabet import ELynx.Data.Alphabet.Character-import ELynx.Tools +eps :: Double+eps = 1e-12++-- Calculate x*log(x) but set to 0.0 when x is smaller than 'eps'.+xLogX :: Double -> Double+xLogX x+  | x < 0.0 = error "Argument lower than zero."+  | eps > x = 0.0+  | otherwise = x * log x+ -- | Entropy of vector. entropy :: (Vector v Double) => v Double -> Double entropy v =@@ -44,7 +53,7 @@         ("entropy: Sesult of following vector is NaN: " ++ show (toList v) ++ ".")     else res   where-    res = negate $ sumVec $ V.map xLogX v+    res = negate $ V.sum $ V.map xLogX v  -- | Effective number of used characters measured using 'entropy'. The result -- only makes sense when the sum of the array is 1.0.@@ -55,7 +64,7 @@ -- binomially sampling the same character twice and only makes sense when the -- sum of the array is 1.0. homoplasy :: Vector v Double => v Double -> Double-homoplasy v = sumVec $ V.map (\x -> x * x) v+homoplasy v = V.sum $ V.map (\x -> x * x) v  -- | Effective number of used characters measured using 'homoplasy'. The result -- only makes sense when the sum of the array is 1.0.@@ -98,4 +107,4 @@ frequencyCharacters alph d = V.map (`saveDivision` s) counts   where     counts = countCharacters alph d-    s = sumVec counts+    s = V.sum counts
src/ELynx/Data/Character/AminoAcid.hs view
@@ -47,10 +47,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | Amino acids. data AminoAcid = A | C | D | E | F | G | H | I | K | L | M | N | P | Q | R | S | T | V | W | Y
src/ELynx/Data/Character/AminoAcidI.hs view
@@ -68,10 +68,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | Amino acids. data AminoAcidI
src/ELynx/Data/Character/AminoAcidS.hs view
@@ -52,10 +52,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | Amino acids. data AminoAcidS
src/ELynx/Data/Character/AminoAcidX.hs view
@@ -52,10 +52,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | Amino acids. data AminoAcidX
src/ELynx/Data/Character/Character.hs view
@@ -27,10 +27,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import qualified Data.Set as S import Data.Vector.Unboxed.Base (Unbox) import Data.Word8 (Word8)-import ELynx.Tools  -- XXX: Remove name clash with ELynx.Data.Alphabet.Alphabet.Character? 
src/ELynx/Data/Character/Nucleotide.hs view
@@ -29,10 +29,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | Nucleotides. data Nucleotide = A | C | G | T
src/ELynx/Data/Character/NucleotideI.hs view
@@ -58,10 +58,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | NucleotideIs. data NucleotideI
src/ELynx/Data/Character/NucleotideX.hs view
@@ -34,10 +34,10 @@   ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools  -- | Extended nucleotides. data NucleotideX
src/ELynx/Data/Sequence/Alignment.hs view
@@ -44,7 +44,8 @@  import Control.Monad hiding (join) import Control.Monad.Primitive-import qualified Data.ByteString.Lazy.Char8 as L+import Control.Parallel.Strategies+import qualified Data.ByteString.Lazy.Char8 as BL import Data.List hiding   ( concat,     length,@@ -56,7 +57,6 @@ import qualified ELynx.Data.Alphabet.DistributionDiversity as D import ELynx.Data.Sequence.Defaults import qualified ELynx.Data.Sequence.Sequence as S-import ELynx.Tools import System.Random.MWC import Prelude hiding   ( concat,@@ -94,6 +94,8 @@     a = S.alphabet $ head ss     bss = map S.characters ss     d = M.fromRows bss+    allEqual [] = True+    allEqual xs = all (== head xs) $ tail xs  -- | Conversion to list of 'S.Sequence's. toSequences :: Alignment -> [S.Sequence]@@ -106,32 +108,25 @@   where     rows = M.toRows da --- -- | Show a 'Alignment', untrimmed.--- summarizeSeq :: Alignment -> Int -> L.ByteString--- summarizeSeq m i =---   L.unwords [ alignLeft nameWidth $ names m !! i---             , summarizeByteString summaryLength $---               L.pack $ V.toList $ V.map toChar $ M.takeRow (matrix m) i ]--header :: Alignment -> L.ByteString+header :: Alignment -> BL.ByteString header a =-  L.unlines $-    [ L.pack "Multi sequence alignment.",-      L.pack $ "Code: " ++ A.alphabetDescription (alphabet a) ++ ".",-      L.pack $ "Length: " ++ show (length a) ++ "."+  BL.unlines $+    [ BL.pack "Multi sequence alignment.",+      BL.pack $ "Code: " ++ A.alphabetDescription (alphabet a) ++ ".",+      BL.pack $ "Length: " ++ show (length a) ++ "."     ]       ++ reportLengthSummary       ++ reportNumberSummary   where     reportLengthSummary =-      [ L.pack $+      [ BL.pack $           "For each sequence, the "             ++ show summaryLength             ++ " first bases are shown."         | length a > summaryLength       ]     reportNumberSummary =-      [ L.pack $+      [ BL.pack $           show summaryNSequences             ++ " out of "             ++ show (nSequences a)@@ -140,9 +135,23 @@       ]  -- | Similar to 'S.summarizeSequenceList' but with different Header.-summarize :: Alignment -> L.ByteString+summarize :: Alignment -> BL.ByteString summarize a = header a <> S.body (toSequences a) +-- Vertical concatenation.+(===) :: V.Unbox a => M.Matrix a -> M.Matrix a -> M.Matrix a+(===) l r = M.fromRows $ lRs ++ rRs+  where+    lRs = M.toRows l+    rRs = M.toRows r++-- Horizontal concatenation.+(|||) :: V.Unbox a => M.Matrix a -> M.Matrix a -> M.Matrix a+(|||) l r = M.fromColumns $ lCs ++ rCs+  where+    lCs = M.toColumns l+    rCs = M.toColumns r+ -- | Join two 'Alignment's vertically. That is, add more sequences -- to an alignment. See also 'concat'. join :: Alignment -> Alignment -> Alignment@@ -219,6 +228,16 @@ -- the number of characters. type FrequencyData = M.Matrix Double +-- Map a function on each row of a DIM2 array; parallel version with given chunk size.+fMapColParChunk ::+  (V.Unbox a, V.Unbox b) =>+  Int ->+  (V.Vector a -> V.Vector b) ->+  M.Matrix a ->+  M.Matrix b+fMapColParChunk n f m =+  M.fromColumns (map f (M.toColumns m) `using` parListChunk n rseq)+ -- | Calculcate frequency of characters at each site of a multi sequence alignment. toFrequencyData :: Alignment -> FrequencyData toFrequencyData a = fMapColParChunk 100 (D.frequencyCharacters spec) (matrix a)@@ -236,6 +255,13 @@   where     nSites = M.cols fd +-- Parallel map with given chunk size.+parMapChunk :: Int -> (a -> b) -> [a] -> [b]+parMapChunk n f as = map f as `using` parListChunk n rseq++chunksize :: Int+chunksize = 500+ -- | Diversity analysis. See 'kEffEntropy'. kEffEntropy :: FrequencyData -> [Double] kEffEntropy fd = parMapChunk chunksize D.kEffEntropy (M.toColumns fd)@@ -262,6 +288,11 @@ countUnknowns a = V.length . V.filter (A.isUnknown (alphabet a)) $ allChars   where     allChars = M.flatten $ matrix a++-- Sample the given sites from a matrix.+subSampleMatrix :: V.Unbox a => [Int] -> M.Matrix a -> M.Matrix a+subSampleMatrix is m =+  M.fromColumns $ foldl' (\a i -> M.takeColumn m i : a) [] is  -- | Sample the given sites from a multi sequence alignment. subSample :: [Int] -> Alignment -> Alignment
src/ELynx/Data/Sequence/Sequence.hs view
@@ -48,14 +48,13 @@ where  import Control.Parallel.Strategies-import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import Data.List (maximumBy) import Data.Ord (comparing) import qualified Data.Vector.Unboxed as V import qualified ELynx.Data.Alphabet.Alphabet as A import ELynx.Data.Alphabet.Character import ELynx.Data.Sequence.Defaults-import ELynx.Tools import qualified Text.Printf as P import Prelude hiding   ( concat,@@ -65,22 +64,22 @@   ( length,   ) --- | For now, 'Name's are just 'L.ByteString's.-type Name = L.ByteString+-- | For now, 'Name's are just 'BL.ByteString's.+type Name = BL.ByteString  -- | The description of a sequence.-type Description = L.ByteString+type Description = BL.ByteString  -- | The vector of characters of a sequence. type Characters = V.Vector Character  -- | Convert byte string to sequence characters.-fromByteString :: L.ByteString -> Characters-fromByteString = V.fromList . map fromChar . L.unpack+fromByteString :: BL.ByteString -> Characters+fromByteString = V.fromList . map fromChar . BL.unpack  -- | Convert sequence characters to byte string.-toByteString :: Characters -> L.ByteString-toByteString = L.pack . map toChar . V.toList+toByteString :: Characters -> BL.ByteString+toByteString = BL.pack . map toChar . V.toList  -- | Sequences have a name, a possibly empty description, a code and hopefully a -- lot of data.@@ -92,33 +91,51 @@   }   deriving (Show, Eq) -getInfo :: Sequence -> L.ByteString+alignRight :: Int -> BL.ByteString -> BL.ByteString+alignRight n s =+  BL.replicate (fromIntegral n - l) ' ' <> BL.take (fromIntegral n) s+  where+    l = BL.length s++alignLeft :: Int -> BL.ByteString -> BL.ByteString+alignLeft n s =+  BL.take (fromIntegral n) s <> BL.replicate (fromIntegral n - l) ' '+  where+    l = BL.length s++getInfo :: Sequence -> BL.ByteString getInfo s =-  L.unwords+  BL.unwords     [ alignLeft nameWidth (name s),-      alignRight fieldWidth (L.pack $ show $ alphabet s),-      alignRight fieldWidth (L.pack . show $ len),-      alignRight fieldWidth (L.pack $ P.printf "%2.2f" pGaps)+      alignRight fieldWidth (BL.pack $ show $ alphabet s),+      alignRight fieldWidth (BL.pack . show $ len),+      alignRight fieldWidth (BL.pack $ P.printf "%2.2f" pGaps)     ]   where     len = length s     nGaps = countGaps s     pGaps = 100 * fromIntegral nGaps / fromIntegral len :: Double +-- If a string is longer than a given value, trim it and add some dots.+summarizeByteString :: Int -> BL.ByteString -> BL.ByteString+summarizeByteString l s+  | BL.length s >= fromIntegral l = BL.take (fromIntegral l) s <> BL.pack "..."+  | otherwise = s+ -- | Trim and show a 'Sequence'.-summarize :: Sequence -> L.ByteString+summarize :: Sequence -> BL.ByteString summarize s =-  L.unwords+  BL.unwords     [getInfo s, summarizeByteString summaryLength $ toByteString (characters s)]  -- | Trim and show a list of 'Sequence's.-summarizeSequences :: [Sequence] -> L.ByteString+summarizeSequences :: [Sequence] -> BL.ByteString summarizeSequences ss = header ss <> body (take summaryNSequences ss)  -- | Header printed before 'Sequence' list.-tableHeader :: L.ByteString+tableHeader :: BL.ByteString tableHeader =-  L.unwords+  BL.unwords     [ alignLeft nameWidth "Name",       alignRight fieldWidth "Code",       alignRight fieldWidth "Length",@@ -127,15 +144,15 @@     ]  -- | A short description of the sequence.-header :: [Sequence] -> L.ByteString+header :: [Sequence] -> BL.ByteString header ss =-  L.unlines $+  BL.unlines $     reportIfSubsetIsShown-      ++ [ L.pack $+      ++ [ BL.pack $              "For each sequence, the "                ++ show summaryLength                ++ " first bases are shown.",-           L.pack $ "List contains " ++ show (Pr.length ss) ++ " sequences.",+           BL.pack $ "List contains " ++ show (Pr.length ss) ++ " sequences.",            "",            tableHeader          ]@@ -147,12 +164,12 @@         ++ show (Pr.length ss)         ++ " sequences are shown."     reportIfSubsetIsShown-      | l > summaryNSequences = [L.pack s]+      | l > summaryNSequences = [BL.pack s]       | otherwise = []  -- | Trim and show a list of 'Sequence's.-body :: [Sequence] -> L.ByteString-body ss = L.unlines (map summarize ss `using` parListChunk 5 rdeepseq)+body :: [Sequence] -> BL.ByteString+body ss = BL.unlines (map summarize ss `using` parListChunk 5 rdeepseq)  -- | Calculate length of 'Sequence'. length :: Sequence -> Int@@ -161,6 +178,9 @@ -- | Check if all 'Sequence's have equal length. equalLength :: [Sequence] -> Bool equalLength = allEqual . map length+  where+    allEqual [] = True+    allEqual xs = all (== head xs) $ tail xs  -- | Find the longest 'Sequence' in a list. longest :: [Sequence] -> Sequence@@ -180,16 +200,16 @@   | i /= j =     error $       "concatenate: Sequences do not have equal names: "-        ++ L.unpack i+        ++ BL.unpack i         ++ ", "-        ++ L.unpack j+        ++ BL.unpack j         ++ "."   | d /= f =     error $       "concatenate: Sequences do not have equal descriptions: "-        ++ L.unpack d+        ++ BL.unpack d         ++ ", "-        ++ L.unpack f+        ++ BL.unpack f         ++ "."   | c /= k =     error $
src/ELynx/Data/Sequence/Translate.hs view
@@ -19,7 +19,13 @@ import qualified ELynx.Data.Alphabet.Character as C import ELynx.Data.Character.Codon import ELynx.Data.Sequence.Sequence-import ELynx.Tools++-- Chop list into chunks of given length. If the last chop is shorter than+-- length, it is dropped.+chopVec :: V.Unbox a => Int -> V.Vector a -> [V.Vector a]+chopVec n xs+  | V.length xs < n = []+  | otherwise = V.take n xs : chopVec n (V.drop n xs)  -- | Translate a sequence from 'DNA' or 'DNAX' to 'ProteinS'. translateSeq :: UniversalCode -> Int -> Sequence -> Sequence
src/ELynx/Export/Sequence/Fasta.hs view
@@ -18,19 +18,19 @@   ) where -import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import ELynx.Data.Sequence.Sequence -fastaHeader :: L.ByteString -> L.ByteString -> L.ByteString+fastaHeader :: BL.ByteString -> BL.ByteString -> BL.ByteString fastaHeader n d =-  L.singleton '>' <> n <> if L.null d then L.empty else L.pack " " <> d+  BL.singleton '>' <> n <> if BL.null d then BL.empty else BL.pack " " <> d  -- | Convert a 'Sequence' to Fasta format.-sequenceToFasta :: Sequence -> L.ByteString+sequenceToFasta :: Sequence -> BL.ByteString sequenceToFasta s =-  L.unlines [fastaHeader (name s) (description s), toByteString $ characters s]+  BL.unlines [fastaHeader (name s) (description s), toByteString $ characters s]  -- | Convert a list 'Sequence's to Fasta format. A newline is added between any -- two 'Sequence's.-sequencesToFasta :: [Sequence] -> L.ByteString-sequencesToFasta ss = L.concat $ map sequenceToFasta ss+sequencesToFasta :: [Sequence] -> BL.ByteString+sequencesToFasta ss = BL.concat $ map sequenceToFasta ss
src/ELynx/Import/Sequence/Fasta.hs view
@@ -14,72 +14,57 @@ -- Parse FASTA files. -- -- [NCBI file specifications](https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=BlastHelp).------ For more complicated parsers, try to use a [lexer](https://hackage.haskell.org/package/megaparsec-7.0.1/docs/Text-Megaparsec-Byte-Lexer.html). module ELynx.Import.Sequence.Fasta-  ( Parser,-    fastaSequence,+  ( fastaSequence,     fasta,   ) where -import Control.Monad-import qualified Data.ByteString.Lazy.Char8 as L+import Control.Applicative+import qualified Data.Attoparsec.ByteString as AS+import qualified Data.Attoparsec.ByteString.Char8 as AC+import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Set as S-import Data.Void-import Data.Word8-  ( Word8,-    isAlphaNum,-  )+import Data.Word8 (Word8) import ELynx.Data.Alphabet.Alphabet as A import ELynx.Data.Alphabet.Character import ELynx.Data.Sequence.Sequence-import ELynx.Tools-import Text.Megaparsec-import Text.Megaparsec.Byte --- | Shortcut.-type Parser = Parsec Void L.ByteString--isSpecial :: Word8 -> Bool-isSpecial w = w `elem` map c2w ['_', '|', '.', '-']--isHeader :: Word8 -> Bool-isHeader w = isAlphaNum w || isSpecial w+isSpecial :: Char -> Bool+isSpecial w = w `elem` ['_', '|', '.', '-'] -isHorizontalSpace :: Word8 -> Bool-isHorizontalSpace w = (w == c2w ' ') || (w == c2w '\t')+isHeader :: Char -> Bool+isHeader w = AC.isAlpha_ascii w || AC.isDigit w || isSpecial w -sequenceHeader :: Parser (L.ByteString, L.ByteString)+sequenceHeader :: AS.Parser (BL.ByteString, BL.ByteString) sequenceHeader = do-  _ <- char (c2w '>')-  n <- takeWhile1P (Just "Name") isHeader-  _ <- takeWhileP (Just "Horizontal space") isHorizontalSpace-  d <- takeWhileP (Just "Description") isHeader-  _ <- eol-  return (n, d)+  _ <- AC.char '>'+  n <- AC.takeWhile1 isHeader+  _ <- AS.takeWhile AC.isHorizontalSpace+  d <- AC.takeWhile isHeader+  _ <- AC.endOfLine+  return (BL.fromStrict n, BL.fromStrict d)  -- It is a little faster to directly pass the set of allowed characters. Then, -- this set only has to be calculcated once per sequence in 'fastaSequence'.-sequenceLine :: S.Set Word8 -> Parser L.ByteString+sequenceLine :: S.Set Word8 -> AS.Parser BL.ByteString sequenceLine s = do   -- XXX: Will fail for non-capital letters.-  !xs <- takeWhile1P (Just "Alphabet character") (`S.member` s)-  _ <- void eol <|> eof-  return xs+  !xs <- AS.takeWhile1 (`S.member` s)+  return (BL.fromStrict xs)  -- XXX: If sequences are parsed line by line, the lines have to be copied when -- forming the complete sequence. This is not memory efficient.  -- | Parse a sequence of characters.-fastaSequence :: Alphabet -> Parser Sequence+fastaSequence :: Alphabet -> AS.Parser Sequence fastaSequence a = do   (n, d) <- sequenceHeader   let !alph = S.map toWord (A.all . alphabetSpec $ a)-  lns <- some (sequenceLine alph)-  _ <- many eol-  return $ Sequence n d a (fromByteString $ L.concat lns)+  lns <- sequenceLine alph `AS.sepBy1` AC.endOfLine+  _ <- many AC.endOfLine+  return $ Sequence n d a (fromByteString $ BL.concat lns)  -- | Parse a Fasta file with given 'Alphabet'.-fasta :: Alphabet -> Parser [Sequence]-fasta a = some (fastaSequence a) <* eof+fasta :: Alphabet -> AS.Parser [Sequence]+fasta a = some (fastaSequence a) <* AS.endOfInput
test/ELynx/Data/Sequence/AlignmentSpec.hs view
@@ -13,7 +13,7 @@   ) where -import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Matrix.Unboxed as M import ELynx.Data.Alphabet.Alphabet import ELynx.Data.Alphabet.Character@@ -31,8 +31,8 @@ ssA :: Alignment ssA =   Alignment-    (map L.pack ["SEQUENCE_1", "SEQUENCE_2", "SEQUENCE_3"])-    (replicate 3 L.empty)+    (map BL.pack ["SEQUENCE_1", "SEQUENCE_2", "SEQUENCE_3"])+    (replicate 3 BL.empty)     DNAI     ssData 
test/ELynx/Data/Sequence/SequenceSpec.hs view
@@ -13,7 +13,7 @@   ) where -import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import ELynx.Data.Alphabet.Alphabet import ELynx.Data.Sequence.Sequence import ELynx.Import.Sequence.Fasta@@ -26,17 +26,13 @@ fastaDifferentLengthTrimmedFN :: FilePath fastaDifferentLengthTrimmedFN = "data/NucleotideDifferentLengthTrimmed.fasta" -longestSequenceInFileBS :: L.ByteString+longestSequenceInFileBS :: BL.ByteString longestSequenceInFileBS =-  L.unlines $-    map L.pack [">SEQUENCE_3", "ATTTAAAAAAACCCAAAACCCGGGCCCCGGGTTTTTTTA"]+  BL.unlines $+    map BL.pack [">SEQUENCE_3", "ATTTAAAAAAACCCAAAACCCGGGCCCCGGGTTTTTTTA"]  longestSequenceInFile :: Sequence-longestSequenceInFile =-  parseByteStringWith-    "Fasta byte string"-    (fastaSequence DNA)-    longestSequenceInFileBS+longestSequenceInFile = parseByteStringWith (fastaSequence DNA) longestSequenceInFileBS  spec :: Spec spec = do
test/ELynx/Export/Sequence/FastaSpec.hs view
@@ -30,9 +30,5 @@       $ do         ss <- parseFileWith (fasta DNAI) fastaNucleotideIUPACFN         let f = sequencesToFasta ss-            ss' =-              parseByteStringWith-                "Fasta byte string created from sequence object"-                (fasta DNAI)-                f+            ss' = parseByteStringWith (fasta DNAI) f         ss `shouldBe` ss'