The index that replaces the text

The index that does not notice

Three compressed indexes over the same characters. One is flat at six and a half bits a character however many copies the collection holds; the other two fall by factors of five and six. At one copy the two that fall are the largest of the three.

Three structures in this field answer the same three questions — how many occurrences, where are they, and what does the text say — and their sizes are proportional to three different quantities.

The FM-index stores the Burrows–Wheeler transform in a wavelet tree of bit vectors, so its payload is n·H₀ of the transform. The run-length index stores the transform as heads and lengths, so its payload follows r. The phrase index stores a Lempel–Ziv parse and three orderings of its boundaries, so its payload follows z.

Those are three different functions of one text, and on most texts they do not order the same way. This plate is what happens when the text is held fixed and the arrangement is swept.

One index does not notice the repetition and two of them are made of itThree compressed indexes on 8,192 characters, against how many near-copies the text is made of — which in this cell is also the document count, because each document is one copy. The entropy-bounded FM-index is flat at about 6.4 bits a character throughout: the transform is a permutation, so its zeroth-order entropy is the text's, and no amount of repetition changes it. The run-length index falls from 18.5 to 3.4 and the phrase index from 9.1 to 1.6. At one copy the two repetition-aware structures are the LARGEST of the three, by a factor of 2.88x; the crossing is what decides which structure a collection wants, and it is a property of the collection rather than of the field.051015204060documents, which here is also the copy countbits a characterFM-indexrun-length indexphrase index8,192 characters throughout2665 runs down to 427
Fig. 1 Bits a character for three indexes over the same eight thousand characters, against how many near-copies the collection is made of — which in a version history is also the document count.

The flat line

The FM-index reads 6.43 bits a character at one copy and 6.38 at sixty-four. That is a change of eight tenths of a per cent across a collection that has become sixty-four times more repetitive.

The reason is the entropy that cannot see a copy, and it is not a limitation of an implementation. The Burrows–Wheeler transform is a permutation of the text. A permutation changes no character’s frequency, so it changes a zeroth-order entropy by nothing at all — measured there to fifteen decimal places. A wavelet tree of plain bit vectors, shaped by the character frequencies, costs n·H₀ of the sequence it holds. So the FM-index costs n·H₀ of the text, and duplicating a text leaves H₀ exactly where it was.

The small drift that does appear is the directories and the C table, which are o(n) terms whose constants shift slightly as σ changes. Nothing in the structure is proportional to anything the copying moved.

This is worth stating plainly because the transform is routinely credited with the compression in this family. It is credited with the wrong compression. What it does is gather characters that share a right context, which turns a text’s higher-order structure into runs — and a plain bit vector charges one bit per bit whatever is in it, so those runs cost exactly what the scattered version cost. The transform’s gathering is worth something only to a structure that charges less for a run than for a change, which is the next two lines.

There is a version of this structure that does benefit, and naming it sharpens what the flat line is measuring. Replace the plain bit vectors with block-classed ones — the representation that stores each block of b bits as its class and an offset within that class — and a level that is nearly all zeros becomes nearly free. The transform’s gathering then does pay, because a gathered level has long runs and a run’s offset costs nothing.

That structure is in this collection and it is measured: rank is the only thing it does is where the block representation was priced, and it reaches the text’s higher-order entropy rather than its zeroth. What it does not reach is repetition. A collection of sixty-four copies has the same conditional entropies as one copy — the copies do not make the next character more predictable given the previous few — so the compressed-block index falls a little and then stops, and its line on this plate would sit between the flat one and the two that fall, much closer to the flat one.

Three levels of a hierarchy, then: zeroth-order entropy, higher-order entropy, and repetition. The first two are properties of a text’s local statistics and the third is not, and no amount of the first two implies the third.

The two that fall

The run-length index goes from 18.5 bits a character to 3.4 — a factor of 5.5. The phrase index goes from 9.4 to 1.6, a factor of 5.7.

Both are following the measures what repetition is worth once the logarithm is gone sweeps: r/n falls by 3.4 and z/n by 4.7 over a comparable sweep, and the index sizes fall slightly further because the width of each stored item also shrinks as the counts do — a run head’s index needs ⌈log₂ r⌉ bits and r is falling.

What the two lines do not do is cross each other. The phrase index is smaller than the run-length index at every point of this sweep, by a factor between 2.0 and 2.2, and the gap is remarkably stable. That is a fact about this collection rather than about the two families: a version history with a small divergence produces long phrases and long runs in roughly fixed proportion, so the two measures move together.

On a genuinely repetitive collection the fall is the collectionPhrases per character against size, with the axis being the copy count at a fixed base of 2,048 characters — so the arrangement changes and the alphabet does not. The pale line is a shuffle of the same characters: same length, same alphabet, same zeroth-order entropy, no arrangement. It falls by 1.45x across the sweep, which is the logarithm every text pays because z/n is about one over log_sigma n. The collection falls by 4.71x, so what the repetition is actually worth is 3.24x. On real prose the same two-ended measurement left 1.37x; here the control accounts for 30.8% of the raw curve rather than for most of it, which is the difference between a collection that repeats and one that is merely predictable.10⁴0.1charactersphrases of the parse, per charactershuffled: 1.45xthe collection: 4.71x4,111 to 65,551 charactersworth 3.24x after the control
Fig. 2 The measures the two falling lines follow, with a shuffled control that separates the arrangement from the arithmetic.

The stability of that ratio is worth one more sentence, because it is the kind of thing that looks like a law and is not. r and z are related — a text with few phrases has few runs — but the relationship is loose, and there are texts where they diverge sharply. A periodic text has a tiny run count and a phrase count that grows with the number of periods; a text built from a Fibonacci word has both small but in a different ratio. The measure that cannot see the alphabet is where the parse’s own blind spots were measured, and it is the reason a plate showing the two moving together should be read as a fact about this collection.

The crossing that decides

The important feature of the plate is at the left-hand edge, and it is the one that inverts the usual presentation.

At one copy — an ordinary text with no duplication in it — the run-length index is 18.5 bits a character against the FM-index’s 6.4. It is 2.9 times larger than the structure it is offered as an improvement on. The phrase index is 9.4, which is 1.5 times larger.

Both of the repetition-aware structures are the wrong choice on a text that does not repeat, and they are the wrong choice by a wide margin rather than a marginal one. The collection decides which index is small made that argument on one long text; it is unchanged by folding in the document count, and it is worth restating because the literature’s framing does not survive it.

The framing is that these are compressed indexes and the FM-index is the entropy-bounded one, as though the first were a strictly stronger claim. It is not a stronger claim; it is a different one. A structure proportional to r has no bound in terms of H₀ at all, and on a high-entropy text with no repetition r approaches n(1 − 1/σ), which is worse than n bits before the directories are counted.

The asymmetry is not only about size. The FM-index’s query cost is a constant number of ranks per character of the pattern, whatever the text is; the phrase index’s is a chain walk whose length is a property of the parse and is bounded by nothing. The character that costs a chain is that measurement, and it means the phrase index’s advantage in bits is paid for in a query-time distribution with a long tail. Choosing between them on size alone is choosing on one of two axes.

Where the crossings actually are

Reading the crossings off this sweep: the phrase index crosses the FM-index between one and four copies, and the run-length index crosses it between four and sixteen. Interpolating on the log scale puts the first near two copies and the second near seven.

Those are small numbers, and they are the reason these structures exist. A collection that holds a document and one revision of it is already past the first crossing. A collection with eight near-identical members is past both. Almost every archive of anything is past both, which is why the structures are worth building — and almost every single document is past neither, which is why they are not universal.

The four cells, and the two measures in eachRuns of the transform and phrases of the parse, per character, on 8,192 characters arranged four ways. The top row is prose and the bottom is 16 near-copies of one base; the left column is one text and the right is that text cut into 16 documents with a separator between them. Everything the repetition strand measured is the bottom left cell and everything the document strand measured is the top right; the bottom right is the one nothing here had filled. Cutting the repetitive text raises r/n from 0.0638 to 0.0768 — 1.20x — and cutting prose raises it from 0.2996 to 0.3053. Those two factors are the subject of the next plate, and they do not come out in the expected order.one text16 short documentsprosecopies of one baseprose, one textr/n 0.2996z/n 0.1273prose, many short documentsr/n 0.3053z/n 0.1301copies of one base, run togetherr/n 0.0638z/n 0.0354one copy per documentr/n 0.0768z/n 0.04188,192 characters throughoutthe outlined cell is the one that was empty
Fig. 3 The four cells the sweep sits in, with the two measures in each. The left column is one text; the right column is that text cut into documents, which moves the measures by a couple of per cent.

A collection sitting near a crossing is the awkward case and it is worth saying what to do there. Near two copies the phrase index and the FM-index are within a few per cent, and the choice should be made on the query profile rather than on the size: the FM-index answers a count in m ranks and the phrase index answers it by a boundary search with a chain walk behind it. Near seven copies the run-length index joins them, and the same reasoning applies with the additional consideration that its sampling term is the part that has not shrunk.

What the document count does to all three

There is a second axis in this cell and it is nearly invisible on the plate, which is itself the finding.

In a version history the document count and the copy count are the same number, so the sweep above moves both. Separate them — cut the same copies into a different number of documents — and the index sizes follow the copies and not the documents. Cutting sixteen copies into a hundred and twenty-eight documents raises the run count by two per cent and the phrase count by three, which is a movement of a few tenths of a bit a character in structures spanning fifteen.

A boundary that costs nothing is where that measurement is made and the price of a boundary is what precedes it is why. The consequence for this plate is a warning about reading it: a sweep of the document count on a versioned collection is sweeping the copy count, and a reader who takes the x-axis at face value will conclude that fragmenting a collection makes its index smaller. It does the opposite, slightly, and the large effect is entirely the copies.

The parts, and which one is the size

It is worth opening the two falling structures once, because their sizes are dominated by different things and the domination changes across the sweep.

The phrase index at eight copies holds a length, a source and a literal per phrase — that is z(2⌈log₂ n⌉ + ⌈log₂ σ⌉) — and three permutations of its z boundaries, at z⌈log₂ z⌉ each. On this collection the parse accounts for 55% of the structure and the three orders for 45%. Half an index is three permutations is where that split is measured and where one of the three turns out to be the inverse of another.

The run-length index holds the run heads in a wavelet tree, the run lengths, a sparse bit vector over the run starts, and a suffix-array sampling. At one copy the sampling is a small share and at sixty-four it is the largest single part, because the sampling is n/s values and n has not fallen while everything else has. That is what is still proportional to n, and it is the term the r-index’s run-boundary sampling exists to remove.

A separator each is paid in the alphabet, not in the runsThe three joins on a collection of 32 near-copies. Running the documents together costs nothing and invents strings no document contains. One shared separator raises the alphabet from 21 symbols to 22 and the runs by 1.00x — which is to say by nothing. Giving each document its own raises the alphabet to 52, which crosses a power of two and so costs a whole bit on every character of every document: 20.2% on the packed text. It costs 2.9% in runs. The two currencies disagree by more than an order of magnitude, and a collection that needs distinct boundaries should be told which one it is paying in.run together1.00xsigma 21 · 5 bitsone separator1.00xsigma 22 · 5 bitsa separator each1.20xsigma 52 · 6 bitsruns 1.00xruns 1.00xruns 1.03x32 documents of 512 characterspacked bits, against running them together
Fig. 4 A cost the three structures do not share: what a document boundary marker does to the alphabet and to the run count, which two of the three charge for differently.
Repetition does not give the listing apparatus anything to doOccurrences per document for two patterns, in three collections of 32 documents. The pale bar is the collection's own most frequent 6-character string and the dark bar is a string drawn from one of its documents, which is what a reader asks for. On the drawn pattern all three cells read within a tenth of each other — prose 1.87, one copy per document 1.84, copies cut across documents 1.88. In a collection of versions the copies and the documents are the same partition, so a phrase of the base occurs once in each of d documents: d occurrences and d documents, and a ratio of one. The apparatus needs a pattern that repeats INSIDE a document, and repetition of this kind does not provide one.prose, many short documents2.38 most frequent1.87 drawn from a documentone copy per document4.78 most frequent1.84 drawn from a documentcopies cut across the boundaries2.91 most frequent1.88 drawn from a documentoccurrences per document holding the pattern32 documents · 6-character patternsall three within a tenth on the drawn pattern
Fig. 5 A property none of the three structures charges for, and which decides whether a fourth apparatus is worth adding to any of them: how often a pattern occurs in each document holding it.

There is one more part that none of the three sizes above includes, and leaving it out is a choice rather than an oversight. A collection of documents usually wants to know which document an occurrence is in, and that is a document array plus whatever apparatus reads it — measured separately in this field because it sits on top of any of the three. Its size is a function of the document count and the length distribution, not of the repetition, so it is the one part of a collection’s index that a version history makes worse rather than better: more copies means more documents means a wider document array.

At sixty-four documents that array is a few thousand bits against a run-length index of about twenty-seven thousand, so it is not decisive. At the document counts a real archive has it can be comparable, and the document array is the length distribution is where its own compression is measured.

A collection can be measured before it is indexed

The practical form of all this is that the choice does not require building three indexes.

Compute r and z on the collection — both are linear-time and both are cheap relative to constructing anything — and compute H₀ from the character frequencies. Multiply n·H₀ for the first, and roughly r(2⌈log₂ n⌉ + ⌈log₂ σ⌉) and z(2⌈log₂ n⌉ + 3⌈log₂ z⌉) for the other two. The three numbers order the same way as the built structures do, to within the directories.

That is not a new observation, and the reason it is worth making here is the shuffled control. A collection reporting a low z/n may be reporting a logarithm rather than repetition, and a structure chosen on the uncorrected number will be chosen on an artefact. The correction is one shuffle, and on a collection where it matters it is the difference between 4.7 and 3.2.

Reading the plate as a decision rather than a description

The plate has four points on it and a reader who has a collection in hand can locate themselves on it in about a minute, which is the whole of what it is for.

Take the collection, run a Lempel–Ziv parse and a Burrows–Wheeler transform on it, and compute z/n and r/n. Shuffle it and do the same, to get the control. Divide. A corrected ratio near one means the collection is at the left-hand edge of this plate however large it is, and the entropy-bounded structure is the right one. A corrected ratio of three or more puts it at the right-hand edge, and the two falling structures are between two and four times smaller.

The middle is where judgment is needed, and the reason is that the plate’s x-axis is a construction rather than a property. A collection is not really “eight copies of a base”; it is a collection whose repetition happens to be worth about what eight copies would be worth. Two collections at the same corrected ratio can differ in the shape of their repetition — many short duplicated stretches against a few long ones — and r and z weigh those differently. A collection whose duplication is many short stretches has a phrase count that falls slowly and a run count that falls fast, and its point on this plate is two points.

That is the honest limit of a two-line summary of a collection, and it is why the measurement is quick rather than automatic. What the plate settles is the large question — is this collection at the left edge or the right — and that question is settled by the corrected ratio, which no amount of shape can disguise.

On a genuinely repetitive collection the fall is the collectionPhrases per character against size, with the axis being the copy count at a fixed base of 2,048 characters — so the arrangement changes and the alphabet does not. The pale line is a shuffle of the same characters: same length, same alphabet, same zeroth-order entropy, no arrangement. It falls by 1.30x across the sweep, which is the logarithm every text pays because z/n is about one over log_sigma n. The collection falls by 3.21x, so what the repetition is actually worth is 2.48x. On real prose the same two-ended measurement left 1.37x; here the control accounts for 40.3% of the raw curve rather than for most of it, which is the difference between a collection that repeats and one that is merely predictable.10⁴0.1charactersphrases of the parse, per charactershuffled: 1.30xthe collection: 3.21x8,207 to 65,551 charactersworth 2.48x after the control
Fig. 6 The same sweep on a four-symbol alphabet, where the control falls faster and a larger share of the raw curve is arithmetic.

The alphabet matters to that procedure and it is easy to forget. On four symbols the control’s own arithmetic falls faster with size, so a rule of thumb calibrated on English overstates what a DNA collection’s repetition is worth. Every structure on this plate is used on DNA more than on anything else, which makes it the case where the correction is most needed and least often applied.

What the flat line is good for

Ending on the FM-index’s flatness as a defect would be the wrong reading, and it is worth closing that off.

A structure whose size is n·H₀ has a guarantee. It is that size on every text, and the size can be computed from the character frequencies before anything is built. The two falling structures have no such guarantee: their sizes depend on a property of the arrangement that has no bound in terms of anything simpler, and on an adversarial text they are worse than storing the text.

So the three structures trade a bound against an opportunity. The FM-index takes the bound and cannot benefit from repetition. The other two take the opportunity and have no bound. A collection that knows what it holds should take the opportunity; a service indexing whatever arrives should think carefully about the bound, because the worst case is not a rounding.

The separators sort first, so their rows hold the documents' last charactersWhy the cut is nearly free on a repetitive collection. A separator sorts before every ordinary character, so its rows sit at the top of the suffix array — and the transform characters in those rows are the LAST characters of the documents. Those characters form one block of the transform, and a block costs one run per change inside it. At 128 documents a repetitive collection cut at one offset of near-identical copies ends with 6 distinct characters and prose ends with 18. The runs follow: 19 added against 293, which is 0.15 a separator against 2.31. The price of a boundary is the entropy of what precedes it.051015255075100125documentsdistinct characters the documents end with+7+52+162+293+4+-1+9+19prosecopies of one basethe label is the runsthe cut added128 documents0.15 against 2.31 runs a separator
Fig. 7 The quantity that decides how much the document boundaries cost the two run-based structures: how predictably the documents end.
Cutting a repetitive text into documents costs it almost nothingRuns per character against the document count, on one fixed text of 8,192 characters cut more and more finely — so nothing moves but the number of separators. The expectation this measurement was made against is that a separator breaks a run, which would make a repetitive collection pay for its document count in the measure it exists to be small in. It does not. Across seven doublings the repetitive line rises by 1.02x and the PROSE line rises by 1.10x — the opposite ordering. In absolute terms the same 127 separators add 19 runs to the repetitive text and 293 to prose. The reason is not in this plate; it is in the characters the documents end with.1101000.1documentsruns of the transform, per characterprose: 1.10xcopies: 1.02x8,192 characters, cutthe control rises further
Fig. 8 The axis this plate is often confused with: cutting a fixed text into more documents, which moves the run count by two per cent against the copy count’s factor of three.

That framing also explains why hybrid structures exist and why this collection has not built one. A structure taking the minimum of the two — an entropy-bounded index with a run-length representation of the levels whose bit vectors have long runs — gets the bound and most of the opportunity. It is the obvious next thing and it is a different strand.

What this makes readable

Essays that name this one as a prerequisite.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

What links here

Every essay whose body links to this one.

The objects this essay names

Each one links to every other essay that touches it.

Burrows-wheeler runsCrossing pointDocument collectionEntropyIndex sizePhrase countRepetitionSelf-indexWavelet tree