BWT: Reordering Without Data Loss
Huffman coding uses symbol frequencies, while methods from the Lempel-Ziv family refer to repeated strings. Both preserve data but target different forms of redundancy. The Burrows-Wheeler Transform orders a block by character context and prepares it for later statistical coding.
The orders block rotations by lexicographical position. Described by Michael Burrows and David Wheeler in 1994, BWT does not reduce the data size. It is a reversible permutation of a block that groups characters with similar contexts. The result can then be processed by RLE, MTF, and entropy coding. BWT is one stage of the bzip2 archiver; its documentation reports generally better results than conventional LZ compressors, while the ratio still depends on the data. Sorting an entire block increases computation and memory use, so practical formats divide input into bounded blocks.
The BWT Transform: Rotations, Sorting, and Output
BWT operates on an entire block rather than encoding one byte at a time. Let us transform "ANANAS$". The character '$' marks the end of the string and is lexicographically smaller than the other characters, giving each rotation an unambiguous position.
Step 1: Create All Cyclic Rotations
First, create every cyclic shift of the input string. Each next shift moves the first character to the end.
ANANAS$
NANAS$A
ANAS$AN
NAS$ANA
AS$ANAN
S$ANANA
$ANANAS
Step 2: Sort the Rotations Lexicographically
Next, sort the rotations lexicographically. compares characters from left to right in dictionary order. An implementation need not materialize the entire matrix, since rotations can be represented by indexes or a suffix array.
| Index | Sorted rotation |
|---|---|
| 0 | $ANANAS |
| 1 | ANANAS$ |
| 2 | ANAS$AN |
| 3 | AS$ANAN |
| 4 | NANAS$A |
| 5 | NAS$ANA |
| 6 | S$ANANA |
Step 3: Extract the Output, the Last Column
The BWT output is the sequence of characters in the last column of the sorted table. We denote this column by .
L = S$NNAAA
The string "ANANAS$" produces "S$NNAAA". The three 'A' characters form a run that RLE can encode efficiently. Grouping contexts is the purpose of applying BWT before the coding stages.
Step 4: Record the Primary Index
To reverse the transform, store the row containing the original string. In this example, "ANANAS$" is in row 1, so the primary index is 1.
The complete forward Burrows-Wheeler output for "ANANAS$" is the pair ("S$NNAAA", ).
For comparison, a block without an explicit terminator can contain "BANANA". Its rotations sort as "ABANAN", "ANABAN", "ANANAB", "BANANA", "NABANA", "NANABA". The output is "NNBAAA", with primary index .
The Inverse BWT Transform: Reconstructing the Input
The inverse process needs only the last column and the primary index. It relies on a property connecting the first and last columns of the sorted matrix.
The Last-to-First (LF) Mapping Property
In the sorted matrix, call the first column and the last column .
$ S
A $
A N
A N
N A
N A
S A
is the sorted version of the input characters ($,A,A,A,N,N,S). We can reconstruct it by sorting the characters of .
The property that makes BWT reversible is the Last-to-First (LF) Mapping:
The mapping moves from a character in to its corresponding position in . Because each character in begins a sorted rotation, repeated moves reconstruct the input backwards. The occurrence number matters because repeated letters occupy different rows. Stable numbering of occurrences identifies one LF edge for each row.
Inverse Transformation Steps
Reconstruct "ANANAS$" from "S$NNAAA" and .
- Step 1: Reconstruct First Column (F): Sort the characters of . Sorting "S$NNAAA" gives "$AAANNS".
- Step 2: Start with the Primary Index: The index identifies the row of the original rotation. In , is '$'. The character at is the final character of the input.
- Step 3: Apply the LF-Mapping: The '$' at L[1] is the first '$' in . It corresponds to the first '$' in , at index 0. The next row is row 0.
- Step 4: Walk Backwards, Iteration 2: Go to row 0. The character in is 'S', the preceding character. So far, read from the end, we have S$.
- Step 5: Repeat LF-Mapping: The 'S' at is the first 'S' in . It corresponds to the first 'S' in , at index 6. The next row is 6.
- Step 6: Continue Iterating:
- . Take L[1] = '$'. The first '$' in L maps to index 0 in F. New . Reconstructed suffix: $.
- . Take L[0] = 'S'. The first 'S' in L maps to index 6 in F. New . Reconstructed suffix: S$.
- . Take L[6] = 'A'. The third 'A' in L maps to index 3 in F. New . Reconstructed suffix: AS$.
- . Take L[3] = 'N'. The second 'N' in L maps to index 5 in F. New . Reconstructed suffix: NAS$.
- . Take L[5] = 'A'. The second 'A' in L maps to index 2 in F. New . Reconstructed suffix: ANAS$.
- . Take L[2] = 'N'. The first 'N' in L maps to index 4 in F. New . Reconstructed suffix: NANAS$.
- . Take L[4] = 'A'. The first 'A' in L maps to index 1 in F. New . Reconstructed string: ANANAS$.
bzip2 Pipeline Stages After BWT
BWT prepares a block for later transformations. The bzip2 format also has an initial RLE stage, followed by BWT, MTF, another RLE stage, and Huffman coding.
- Stage 1: RLE and Burrows-Wheeler Transform (BWT): The bzip2 format applies an initial RLE stage, then processes data in blocks to group characters with similar contexts and produce the L column and an index.
- Stage 2: Move-to-Front (MTF) Transform: The output of the BWT, which now has long runs of identical characters, is processed by the MTF transform. MTF maintains a list of the alphabet. For each character it processes, it outputs the character's current position in the list and then moves that character to the front of the list. The result is a stream of small numbers (mostly 0s, 1s, and 2s), which is even more statistically redundant.
- Stage 3: Run-Length Encoding (RLE): The stream from MTF is processed by another RLE stage, including to shorten runs of zeros.
- Stage 4: Entropy Coding: In bzip2, the final stream of symbols from the RLE stage is compressed using Huffman coding. Other BWT pipelines may use arithmetic coding at this stage.
In practice, MTF records a character position in the current alphabet list and moves that character to the front. BWT often produces small positions, especially zeros, so RLE can describe their runs with fewer symbols. Huffman then assigns codes according to frequency. Each stage has a separate role and can be reversed during decompression in the opposite order. The primary index is stored with the input block and selects the original rotation.
Role of BWT
BWT is a reversible permutation that uses character context to prepare a block for RLE, MTF, and entropy coding. Sorting increases computation and requires the whole block. The resulting compression depends on the data and on the later pipeline, so BWT itself is not a measure of size reduction. The same rotation order can support text indexes, while bzip2 uses it primarily as a data-preparation stage.