Skip to content

fasta rework - #284

Merged
TimothyStiles merged 21 commits into
bebop:mainfrom
soypat:fasta-rework
Dec 15, 2022
Merged

fasta rework#284
TimothyStiles merged 21 commits into
bebop:mainfrom
soypat:fasta-rework

Conversation

@soypat

@soypat soypat commented Nov 29, 2022

Copy link
Copy Markdown
Contributor

So this is NOT intended as a performance boost change. This is above all an API rework for ease of use and clarity.

I have implemented a fasta.Parser type. This type is intended to be the base to parse all fasta data and replace all other implementations.

Benefits of Parser.ParseNext() when compared to XConcurrent functions:

  • Provides the bare minimum API signature to parse a single fasta genome
  • It is a simple and easy to understand API.
  • No ambiguities on API use.
  • Takes a io.Reader for user liberty to use with any API or stream.
  • User has more control over how many fasta genomes they want to read
  • (EDIT) Also errors! ParseNext can return a very useful error with the line number on which parsing stopped
goos: linux
goarch: amd64
pkg: github.com/TimothyStiles/poly/io/fasta
cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
BenchmarkFastaLegacy-8   	      55	  19968425 ns/op	17326897 B/op	  158406 allocs/op
BenchmarkParser-8        	     118	   9643441 ns/op	16795447 B/op	   58150 allocs/op

@gitpod-io

gitpod-io Bot commented Nov 29, 2022

Copy link
Copy Markdown

Comment thread io/fasta/fasta.go Outdated
Comment thread tutorials/000_introduction_test.go
Comment thread io/fasta/fasta_test.go
@TimothyStiles

Copy link
Copy Markdown
Collaborator

@soypat the benchmark needs some fixing but this is looking really good! Is your plan to eventually deprecate the legacy fasta parsers as well?

@soypat

soypat commented Dec 2, 2022

Copy link
Copy Markdown
Contributor Author

@TimothyStiles I'd really like to see those "Concurrent" parsers go. Maybe Read can be renamed as ReadFile, but that'd be gravy. Parse is OK as shorthand for creating a new parser and reading all.

@soypat
soypat force-pushed the fasta-rework branch 2 times, most recently from 1ad691f to ec58940 Compare December 2, 2022 12:27
@soypat
soypat marked this pull request as ready for review December 2, 2022 12:28
@soypat

soypat commented Dec 2, 2022

Copy link
Copy Markdown
Contributor Author

@TimothyStiles So I have changed the following things:

  • I initialize uniprot 1MB fasta as a raw format string in TestMain. This will avoid some noisiness of file manipulation in benchmarks
  • I have added an argument to NewParser: maxLineSize, in bytes. This is to give users some freedom of how much heap they want to use for the parser.
  • Added error handling during benchmarks
  • Better error handling for the non-EOF case during parsing

@TimothyStiles

Copy link
Copy Markdown
Collaborator

@soypat recent additions are looking great. Having a shorthand like Parse would be excellent.

Reset, ParseAll and ParseN need to be tested and it looks there are a couple of corner cases in ParseNext that also need to be tested.
Screen Shot 2022-12-02 at 1 16 24 PM
Screen Shot 2022-12-02 at 1 16 32 PM

@soypat

soypat commented Dec 3, 2022

Copy link
Copy Markdown
Contributor Author

@TimothyStiles Alright, looks much better. Fixed various bugs and test coverage is up to 95%

Comment thread io/fasta/fasta.go
}
fastas = append(fastas, fasta)
}
return fastas, nil

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

success case doesn't seem to be covered by tests?

@TimothyStiles

Copy link
Copy Markdown
Collaborator

@soypat I just rewrote fasta.Parse as a wrapper around fasta.ParseAll and had a couple of tests fail.

ExampleWrite fails and doesn't return anything when trying to read what was written out. Couple of things are happening here.

  1. New parser is stripping the * character which denotes a stop codon. Since it's technically not a amino acid it doesn't have a letter but is important to denote.
  2. It looks like there's a fatal difference between what the parser can read and what the writer outputs.

Screen Shot 2022-12-06 at 1 44 25 PM
Screen Shot 2022-12-06 at 1 44 11 PM
Screen Shot 2022-12-06 at 1 43 57 PM

Been super busy the past several days but will be free to work on this together any time this week after tonight!

Comment thread io/fasta/fasta.go Outdated
Comment thread io/fasta/fasta_test.go
expected []Fasta
}{
{
content: ">humen\nGATTACA\nCATGAT", // EOF-ended Fasta not valid

@TimothyStiles TimothyStiles Dec 9, 2022

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we should probably be more permissive and allow EOF-ended Fasta as valid?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@soypat my latest changes have this entire suite of tests failing on off-by-one errors where there's always one more fasta than what is expected. Any idea what's going on?

@soypat soypat Dec 9, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll take a deep dive look today! Sorry, was in the zone with another project yesterday

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm hesitant to say EOF ended fastas are OK. This means you can't use io.LimitReader to read fastas at all using poly API since its use will return incomplete fastas without warning. The reason this is important to me is because io.LimitReader is a commonly used foundation for writing software resilient to adversarial user input.

Though I'd also like users who want to parse EOF ended fastas to be able to use poly. This is possible since we can return a valid fasta alongside an io.EOF error when the fasta is immediately terminated by an EOF. This will allow users who need this functionality to build their own parsers who treat the EOF case specially.

Note: ParseNext now (correctly?) parses data/empty.fasta, returning a single fasta.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe we can add a boolean flag to the ParseN, ParseAll, ParseBytes: acceptEOFTerminatedFastas?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe we can add a boolean flag to the ParseN, ParseAll, ParseBytes: acceptEOFTerminatedFastas?

This feels a little clunky but may be the way to do it. Should this variadic or explicit?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note: ParseNext now (correctly?) parses data/empty.fasta, returning a single fasta.

Just made empty.fasta truly empty. It's meant to test the corner cases I just commented on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This feels a little clunky but may be the way to do it. Should this variadic or explicit?

Maybe we can have parser options? Something like this https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis? Or maybe that's too much- just a struct with fields?

Comment thread io/fasta/fasta.go Outdated
Comment thread io/fasta/fasta.go Outdated
Comment thread io/fasta/fasta.go Outdated
Comment thread io/fasta/fasta.go
@soypat

soypat commented Dec 11, 2022

Copy link
Copy Markdown
Contributor Author

I removed empty.fasta since it can be represented as a const string

@TimothyStiles
TimothyStiles merged commit 9fbdee2 into bebop:main Dec 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants