The floors

What a reordering costs to undo

Sorting the characters of a text clusters them perfectly: a move-to-front pass then leaves 289 bits where the text's own floor is 31,931. Naming which arrangement of those characters the text was costs 31,827 bits, and the two numbers add to the floor it started from. The Burrows–Wheeler transform clusters less and costs nothing to undo, which is the only reason it is the one that is used.

The transform that emits nothing established that a permutation can lower a floor it cannot change. The Burrows–Wheeler transform outputs the characters it was given in a different order, so its zeroth-order entropy is identical to its input’s; put the output through a move-to-front pass and the entropy falls from 4.156 bits per symbol to 1.802, and the whole of the difference is in the order.

That result invites an obvious question and does not ask it. If clustering is what pays, why stop at the transform’s clustering? Sort the characters. A sorted string is perfectly clustered — one run per distinct symbol — and a move-to-front pass on it emits one non-zero rank per distinct symbol and zeroes for everything else. Its entropy is very close to nothing.

The answer is that the comparison was never between two streams. It is between two streams plus whatever each one costs to undo, and only one of those two numbers was on the plate.

Four permutations of the same 8,192 symbols: identity 34.0k, shuffle 34.4k, sorted 32.1k, bwt 14.8k bits against a floor of 31.9k8,192 symbols with a zeroth-order entropy of 3.8979 bits, so coding the text as it stands costs 31931 bits. Each permutation is applied, the result is put through move-to-front and coded at its own zeroth-order entropy, and whatever the inverse needs is added. The text as it is: mean run 1.01, 34020 bits of stream and nothing to undo it, 34020 in all — 1.065 of the floor. A shuffle agreed in advance: mean run 1.10, 34418 bits of stream and nothing to undo it, 34418 in all — 1.078 of the floor. The characters sorted: mean run 390.10, 289 bits of stream and 31827 bits to undo it, 32116 in all — 1.006 of the floor. The Burrows–Wheeler transform: mean run 3.38, 14760 bits of stream and nothing to undo it, 14760 in all — 0.462 of the floor.thousands of bits · the floor is 31.9kthe text as it is34.0k streamto undo itnothinga shuffle agreed in advance34.4k streamto undo itnothingthe characters sorted0.3k streamto undo it31.8kthe Burrows–Wheeler transform14.8k streamto undo itnothingmodel: order 0 after move-to-front · Words from a fixed vocabularydashed: coding the text as it stands
Fig. 1 Four permutations of the same 8,192 symbols, whose zeroth-order entropy is 3.898 bits, so coding the text as it stands costs 31,931 bits. Each permutation is applied, the result is put through move-to-front and coded at its own zeroth-order entropy, and whatever the inverse needs is added. The text as it stands: 34,020 bits and nothing to undo. A shuffle agreed in advance: 34,418 and nothing. The characters sorted: 289 bits of stream and 31,827 to undo it, 32,116 in all. The transform: 14,760 and nothing.

The sort’s two bars are the page. It drives the stream to 289 bits — a saving of 99.1% against the untransformed text — and the total comes to 1.006 times the floor it started from.

Why the cancellation is exact

This is not a coincidence about this text and it is not approximately true. It is a counting argument, and it is the same shape as the one under every floor in this field.

A sort sends every arrangement of a multiset of characters to the same string. To undo it, a decoder must be told which arrangement the text was, and there are

n!knk!\frac{n!}{\prod_k n_k!}

of them, where nkn_k is how many times the kk-th symbol occurs. The number of bits needed to name one of them is the base-two logarithm of that count, and Stirling’s approximation puts that logarithm at nH0n H_0 — the text’s own zeroth-order entropy — to within a term of order logn\log n. On these 8,192 symbols the exact logarithm is 31,827 bits and nH0n H_0 is 31,931: the two agree to three parts in a thousand, and the gap shrinks as the text grows.

So the sort’s side information is the floor, up to a vanishing correction. A compressor that sorts and then codes has moved the entire cost from the stream into the description of the permutation, and gained nothing at all. It is the compression equivalent of the counting argument the bits a coder emits uses to establish the floor in the first place: there are only so many texts, and something has to distinguish them.

The same number, in another field

That quantity has appeared before in a subject with no compression in it at all.

log2n!\log_2 n! is the number of comparisons a comparison sort must average, and the floor under every comparison sort derives it by counting leaves: the answer is one of n!n! orderings, each comparison has two outcomes, so no algorithm can average fewer. When the keys are not distinct the count of possible answers falls to n!/knk!n!/\prod_k n_k!, because arrangements that differ only by swapping equal keys are the same answer, and the floor falls with it.

That is the same expression, to the symbol. The bits a decoder needs to undo a sort are the comparisons a sorter needs to perform it, and they are equal for the same reason: both are the logarithm of how many arrangements the operation cannot tell apart. One field calls it a lower bound on questions and the other calls it side information, and there is one counting argument underneath.

The correspondence is worth following one step further, because it says something about the transform. A sort discards log2(n!/nk!)\log_2(n!/\prod n_k!) bits and a sorter pays the same to produce them. The Burrows–Wheeler transform sorts too — its block sort is exactly a sort, of nn rotations, and it pays that price in comparisons — and then keeps the result in a form that discards nothing. So the transform is the case where the work of sorting is done and the information it establishes is retained rather than thrown away, and one run, four counts, four answers is the page about how much that work actually is.

Every one of the 6,561 strings of 8 characters over 3, transformed and talliedAll 6,561 strings of length 8 over abc, each put through the permutation, and the outputs counted. The text as it is: 6,561 distinct outputs, at most 1 strings sharing one, and a mean of 0.000 bits needed to say which. A shuffle agreed in advance: 6,561 distinct outputs, at most 1 strings sharing one, and a mean of 0.000 bits needed to say which. The characters sorted: 45 distinct outputs, at most 560 strings sharing one, and a mean of 8.016 bits needed to say which. The Burrows–Wheeler transform: 6,561 distinct outputs, at most 1 strings sharing one, and a mean of 0.000 bits needed to say which. A permutation that needs no bits here is one whose inverse reads its own output and nothing else.mean bits needed to say which string it wasthe text as it is — one string an output0.00 bitsa shuffle agreed in advance — one string an output0.00 bitsthe characters sorted — up to 560 share one8.02 bitsthe Burrows–Wheeler transform — one string an output0.00 bitsevery string of 8 characters over abccounted, not derived
Fig. 2 The same claim counted rather than derived. All 6,561 strings of eight characters over three letters are put through each permutation and the outputs tallied. The text as it stands, a shuffle agreed in advance and the transform each give 6,561 distinct outputs — one string per output, nothing to say. Sorting gives 45 distinct outputs, as many as 560 strings share one, and it takes a mean of 8.016 bits to say which of them it was.

Forty-five outputs for 6,561 strings is the collapse made visible, and it is why the enumeration is worth doing rather than trusting the formula: the rule the ledger applies is a claim about a family of inputs, and here every member of a family is checked.

The transform’s column of that plate is the result the rest of the page turns on. Six thousand five hundred and sixty-one strings, six thousand five hundred and sixty-one outputs: the transform is injective on strings of a fixed length, so nothing whatever has to be sent alongside it. Its inverse reads its own output and needs no other input.

There is one reading of the sort’s row that has to be closed off, because it is the reading that makes the result look like an accounting trick. The 289 bits are not fake and the 31,827 are not a penalty invented to cancel them. A decoder handed 289 bits and told they are a sorted string reconstructs the multiset and nothing else, and the multiset is genuinely almost free to describe — it is a list of twenty-one counts. What it cannot reconstruct is the text, and the 31,827 bits are the smallest description of the difference that exists. Both halves are real transmissions and the sum is what a receiver has to be sent.

A permutation the inverse can compute

That property is not shared by clustering permutations in general, and it is worth saying exactly what it is.

Every rotation of "a-sort-is-not-free", sortedThe transform is the last column of this table. It is a permutation of the input — the same characters in a different order — so its zeroth-order entropy is exactly the input's, 3.899 bits per symbol, and by that measure nothing has happened. What has happened is that characters sharing a following context now sit together: on a 8,192-symbol stream the mean run goes from 1.01 to 3.38, and a move-to-front pass turns that into a floor of 1.80 bits per symbol against 4.16.each row is one rotation · the table is sorted · the transform is the last columnfirstlasta-sort-is-not-free-freea-sort-is-not-is-not-freea-sort-not-freea-sort-is-sort-is-not-freeaa-sort-is-not-freeea-sort-is-not-freeea-sort-is-not-frfreea-sort-is-not-is-not-freea-sort-not-freea-sort-is-ort-is-not-freea-sot-freea-sort-is-nreea-sort-is-not-f… 5 further rotationsmeasured on 8,192 symbols of the same source:H₀ of the text3.899 bitsH₀ of the last column3.899 bitsH₀ after move-to-front, before4.156 bitsH₀ after move-to-front, after1.802 bitsmodel: order 0, before and after a permutationmean run 1.01 → 3.38
Fig. 3 Every rotation of a short string, sorted, with the transform’s output as the last column. The transform is a function of the text alone — no parameter, no seed, no table — and its inverse is a function of the output alone. The bars beneath are measured on 8,192 symbols of the same kind: the mean run rises from 1.01 to 3.38, and move-to-front turns that into 1.80 bits per symbol against 4.16.

The rows of that table are the rotations of the text, sorted. The first column is the sorted characters — which is precisely the permutation this page has been pricing — and the last column is the transform. They are two columns of one table, and the difference between them is everything.

The first column throws away which rotation each character came from. The last column keeps it: a character in the last column is the one preceding a rotation, and the rotations are in a known order, so the last column records both the characters and enough about their positions to walk the text backwards. That is the LF mapping, and it is why a decoder needs nothing but the output.

Put the other way round: the transform’s output is a description of the permutation as well as of the characters, and the sorted column is only a description of the characters. The bits the sort has to send separately are bits the transform has already sent inside its own stream.

That also explains why the transform clusters less. It cannot cluster perfectly, because a perfectly clustered output would be the sorted string, which is not invertible. The 1.802 bits per symbol is what is left after the output has been made to carry its own inverse, and the gap between 1.802 and 0.035 is the price of that.

What the shuffle is for

The third row of the ledger is a control and it pays for its place twice.

A shuffle fixed by a stated seed costs nothing to undo — encoder and decoder agree on the seed in advance, so the permutation is common knowledge and there is no side information at all. By the accounting of this page it is free. And it makes the stream worse: 34,418 bits against 34,020 for the untransformed text.

So being free to undo is not sufficient, which is the half of the argument the transform’s success might otherwise hide. A permutation has to be both computable by the decoder and useful to the coder, and the two conditions pull against each other: the permutations that cluster hardest are the ones that discard the most, and what they discard is exactly what the decoder needed.

The shuffle also says something about move-to-front that the previous page could not. Applied to the untransformed text, move-to-front raises the cost from 31,931 bits to 34,020 — it is a loss of 6.5% on text it cannot exploit. It is not a neutral recoding; it is a model, one that assumes a symbol seen recently will be seen again soon, and on a stream where that is false it charges for the assumption. That is the same observation the dictionary that builds itself makes about a sliding window — a structure with no probabilities in it is still a claim about what the data looks like.

Four permutations of the same 8,192 symbols: identity 17.5k, shuffle 24.6k, sorted 24.6k, bwt 11.5k bits against a floor of 24.6k8,192 symbols with a zeroth-order entropy of 2.9998 bits, so coding the text as it stands costs 24575 bits. Each permutation is applied, the result is put through move-to-front and coded at its own zeroth-order entropy, and whatever the inverse needs is added. The text as it is: mean run 1.02, 17471 bits of stream and nothing to undo it, 17471 in all — 0.711 of the floor. A shuffle agreed in advance: mean run 1.14, 24572 bits of stream and nothing to undo it, 24572 in all — 1.000 of the floor. The characters sorted: mean run 1024.00, 101 bits of stream and 24532 bits to undo it, 24633 in all — 1.002 of the floor. The Burrows–Wheeler transform: mean run 3.93, 11484 bits of stream and nothing to undo it, 11484 in all — 0.467 of the floor.thousands of bits · the floor is 24.6kthe text as it is17.5k streamto undo itnothinga shuffle agreed in advance24.6k streamto undo itnothingthe characters sorted0.1k streamto undo it24.5kthe Burrows–Wheeler transform11.5k streamto undo itnothingmodel: order 0 after move-to-front · Order-1 Markov chaindashed: coding the text as it stands
Fig. 4 The same four permutations on 8,192 symbols from an order-1 Markov chain, whose zeroth-order floor is 24.6k bits. The text as it stands costs 17.5k — move-to-front alone beats the zeroth-order floor, with no permutation at all, because the chain’s own locality is what the pass is built to exploit. The shuffle destroys that locality and costs 24.6k, the sort costs 24.6k, and the transform costs 11.5k.

On a Markov chain the move-to-front pass is worth 29% by itself, and the shuffle’s 24.6k is the measurement of what it was exploiting: destroy the order and the pass goes back to charging exactly the zeroth-order floor. That is a cleaner statement of what move-to-front is than the previous page’s, which could only say what it did after the transform.

Two sources where none of this works

The ledger is a fair instrument only if it can report a failure, so here are two.

Four permutations of the same 8,192 symbols: identity 24.6k, shuffle 24.6k, sorted 24.6k, bwt 24.6k bits against a floor of 24.6k8,192 symbols with a zeroth-order entropy of 2.9994 bits, so coding the text as it stands costs 24571 bits. Each permutation is applied, the result is put through move-to-front and coded at its own zeroth-order entropy, and whatever the inverse needs is added. The text as it is: mean run 1.14, 24572 bits of stream and nothing to undo it, 24572 in all — 1.000 of the floor. A shuffle agreed in advance: mean run 1.15, 24571 bits of stream and nothing to undo it, 24571 in all — 1.000 of the floor. The characters sorted: mean run 1024.00, 101 bits of stream and 24528 bits to undo it, 24629 in all — 1.002 of the floor. The Burrows–Wheeler transform: mean run 1.15, 24629 bits of stream and nothing to undo it, 24629 in all — 1.002 of the floor.thousands of bits · the floor is 24.6kthe text as it is24.6k streamto undo itnothinga shuffle agreed in advance24.6k streamto undo itnothingthe characters sorted0.1k streamto undo it24.5kthe Burrows–Wheeler transform24.6k streamto undo itnothingmodel: order 0 after move-to-front · Uniform over 8 symbolsdashed: coding the text as it stands
Fig. 5 Eight symbols, uniformly random, 8,192 of them: a zeroth-order floor of exactly 24.6k bits. Every one of the four permutations costs 24.6k. There is no order to find, the transform’s output is as unclustered as its input, move-to-front neither gains nor loses, and the sort’s stream-plus-side-information lands on the floor as it must.

That is the case the whole apparatus has to get right and it does: four different routes, one number, and the number is the floor. A measurement that reported a saving here would be measuring itself.

Four permutations of the same 8,192 symbols: identity 9.3k, shuffle 9.4k, sorted 6.3k, bwt 9.4k bits against a floor of 6.2k8,192 symbols with a zeroth-order entropy of 0.7589 bits, so coding the text as it stands costs 6217 bits. Each permutation is applied, the result is put through move-to-front and coded at its own zeroth-order entropy, and whatever the inverse needs is added. The text as it is: mean run 5.25, 9320 bits of stream and nothing to undo it, 9320 in all — 1.499 of the floor. A shuffle agreed in advance: mean run 5.18, 9376 bits of stream and nothing to undo it, 9376 in all — 1.508 of the floor. The characters sorted: mean run 1024.00, 101 bits of stream and 6184 bits to undo it, 6285 in all — 1.011 of the floor. The Burrows–Wheeler transform: mean run 5.24, 9378 bits of stream and nothing to undo it, 9378 in all — 1.508 of the floor.thousands of bits · the floor is 6.2kthe text as it is9.3k streamto undo itnothinga shuffle agreed in advance9.4k streamto undo itnothingthe characters sorted0.1k streamto undo it6.2kthe Burrows–Wheeler transform9.4k streamto undo itnothingmodel: order 0 after move-to-front · One symbol at 0.9dashed: coding the text as it stands
Fig. 6 A stream where one symbol of eight arrives nine times in ten, with a zeroth-order floor of 6.2k bits. The text as it stands costs 9.3k after move-to-front and the transform costs 9.4k — both worse than simply coding the text. Only the sort comes close, at 6.3k, and it comes close by having nothing to gain.

Here the transform is a loss of 51%, and the reason is that the move-to-front pass is the wrong model for a stream that is already skewed. The rank stream it produces is dominated by zeroes either way, and the ranks carry less structure than the original symbols did — the same failure the optimal code that is beaten records for Huffman codes on skewed sources, arriving from a different direction. A compressor that applied this pipeline unconditionally would make such a file half again larger, which is why real ones measure and choose.

A reader might reasonably ask whether the skewed case is an argument against move-to-front rather than against the transform, and the ledger separates them. On that stream the untransformed text after move-to-front costs 9.3k and the transform costs 9.4k, so the pass is losing 3.1k on its own and the transform is adding a further 0.1k. The pass is the larger fault. But the transform cannot be run without something to turn its clustering into skew, and a pipeline is priced as a pipeline: a code word is at least one bit is the page about what happens when a stage’s floor is a floor of its own rather than of the data.

How the numbers move with the block

One parameter has been held fixed throughout and it is the one a real compressor tunes.

What each permutation costs a coder, per symbol, once undoing it is counted — against a floor of 3.892 bitsBits per symbol of the original text, against its length, on logarithmic axes: the stream after a move-to-front pass plus whatever the inverse needs, for each permutation. The text as it is: 4.113, 4.129, 4.135, 4.153, 4.150. The characters sorted: 4.033, 3.977, 3.936, 3.920, 3.903. The Burrows–Wheeler transform: 2.164, 1.968, 1.872, 1.802, 1.768. The zeroth-order floor of the untransformed text is 3.882, 3.897, 3.893, 3.898, 3.892 at the same sizes, so every permutation but the transform sits at or above the number it was meant to beat.10⁴symbolsbits per symbol, stream and side informationthe text's own floorthe text as it isthe characters sortedthe Burrows–Wheeler transformmodel: order 0 after move-to-front · Words from a fixed vocabularydashed: the floor of the text itself
Fig. 7 Bits per symbol of the original text against its length, on logarithmic axes, for three of the permutations. The untransformed text after move-to-front holds at 4.11 to 4.15. The sort falls from 4.033 to 3.903 towards the floor, which is what the vanishing logn\log n correction looks like. The transform falls from 2.164 at a thousand symbols to 1.768 at sixteen thousand, and is still falling.

The sort’s line is the counting argument converging: at a thousand symbols the side information is a few per cent above nH0n H_0 because of the correction term, and by sixteen thousand it is within a fraction of a per cent. It approaches the floor from above and never reaches it.

The transform’s line falls because the sorted rotations get longer, so the contexts that decide the order get more specific and the clustering improves. That is the reason bzip2 sorts blocks of 900 kB rather than of 9 kB, and it is also the reason it cannot simply sort the whole file: the block sort’s cost grows faster than the block does, which the transform that emits nothing priced — and which is why the transform’s afterlife is in the index that is the text, where the cost of constructing it is amortised over many queries, rather than in a compressor that reads its input once.

What is claimed and what is not

Every number here is a floor, not a file size. The stream is charged at its own zeroth-order entropy, which is what an arithmetic coder with a perfect model of the symbol frequencies would spend, and no coder is run. A real pipeline also pays for a frequency table, for block headers and for the run-length stage between move-to-front and the coder. Those are constants of a few hundred bits and none of them changes a ranking here.

The side information is counted as if it were sent optimally. Naming one arrangement of the multiset takes log2(n!/nk!)\log_2(n!/\prod n_k!) bits only if the arrangements are equally likely and coded together; a decoder given the sorted string knows the multiset, so this is the right count for that decoder. A different decoder — one already holding a model of the text — would need fewer, and then the sort would be a saving rather than a wash. The argument is about what a zeroth-order decoder needs.

Move-to-front is doing part of the work and is not the only choice. Run-length coding the transform’s output, or applying an order-1 model to it directly, reaches different numbers, and the previous page’s 1.802 and this page’s 14,760 bits are both statements about the transform with this pass after it. What is not sensitive to the choice is the sort’s ledger, since the sort’s stream is near zero under any reasonable pass and its side information does not depend on the pass at all.

And injectivity was checked at one length over one alphabet. The enumeration covers every string of eight characters over three letters. The transform is injective on strings of a fixed length for every length and alphabet — the standard argument is that the sorted-rotations table is recoverable from its last column — but that argument is not what this page measured.

What to take from the ledger

Three things, in the order a reader is likely to need them.

A preprocessing step is priced by its total, and the total includes its inverse. That sounds obvious written down and the literature is full of transforms reported on the stream alone — which is the number that makes a full sort look like a 99% saving. The check is cheap: ask how many inputs produce this output, and if the answer is more than one, the logarithm of it is a cost that has to appear somewhere.

A transform that is a function of its own output is in a different class from one that is not, and it is the class worth looking for. The transform, a shuffle with an agreed seed and the identity are all in it; the sort is not. What makes the transform interesting is that it is the only member of that class here which also clusters, and the previous page’s whole result is the measurement of how much.

And none of it is unconditional. The two failing sources above are not edge cases constructed to embarrass the method — a uniform stream and a heavily skewed one are both ordinary — and on one of them the pipeline costs half again what doing nothing costs. The model is the compressor is the page that says why: every number in this field is a floor under a model, the move-to-front pass is a model, and a model applied to data it does not fit charges for the misfit. The ledger’s job is to make that charge visible, and a plate that drew only the stream would hide it.

Still open: the permutations between the two

The sort and the transform are the two ends of a range that this page has left empty. Sorting the rotations by their whole following context gives the transform; sorting the characters with no context at all gives the sort. Between them is sorting the rotations by their first kk characters — a shorter context, a coarser sort, and a transform that keeps less of the order.

The prediction from this page is that such a transform costs something to undo, since a shorter context leaves ties and the ties are exactly the information the inverse is missing, and that the amount grows as kk falls until at k=0k = 0 it is the whole multinomial. If that is right, then the transform and the sort are one family with a parameter, and the parameter buys clustering with side information at a stated exchange rate.

The measurement that follows implements the order-kk sort for kk from zero upwards, enumerates the preimages at small lengths to count what each one costs to undo rather than assuming it, and asks whether any kk short of the full context comes out ahead — since a shorter sort is cheaper to compute, and the block sort’s cost is the reason the transform lost its place in compression.

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.

Bits per symbolBurrows-wheeler transformCounting argumentEntropyInformation-theoretic boundInvertibilityLower boundMeasurement designMove to frontPermutationPigeonhole principleSide information