Concept

Rank query — where it appears

How many times a given symbol occurs before a given position, the primitive a compressed index performs in place of comparing two characters. It is the primitive a compressed index performs in place of comparing two characters, so a backward search makes zero character comparisons and many of these.

Named by 20 essays across 6 fields — each of them below, with the objects they name alongside it.

0$abracadabra1a$abracadabr2abra$abracad3abracadabra$4acadabra$abr5adabra$abrac6bra$abracada7bracadabra$a8cadabra$abra9dabra$abraca10ra$abracadab11racadabra$aball 12 rotations of "abracadabra$", sorted0 character comparisons

A search that runs backwards

Twenty-three occurrences of a six-character pattern in sixteen thousand characters, found in twelve rank queries and zero character comparisons. Not few comparisons — none. The algorithm never asks whether two symbols are equal, and it knows how many matches there are before it has located one.

indexes · Index
a text that repeats itselfH0 4.01 · H3 0.236.291.48an order-1 sourceH0 3.00 · H3 0.895.151.67English-likeH0 3.89 · H3 0.976.162.25four symbols, uniformH0 2.00 · H3 1.994.102.59eight symbols, uniformH0 3.00 · H3 2.835.143.65bits per character of textupper bar: plain bit vectors · lower bar: compressed16,384 characters each · sample rate 64sigma 21, 8, 21, 4, 8

The index that is smaller than the text

The Burrows–Wheeler transform is a permutation, so it changes no symbol frequency and a plain index over it is the same size whether the text has deep structure or none — 6.29 bits a character against 6.16, on texts whose third-order entropies differ fourfold. What the transform changed was the runs, and a structure that charges one bit per bit cannot see a run.

indexes · Index
balanced, bits49,2616.01 b/chHuffman-shaped, bits39,0614.77 b/chranks per access5.003.95entropy = 3.90 bitsEnglish-like, sigma = 22 · upper bar balanced, lower Huffman-shapedH0 = 3.90 bits/symbol

Rank is the only thing it does

Constant time and o(n) extra space — a phrase true of a rank directory costing 163% overhead and reading three words, and equally true of one costing 3% and reading eighteen. Both numbers are decided by two integers somebody typed into a header, and the phrase names neither.

machine · Index
100,000110bits held by the whole indexLF steps per located occurrence1 in 11 in 21 in 41 in 81 in 161 in 321 in 641 in 128one point per sampling rate · 66 occurrences located each timeEnglish-like

The text that does not have to be kept

The index reproduces its text character for character, in 1,024 mapping steps and zero reads of anything. That is the whole justification for weighing it against the text rather than beside it — and the price is a dial that moves the structure by 3.3 times and the cost of locating one match by 72.

space · Index
patterns of 225%60 selectspatterns of 441%94 selectspatterns of 863%118 selectspatterns of 1682%117 selectsEnglish-like · 8 copies of 512share of steps needing no lookup40 patterns a row

The occurrence carried through the search

Backward search returns how many and not where, and every index on this site pays for the second question separately. Carrying one occurrence along with the interval costs a lookup on 75% of the steps for a two-character pattern and on 18% of them for a sixteen-character one, and it is what makes a run-boundary sampling usable at all.

indexes · Index
10010³10⁴errors allowed, kacts0123index walkthe whole table3,000 characters · m = 20 · 4 symbolsno crossing in range

The search that spends a budget

A backward search narrows one interval per pattern character. Give it a budget of three errors and it narrows 39,943 of them instead, finds every occurrence the whole table finds, and reads not one character of the text — 177,046 index ranks against 60,000 table cells and zero characters examined.

indexes · Distance
atgaattcatgagtgacaag00000111111112222222errorsthe pattern, left to right · D belowleast errors neededD, the bound4 symbols · m = 2090 ranks · 2 resets

The errors the rest of the pattern needs

Read the pattern left to right in an index of the reversed text and count the points where the interval empties. That count is a lower bound on the errors any alignment of the prefix must contain, it costs 72 rank operations, and it removes 70% of a search tree.

text · Distance
2·0·1·20101206·7·3·25·012012201012102rowsdocumentfirstpattern " was t" · 30 occurrences · 7 documentsA filled row is the first of its document. Reporting one costs a range minimum;reading every row costs one visit per occurrence.English-like · 8 documents30 rows · 7 documents

A list of documents is not a list of occurrences

A pattern occurring 790 times in eight documents has an answer of size eight. Reading every occurrence to find out costs 790 array reads; the question a collection has that a text does not is the one its index does not answer.

indexes · Document
rank among the boundaries, sorted by the text before themsorted by the text after0 in the rectanglepattern "ss is un"split after 40 end with the left half0 begin with the rightEnglish-like · 2 copies of 512z = 156 · 0 crossing

A rectangle over a permutation

Two orderings of one set of boundaries are two permutations, so a phrase index's intersection is a rectangle over a permutation grid — the one point set a wavelet tree stores exactly, at one bit per point per level and no coordinates at all.

structures · Grid
both halves locate250,584 · 100.0%the reverse half counts only226,504 · 90.4%and its marks are Elias-Fano210,934 · 84.2%and its levels are block-coded167,707 · 66.9%the saving spent on one in eight198,258 · 79.1%protein text, 16,384 characterseach change on top of the last

The part every saving left alone

A bidirectional index was made smaller three times, one part at a time, and every change left the largest part untouched: the wavelet trees, whose levels are two thirds of the structure. Coding every level in blocks of 63 bits — how many ones a block holds and which arrangement it is — takes that protein index from 210,934 bits to 167,707, a larger step than the two before it together. On English words it takes the same structure from 210,872 bits to 76,054, and an ordered tree, coded, ends up smaller than an unordered Huffman tree, plain. Every rank then reads 1.4 times as many entries, and a smaller superblock buys the reads back for part of the bits.

text · Distance
candidates, filtering4,355candidates, grid33rank and select, grid2,471occurrences32one query · same collection · same answer16,384 characters · 32 occurrencescandidates 132x · operations 1.76x

The operations a candidate count leaves out

The grid examines 33 candidates where the scan examines 4,355 — a factor of 132. Counted in the operations each of them performs, the same query is 2,471 against 4,355, and the factor is 1.8.

wrong · Grid
six bits a classone entropy coderuns of a class0.2870.2770.258DNA0.4810.4610.535protein0.4800.3440.323English0.4800.2660.295repetitivebits a character, classes of both trees, per halfclasses, with their pointers16,384 characters a text

Six bits a class

Coding a bidirectional index's wavelet-tree levels in blocks left a part that did not shrink: six bits a block for the block's class, its count of ones, which on English words became a quarter of the levels. Coding the classes by their runs was predicted to halve them. It takes a third off, 0.480 bits a character to 0.323, because two thirds of the blocks are uniform but they come in stretches of about two. One entropy code over the classes does about as well, and on the repetitive collection better. Runs also cut the reads a rank makes by 15%. On random letters runs make the classes larger, and the pointer a variable-length stream needs at every superblock eats most of what an entropy code saves.

text · Distance
the arrangement's indexthe gaps between changesthe shorter, behind a flagEnglish1.2771.3251.008repetitive0.4680.2570.213DNA1.8842.6281.919protein4.0865.5924.15616,384 characters, both treesbits a character of text

A flag for the blocks that change once

A coded wavelet-tree level stores each mixed 63-bit block as the index of its arrangement, as if every arrangement were equally likely. Coding it instead as the gaps between its value changes was predicted to take a third off the offsets on English words. Alone it adds 4%: a third of the mixed blocks change value once, as predicted, but most of the rest change twenty times or more. A one-bit flag that lets each block take the shorter code takes 21% off, and the levels fall from 1.72 bits a character to 1.45. On the repetitive collection the offsets halve.

text · Distance
01234block length, bits (log scale)levels' bits a character1531631272555111,023English words, as first builtrepetitive collection, as first builtEnglish words, runs and the flagrepetitive collection, runs and the flagdashed: a class a block, arrangements only16,384 characters, both trees

The optimum the flag took away

A bidirectional index's wavelet-tree levels were coded in blocks of 63 bits because a sweep found 63 the best length: shorter blocks pay for more classes, longer ones for offsets that approach the block's own length. Then the classes were coded as runs and each offset given a flag to choose a code of its value changes instead. Lengthening the blocks to 127 bits was predicted to save on English words and the repetitive collection and to change nothing on random letters. It saves on all four texts, and there is no best length short of 1,023 bits on three of them: English words fall from 1.431 bits a character at 63 to 1.304 at 1,023. Under the codings the optimum came from, the same levels cost 3.728 at 1,023. The flag turned a long block's offset into the cost of its change points, which no block length moves, and left the length to be set by the one thing still growing with it: the work of a rank.

text · Distance
816320.5000.6000.700work a rank (log scale)levels' bits a character31631272555111023lengths, price 0faint: each length pricedthe repetitive collectionlower and left is better

A flag that weighs the work as well as the bits

A wavelet-tree level's mixed blocks are each coded by their arrangement's index or by the gaps between their value changes, a flag choosing the shorter, and longer blocks then save bits on every text at the price of more work a rank. Charging the flag for decoding work as well as bits was predicted to reach the same curve as shortening the block, and to beat it by no more than a hundredth of a bit a character. On English words, DNA and protein it reaches nothing below that curve. On the repetitive collection it reaches 0.156 bits a character below it: 1,023-bit blocks with a price on work cost 0.458 bits a character for less work a rank than 63-bit blocks do at 0.607. The difference is in which blocks the flag codes by their arrangement — on English words blocks that change value ninety-six times, on the repetitive collection blocks holding one short run of ones in two hundred and fifty-five bits.

text · Distance
1632641.401.601.802work a rank (log scale)levels' bits a character31631272555111023block lengths aloneone price, every levela price for each levelEnglish words, 16,384 charactersfrontiers: nothing beats them on both

A price the levels share

A bidirectional index's wavelet-tree levels code each mixed block by its arrangement or by its gaps, and a flag that charges each code for the work a rank costs inside it reached below the curve the block lengths draw on one text of four. Setting that price separately for each level of the tree was predicted to find a few hundredths of a bit on English words, where the deep levels hold runs. It finds nothing on any text: under a ten-thousandth of a bit beyond one shared price. The price a block wants is set by the text, not by the depth of the node it sits in, and every level holds a fifth of the index.

text · Distance
2411.101.201.301.40in-block work a rank of the workload (log scale)offset bits a characterpriced on every positionpriced where ranks landEnglish words, common patternschosen on one seed, charged on another

The blocks the searches ask for

A flag that codes each block of an index's wavelet-tree levels as an arrangement or a gap code, charged for the work a rank does inside the block, was priced as if ranks landed on every position equally, and a price for each level found nothing that one price did not. Pattern searches do not ask evenly: on English words the busiest tenth of the blocks takes 22% of their ranks, and 35% when common patterns are asked more. Priced by the work of the ranks that actually land in each block, chosen on one sample of searches and charged on another, the flag reaches 0.11 to 0.16 bits a character below the old pricing at the same work on English, and 0.03 on the repetitive collection.

text · Distance
English69% of the busiest tenth shared35% of the first half's ranks29% of the second'srepetitive88% of the busiest tenth shared27% of the first half's ranks26% of the second'seach row: the busiest tenth shared · its share of the first half's ranks · of the second'sblock length 255the busiest tenth of blocks

A layout the traffic does not move

A bidirectional index whose bit vectors code each block as an arrangement or a gap code saved bits at equal work once the choice was priced where a workload's ranks land. Traffic changes, so a layout fitted once was predicted to lose most of that gain when the searches moved from one half of a text's commonest patterns to the other's, and an index that recoded its blocks every thousand searches from their own counts was predicted to keep it. Neither happened, because nothing needed to be kept. Priced on the first half's patterns and never changed, the layout does 8.24 units of in-block work a rank on the second half's, 1% above a layout refitted to them for free; recoding every thousand searches rewrites 48 of 385 blocks, 0.71 bits a character, and ends no better. The busy blocks are where the text's common substrings sort, and both halves share them: 27 of the 39 busiest blocks under one half's patterns are among the 39 busiest under the other's.

text · Distance
5.8066.206.406.60567891011log₂ bits a select readsbits a valuet 256t 64t 16s 1024s 512s 256s 128s 64select samples, t valuesrank samples, s bitsdivergence 0.01a model of reads, not a timing

A sample that has to be found

A select sample every 64 values inside the cheapest partition of an r-index's run-start values cut a select's reads from 1,065 bits to 111 on exact copies. Samples by count bound how many values a select passes, not how many bits it reads, so samples every 128 bits of a chunk's vector were predicted to read a third less on the sparse, drifted sets at the same cost. They bound the scan as promised, but a rank sample does not say which sample a select needs, and finding it by binary search costs a select up to a third of what it reads. The two kinds of sample end up on one frontier, and the rank samples' advantage is that they come in finer steps.

space · Space
3.203.403.603.8056789log₂ bits a select readsbits a valuet 64, s 64two levelsselect samples alonerank samples aloneexact copiesa model of reads, not a timing

A second level that pays in one band

A select on a chunked bit vector can be helped two ways: a select sample every t values says where to start but not how far to scan, and a rank sample every s bits bounds the scan but must be searched for. A directory with both — select samples that name a rank block, rank samples that bound the scan — was predicted to reach under 70 bits read a select for under 3.6 bits a value on an r-index's run starts over exact copies, a point neither kind reaches. It reaches 62 bits read for 3.76 bits a value, which neither kind reaches, and costs 3.73 within 70 bits where the better single kind costs 3.93; but under 3.6 bits a value it reads 84. The select sample names a block two rank samples away, not one. Across eight sets the second level saves more than a twentieth of a bit a value on three, and only between about 65 and 100 bits read a select: outside that band either single kind is as good.

space · Space

Named alongside it

The objects these essays reach for when they reach for this one.

Index sizeWavelet treeCompressed bit vectorMeasurementBidirectional indexSelf-indexEntropyPredictionSpace accountingBackward searchBit vectorBurrows-wheeler transform

All concepts