Building data structures that are smaller than an array and faster (in C)
siganakis.com
siganakis.com
I use wavelet tree heavily in my job. They are very useful for compact representations of order preserving collections of strings which supports fast lookups.
Update: Sux4j has some interesting implementations of fast select/rank:
http://sux4j.dsi.unimi.it/docs/it/unimi/dsi/sux4j/bits/Selec...
I'll do a follow up with the rank / select optimizations.
Thanks!
I'm interested in what you use the WT for in your job... I'm just starting off my PhD in succinct data structures, so it's nice to know that people use them :P
I'm primarily using as a compact order preserving structure to support fast lookups for both (token -> position) and (position -> token). I'm using an updatable variant to support O(1) appends, and then use it for realtime indexing/compression. I probably should do a blog post at one point...
Would be interesting to hear about faster ways to do rank/select without using support structures... I'll keep an eye on your blog :-)
not much content, though..
That's very interesting, are you using raw or compressed bitmaps (such as RLE or RRR)? A limitation of (updatable) wavelet trees is that you need to know in advance the set of symbols that you will see (otherwise you would need to change the tree structure). Is it a problem for you? I wrote a paper with a solution to this problem, I can't share it until mid march, let me know if you are interested.
I've been thinking of writing some blog posts myself for a long time, now I think I will just link to yours.
EDIT: I think WTs should really have a page on Wikipedia...
I don't have a blog yet, I was thinking of opening a blog and then write those posts... :)
If you are using C++ you can have a look at my library of succinct data structures:
https://github.com/ot/succinct
It uses the same algorithms as Sux4j for rank/select (https://github.com/ot/succinct/blob/master/rs_bit_vector.hpp) but it has exhaustive unit tests and the resulting files can be memory-mapped instead of loaded in memory, which has a number of advantages.
I'm also going to release soon an optimized implementation of wavelet trees (actually a more generic variant that we are developing).
For example this: "Use less memory than an array in all circumstances" is just plain impossible if your data distribution is uniform. You just cannot store 1000 32 bit random intergers (preserving their order) in a smaller place than 1000*32 bits; (it comes from from simple combinatorics considerations.)
With custom compression, where there's a will there's generally a way :)
int32_t big_array[2^32] = {0};
/* returns the key */
int64_t write(int_32 value)
{
big_array[value]++;
return big_array[value] << 32 & value;
}
/* returns the value */
int32_t read(int64_t key)
{
return (int32_t) key;
}
/* all indexes between 1 and the return value are valid higher half values
for the key. The lower half is the value itself. */
int32_t seek(int32_t value)
{
return big_array[value];
}
memory: 16GBaccess: O(1)
seek: O(1)
Problem solved!
:)
edit: I'm missing the "Use less memory than an array in all circumstances" requirement, but hey, you talk about "billion of records" in your post.
The author rewrites the initial values as:
1 0 2 0 2 3
He defines the keys array as:
2 3 4 6
Using the method defined in the article to find the values of each of the elements of the rewritten array, wouldn't the values be:
3 2 4 2 4 6
which does not equal the original values of:
3 2 4 6 2 6
The translated array should be:
1 0 2 3 0 3.
I am correcting the article now. For a non-rushed, accurate description please look at:
http://www.alexbowe.com/wavelet-trees
Edit: Got it wrong again... I guess this is why we have computers and debuggers!
rewritten data: 1 0 2 3 0 3
first row : 0 0 1 1 0 1
group a : 1 0 1
group b : 2 2 3
Group a and group b don't seem to match up with the data. Or I've misunderstood something.General intro: http://crd-legacy.lbl.gov/~kewu/ps/LBNL-2164E.pdf
"Word Aligned Hybrid" compression: http://crd-legacy.lbl.gov/~kewu/ps/LBNL-49627.pdf
Bit maps as alternative to inverted index: http://crd-legacy.lbl.gov/~kewu/ps/LBNL-61768.pdf
Bit maps for range queries: http://crd-legacy.lbl.gov/~kewu/ps/LBNL-60891.pdf
Also the "Word Aligned Hybrid" method of RLE compression is patented. http://www.freepatentsonline.com/6831575.html
Patents are thorny, and it's generally not recommended that developers read them: willful infringement equals treble damages, caveat lector. The license may make better reading <http://crd-legacy.lbl.gov/~kewu/fastbit/src/license.txt>. Search for "software patents pose a constant threat to the existence of any free program".
If it remains a concern, and realizing that any other scheme you choose is also likely to be encumbered, you may be able to substitute a different compression such as PForDelta. The important part is cache awareness and branch prediction -- this is likely where your wavelet code is falling short.
Good luck!
The intuition is that you don't need granular memory addressing for storing large chunks. You only need to have addressability at the min(sizeof(doc)) level. So you can usually store the key in a 32bit int or less.
More detail can be found here: https://gist.github.com/1947190
How often are you updating? What is reading from this data structure? What's the query structure?
If you're adding lots of records often, I'd go for a B+tree. There's also a trie (and a B-trie), if your keys share lots of common prefixes.
If you are making consecutive queries, then you'd do well to take advantage of what pages are in L1 and L2 cache, but if you're randomly picking things, that's less important.
These structures are read only. I believe that there is some work on making updatable Wavelet trees but I think that this would be very expensive.
Reads are primarily Seeks (maybe 70%) I think, but Access is also important - so the order must also be encoded.
Storing keys and values is expensive in terms of memory (storing everything twice) so what we are trying to do is to manipulate the data such that we can do these operations quickly while using the minimum number of bits.
Edit: Fixed iPhone related spelling / grammar.
)