Optimizing CRISPR: Sub-second searches on a 3 billion base genome
benchling.engineering
benchling.engineering
BLAT[0] is the most obvious and preferred solution designed for alignment against a reference sequence- a 20 BP search against the human genome should essentially be instantaneous. BLAST [1] is more versatile and a bit slower than BLAT, but would also align these sequence sizes against the human genome in ~1-2 seconds, and is a traditional solution to the alignment problem, and has no license restrictions. BWA [2] and Bowtie [3] default settings could also be modified for the task (they're optimized for performing this task with larger strings on the order of thousands of times per second).
More generally, it would not be difficult to re-implement any of the algorithms behind these software implementations if the authors really wanted to. It's weird, this is the second post I've seen recently when software folks who are now working in the bioinformatics space have seemed completely unaware of both the basic algorithms we use in computational biology and their common implementations, like Smith-Waterman and Burrows–Wheeler. These are complicated problems with 40+ years of research behind them, and the actual solutions are elegant and fast algorithms which solve the problem in a far superior way within reasonable computational time.
[0] http://genome.ucsc.edu/cgi-bin/hgBlat
[1] http://blast.ncbi.nlm.nih.gov/
Bingo. And they use on the order of the genome size in RAM to do so.
The FM-index is a helluva sequence search machine.
Unfortunately, a lot of the existing software is not intended for the search we're trying to do, or does not perform well under these conditions. We did in fact experiment with some of them before building our own. Bowtie, for example, doesn't allow more than 3 mismatches, and is also intended for alignments where there are very few matches (close to 1).
Since we need to be able to support multiple genomes (see Josh's comment), the amount of RAM we need to run a particular set of alignments is relatively important. Things like BLAT (which seems also intended for > 25 bases) need to keep the entire genome index in memory. This means that we would need to spin up a lot more servers to handle parallel requests, especially with different genomes.
FWIW our search is only a couple hundred lines of C++, and does the search with very little memory requirements.
Maybe genomics doesn't have to be so memory hungry. 8)
Most software done by bio"informaticians" seems sub-par, both in implementation and in theoretical properties. I'd suspect this is caused by the fact that a lot of people actually don't come from CS, but biology or other fields.
https://en.wikipedia.org/wiki/Rabin–Karp_algorithm
Rabin karp with rolling hashes is actually (not exactly but almost) what tools like rsync, or bittorent use to find chunks and differences in files. So it scales really well.
The algorithms you cite come from a time when computers looked rather different in their architectures.
Linearly scanning the entire genome from ram for example, could be significantly better than performing multiple index lookups from disk.
Many problems of bioinformatics (the text processing) aren't that hard or special anymore, a genome is tiny compared to the amount of data we have lying around elsewhere.
Consider this: the patent was awarded to a group that could not have invested more than a few thousand dollars of incremental time and resources (in fact, probably the majority of the costs were in the patent application and process itself). And yet the license is worth billions.
Patents were created - and the US Patent Charter still states this - to encourage and enhance the economic stature of the nation. Instead we use patents to throttle it. Imagine if this patent were only good for a few years or up to license fee commensurate with the incremental investment needed to produce and validate the research (even if this fee were 3x, 5x, 10x, etc. of the costs). Everyone could contribute to the work and the pace of innovation accelerates. Instead we've got a couple universities (and, inevitably, follow-on corporate licensors) locking it down for all but the publicly funded and litigous.
There is so much opportunity out there, there are so many brilliant minds, eager innovators, and great startups. Why do we shoot ourselves in the foot with patent nonsense that hasn't been significantly rethought since (in the US) its 18th-century English law origins?
Greed.
The main reasons we decided against a GPU-based approach were cost and scalability:
- We support a dozen+ reference genomes (eg for difference species), and plan to support a lot more (including eventually supporting custom genomes that users provide). Assuming we want to support a few concurrent searches against the same genome, we'd need a few GPUs per genome, and this gets expensive pretty quickly on AWS.
- Our fleet is now non-homogeneous, and now if machine X fails we need to restore machine X' with the same set of genomes.
- If certain genomes are more popular than others, we'll likely have GPUs spun up that aren't being used much (only one lab might be investigating a certain genome, for example). I suppose you could swap genomes in and out of memory as they're accessed, but again it's more complex to manage resources.
- Our current approach allows us to add genomes ad-hoc - hypothetically, you could point us to your own genome on S3 and we'd be able to work with it.
We hint at it towards the end, but we're actually switching to AWS Lambda soon - based on early calculations, it could cost us as little as $50 a month to run everything!
First, try the bitap algorithm.
Second, you can encode the search as a DFA - look up the Aho–Corasick algorithm. Then just run the DFA over the genome. It means that you don't need to match every string at every position. If you've read AAAAA and your string starts with CCCCC with an edit distance of 4, you can skip ahead for a while before you need to start reading again.
Third, you could build a suffix tree (O(n) preprocessing), and then use the standard fuzzy string matching algorithm using suffix trees on it.
I'm really concerned that the team behind a bioinformatics tool is talking about searching sequences without even a mention of BLAST. It should have been solution #1!
We use the scoring function published by Hsu et al[1], which most scientists seem to be using. This function takes into account both the number of mismatches and where they occur in the guide. There's a more readable version here: http://crispr.mit.edu/about .
[1] http://www.nature.com/nbt/journal/v31/n9/full/nbt.2647.html
Both winning strategies, but I suspect you can push it much much further to scan much faster if you're hitting memory bandwidth (ACGT = 2 bit space).
Of all the big-data problems I see, nothing feels as close to a personal problem like genetic data.
I mentioned it in a comment below, but our constraints are a bit different once you start supporting multiple genomes - we could've been clearer about that in the post for sure.
I'd randomly generate it, but I don't know what the statistics should be - and that makes a huge difference for branch prediction / etc.
It should give you the same answer in 3 CPU instructions on registers instead of 2 array lookups and arithmetic in ram. XOR, hammond weight/bitcount and and equality check.
The final algorithm actually keeps track of the last 20 bases + PAM length, and checks both the edit distance and PAM before deciding if something is a match. The Benchling CRISPR tool will do this for you :)