TL;DR
This paper introduces a space-efficient algorithm for computing Re-Pair grammar compression on large datasets, reducing memory usage while maintaining effective compression rates.
Contribution
It presents a novel algorithm that computes Re-Pair in significantly less space, supporting large-scale data processing and recovery of original input.
Findings
Achieves Re-Pair computation in near-quadratic time with reduced space complexity.
Supports recovery of original text within the same time as computation.
Provides variants for parallel and external memory models.
Abstract
Re-Pair is a grammar compression scheme with favorably good compression rates. The computation of Re-Pair comes with the cost of maintaining large frequency tables, which makes it hard to compute Re-Pair on large scale data sets. As a solution for this problem we present, given a text of length whose characters are drawn from an integer alphabet, an time algorithm computing Re-Pair in bits of space including the text space, where is the number of terminals and non-terminals. The algorithm works in the restore model, supporting the recovery of the original input in the time for the Re-Pair computation with additional bits of working space. We give variants of our solution working in parallel or in the external memory model.
| Operation | Example |
|---|---|
| 11100110 | |
| 00011001 | |
| 00100000 | |
| 00011111 | |
| 00000110 | |
| Operation | Example |
|---|---|
| 11100110 | |
| 00011001 | |
| 00100000 | |
| 00011111 | |
| 00000110 | |
Peer Reviews
No public reviews on file for this paper yet. If you reviewed it on a platform where reviews are public (OpenReview, ICLR, NeurIPS, ICML), you can paste yours below so the community can read it here.
Code & Models
Videos
No videos yet. Explain this paper in a talk, walkthrough, or lecture? Add one.
Re-Pair in Small Space
Dominik Köppl
Tomohiro I
Isamu Furuya
Yoshimasa Takabatake
Kensuke Sakai
Keisuke Goto
Abstract
Re-Pair is a grammar compression scheme with favorably good compression rates. The computation of Re-Pair comes with the cost of maintaining large frequency tables, which makes it hard to compute Re-Pair on large scale data sets. As a solution for this problem we present, given a text of length whose characters are drawn from an integer alphabet, an time algorithm computing Re-Pair in bits of working space including the text space, where is the number of terminals and non-terminals. The algorithm works in the restore model, supporting the recovery of the original input in the time for the Re-Pair computation with additional bits of working space. We give variants of our solution working in parallel or in the external memory model.
Keywords: Grammar Compression, Re-Pair, Computation in Small Space
1 Introduction
Re-Pair [21] is a grammar deriving a single string. It is computed by replacing the most frequent bigram in this string with a new non-terminal, recursing until no bigram occurs more than once. Despite this simple-looking description, both the merits and the computational complexity of Re-Pair are intriguing. As a matter of fact, Re-Pair is currently one of the most well-understood grammar schemes.
Besides the seminal work of Larsson and Moffat [21], there are a couple of articles devoted to the compression aspects of Re-Pair: Given a text of length whose characters are drawn from an integer alphabet of size , the output of Re-Pair applied to is at most bits with when represented naively as a list of character pairs [25], where denotes the empirical entropy of the -th order. Using the encoding of Kieffer and Yang [19], Ochoa and Navarro [26] could improve the output size to at most bits. Other encodings were recently studied by Ganczorz [14]. Since Re-Pair is a so-called irreducible grammar, its grammar size, i.e., the sum of the symbols on the right hand of all rules, is upper bounded by [19, Lemma 2], which matches the information-theoretic lower bound on the size of a grammar for a string of length . Comparing this size with the size of the smallest grammar, its approximation ratio has as an upper bound [8] and as a lower bound [2].
On the practical side, Yoshida and Kida [33] presented an efficient fixed-length code for compressing the Re-Pair grammar. Although conceived as a grammar for compressing texts, Re-Pair has been successfully applied for compressing trees [23], matrices [30], or images [11].
For different settings or for better compression rates, there is a great interest in modifications to Re-Pair. Charikar et al. [8, Sect. G] give an easy variation to improve the size of the grammar. Sekine et al. [28] provide an adaptive variant whose algorithm divides the input into blocks, and processes each block based on the rules obtained from the grammars of its preceding blocks. Subsequently, Masaki and Kida [24] gave an online algorithm producing a grammar mimicking Re-Pair. Ganczorz and Jez [15] modified the Re-Pair grammar by disfavoring the replacement of bigrams that cross Lempel-Ziv-77 (LZ77) [34] factorization borders, which allowed the authors to achieve practically smaller grammar sizes. Recently, Furuya et al. [13] presented a variant, called MR-Re-Pair, in which a most frequent maximal repeat is replaced instead of a most frequent bigram.
1.1 Related Work
Although Re-Pair is a well received grammar, there is not much literature found on how to compute Re-Pair efficiently. In this article, we focus on the problem to compute the grammar with an algorithm working in text space, forming a bridge between the domain of in-place string algorithms and the domain of Re-Pair computing algorithms. We briefly review some prominent achievements in both domains:
In-Place String Algorithms.
For the LZ77 factorization, Kärkkäinen et al. [18] present an algorithm computing this factorization with words on top of the input space in time for a variable , achieving words with time. For the suffix sorting problem, Goto [16] gave an algorithm to compute the suffix array with bits on top of the output in time if each character of the alphabet is present in the text. This condition got improved to alphabet sizes of at most by Li et al. [22]. Finally, Crochemore et al. [9] showed how to transform a text into its Burrows-Wheeler transform by using of additional bits. Due to da Louza et al. [10], this algorithm got extended to compute simultaneously the LCP array with bits of additional working space.
Re-Pair Computation.
Re-Pair is a grammar proposed by Larsson and Moffat [21], who gave an algorithm computing it in expected linear time with words of working space, where is the number of non-terminals (produced by Re-Pair). This space requirement got improved by Bille et al. [5], who presented a linear time algorithm taking words on top of the rewriteable text space for a constant with . Subsequently, they improved their algorithm in [4] to include the text space within the words of working space. However, they assume that the alphabet size is constant and , where is the machine word size. They also provide a solution for running in expected linear time. Recently, Sakai et al. [27] showed how to convert an arbitrary grammar (representing a text) into the Re-Pair grammar in compressed space, i.e., without decompressing the text. Combined with a grammar compression that can process the text in compressed space in a streaming fashion, this result leads to the first Re-Pair computation in compressed space.
Our Contribution.
In this article, we propose an algorithm that computes the Re-Pair grammar in time (cf. Thm. 2.3 and Thm. 3.1) with bits of working space including the text space, where is the number of terminals and non-terminals. Given that the characters of the text are drawn from a large integer alphabet with size , the algorithm works in-place. This is the first non-trivial in-place algorithm, as a trivial approach on a text of length would compute the most frequent bigram in time by computing the frequency of each bigram for every integer with , keeping only the most frequent bigram in memory. This sums up to total time, and can be for some texts since there can be different bigrams considered for replacement by Re-Pair. To achieve our goal of total time, we first provide a trade-off algorithm (cf. Lemma 2.2) finding the most frequent bigrams in time for a trade-off parameter . We subsequently run this algorithm for increasing values of , and show that we need to run it times, which gives us time if is increasing sufficiently fast. Our major tools are appropriate text partitioning, elementary scans, and sorting steps, which we visualize in Sect. 2.4 by an example, and practically evaluate in Sect. 2.5. When , a different approach using word-packing and bit-parallel techniques becomes attractive, leading to an time algorithm, which we explain in Sect. 3. Our algorithm can be parallelized (Sect. 5), used in external memory (Sect. 6), or adapted to compute the MR-Re-Pair grammar in small space (Sect. 4). Finally, in Sect. 7 we study several heuristics that make the algorithm faster on specific texts.
1.2 Preliminaries
We use the word RAM model with a word size of for an integer . We work in the restore model [7], in which algorithms are allowed to overwrite the input, as long as they can restore the input to its original form.
Strings.
Let be a text of length whose characters are drawn from an integer alphabet of size . A bigram is an element of . The frequency of a bigram in is the number of non-overlapping occurrences of in , which is at most .
Re-Pair.
We reformulate the recursive description in the introduction by dividing a Re-Pair construction algorithm into turns. Stipulating that is the text after the -th turn with and with , Re-Pair replaces one of the most frequent bigrams (ties are broken arbitrarily) in with a non-terminal in the -th turn. Given this bigram is , Re-Pair replaces all occurrences of bc with a new non-terminal in , and sets with to produce . Since , Re-Pair terminates after turns such that contains no bigram occurring more than once.
2 Sequential Algorithm
A major task for producing the Re-Pair grammar is to count the frequencies of the most frequent bigrams. Our work horse for this task are frequency tables. A frequency table in of length stores pairs of the form , where bc is a bigram and the frequency of bc in . It uses bits of space since an entry stores a bigram consisting of two characters from and its respective frequency, which can be at most . Throughout this paper, we use an elementary in-place sorting algorithm like heapsort:
Lemma 2.1** ([32]).**
An array of length can be sorted in-place in time.
2.1 Trade-Off Computation
By embracing the frequency tables, we present a solution with a trade-off parameter:
Lemma 2.2**.**
Given an integer with , we can compute the frequencies of the most frequent bigrams in a text of length whose characters are drawn from an alphabet of size in time using bits.
Proof.
Our idea is to partition the set of all bigrams appearing in into subsets, compute the frequencies for each subset, and finally merge these frequencies. In detail, we partition the text into substrings such that each substring has length (the last one has a length of at most ). Subsequently, we extend to the left (only if ) and to the right (only if ) such that and overlap by one text position, for . By doing so, we take the bigram on the border of two adjacent substrings and for each into account. Next, we create two frequency tables and , each of length for storing the frequencies of bigrams. With and , we process each of the substrings as follows: Let us fix an integer with . We first put all bigrams of into in lexicographic order. We can perform this within the space of in time since there are at most different bigrams in . We compute the frequencies of all these bigrams in the complete text in time by scanning the text from left to right while locating a bigram in in time with a binary search. Subsequently, we interpret and as one large frequency table, sort it with respect to the frequencies while discarding duplicates such that stores the most frequent bigrams in . This sorting step can be done in time. Finally, we clear and are done with . After the final merge step, we obtain the most frequent bigrams of stored in .
Since each of the merge steps takes time, we need time. For , we can build a large frequency table and perform one scan to count the frequencies of all bigrams in . This scan and the final sorting with respect to the counted frequencies can be done in time. ∎
2.2 Algorithmic Ideas
With Lemma 2.2, we can compute in time with additional bits of working space on top of the text for a parameter with . In the following, we present an time algorithm that needs bits of working space, where the text space is included as a rewriteable part in the working space and is a constant. In this model, we assume that we can enlarge the text from bits to bits without additional extra memory. Our main idea is to store a growing frequency table using the space freed up by replacing bigrams with non-terminals. In detail, we maintain a frequency table in of length for a growing variable , which is set to in the beginning. The table takes bits, which is bits for . When we want to query it for a most frequent bigram, we linearly scan in time, which is not a problem since (a) the number of queries is , and (b) we aim for overall running time. A consequence is that there is no need to sort the bigrams in according to their frequencies, which simplifies the following discussion.
Frequency Table .
With Lemma 2.2, we can compute in time. Instead of recomputing for every turn , we want to recompute it only when it no longer stores a most frequent bigram. However, it is ad-hoc not clear when this happens as replacing a most frequent bigram during a turn (a) removes this entry in and (b) can reduce the frequencies of other bigrams in , making them possibly less frequent than other bigrams not tracked by . Hence, the variable for the -th turn (creating the -th non-terminal) and the variable for recomputing the frequency table the -st time are loosely connected. We group together all turns with the same and call this group the -th round of the algorithm. At the beginning of each round, we enlarge and create a new with a capacity for bigrams. Since a recomputation of takes much time, we want to end a round only if is no longer useful, i.e., when we no longer can guarantee that stores a most frequent bigram. To achieve our claimed time bounds, we want to assign all turns to different rounds, which can only be done if grows sufficiently fast.
Algorithm Outline.
Given we are at the beginning of the -th round and the -th turn, we compute the frequency table storing bigrams, and keep additionally the lowest frequency of as a threshold , which is treated as a constant during this round. During the computation of the -th turn, we replace the most frequent bigram (say, ) in the text with a non-terminal to produce . Thereafter, we remove bc from and update those frequencies in which got decreased by the replacement of bc with , and add each bigram containing the new character into if its frequency is at least . Whenever a frequency in drops below , we discard it. If becomes empty, we move to the -st round, and create a new for storing frequencies. Otherwise ( still stores an entry), we can be sure that stores a most frequent bigram. In both cases, we recurse with the -st turn by selecting the bigram with the highest frequency stored in . We describe in the following how we update of and how large can be at least.
2.3 Algorithmic Details
Suppose that we are in the -th round and in the -th turn. Let be the lowest frequency in computed at the beginning of the -th round. We keep as a constant threshold for the invariant that all frequencies in are at least during the -th round. With this threshold we can assure in the following that is either empty or stores a most frequent bigram.
Now suppose that the most frequent bigram of is , which is stored in . To produce (and hence advancing to the -st turn), we enlarge the space of from to , and replace all occurrences of bc in with a new non-terminal . Subsequently, we would like to take the next bigram of . For that, however, we need to update the stored frequencies in . To see this necessity, suppose that there is an occurrence of abcd with two characters in . By replacing bc with ,
- (a)
the frequencies of ab and cd decrease by one111For the border case a = b = c (resp. b = c = d), there is no need to decrement the frequency of ab (resp. cd)., and 2. (b)
the frequencies of and increase by one.
Updating
We can take care of the former changes (a) by decreasing the respective bigram in (in case that it is present). If the frequency of this bigram drops below the threshold , we remove it from as there may be bigrams with a higher frequency that are not present in . To cope with the latter changes (b), we track the characters adjacent to after the replacement, count their numbers, and add their respective bigrams to if their frequencies are sufficiently high. In detail, suppose that we have substituted bc with exactly times. Consequently, with the new text we have additionally bits of free space222The free space is consecutive after shifting all characters to the left., which we call in the following. Subsequently, we scan the text and put the characters of appearing to the left of each of the occurrences of into . After sorting the characters in lexicographically, we can count the frequency of for each character preceding an occurrence of in the text by scanning linearly. If the obtained frequency of such a bigram is at least as high as the threshold , we insert into , and subsequently discard a bigram with the currently lowest frequency in if the size of has become . In case that we visit a run of ’s during the creation of , we must take care of not counting the overlapping occurrences of . Finally, we can count analogously the occurrences of for all characters succeeding an occurrence of .
Capacity of
After the above procedure we have updated the frequencies of . When becomes empty, we end the -th round and continue with the ()-st round by creating a new frequency table with capacity . In what follows, we (a) analyze in detail when becomes empty (as this determines the sizes and ), and (b) show that we can compensate the number of discarded bigrams with an enlargement of ’s capacity from bigrams to bigrams for the sake of our aimed total running time: If the frequency of bc in is , then we can reduce at most frequencies of other bigrams. Since a bigram must occur at least twice in to be present in , the frequency of bc has to be at least for discarding all bigrams of , and each replacement of bc with frees up bits of the text.
Suppose that we have enough space available for storing the frequencies of bigrams, where is a constant (depending on and ) such that and the working space of Lemma 2.2 with can be stored within this space. Let be the number of bits needed to store one entry in , and let be the minimum number of characters that need to be freed to store one frequency in this space. To understand the value of , we look at the arguments of the minimum function in the definition of and simultaneously at the maximum function in our aimed working space of bits (cf. Thm. 2.3):
- •
The first item in this maximum function allows us to spend bits for each freed character such that we obtain space for one additional entry in after freeing characters.
- •
The second item allows us to use additional bits after freeing up characters.333This additional treatment helps us to let grow sufficiently fast in the first steps to save our time bound, as for sufficiently small alphabets and large text sizes, , which means that we might run the first turns with , and therefore already spend time. Hence, after freeing up characters, we have space to store one additional entry in .
[TABLE]
where we used the equivalence to estimate the two arguments of the maximum function.
Since we let grow by a factor of at least for each recomputation of , , and therefore after steps. Consequently, after reaching , we can iterate the above procedure a constant number of times to compute the non-terminals of the remaining bigrams occurring at least twice.
Time Analysis
On the total picture, we compute times with Lemma 2.2. For the -th time, we run the algorithm of Lemma 2.2 with on a text of length at most in time with . Summing this up, we yield
[TABLE]
In the -th turn, we update by decreasing the frequencies of the bigrams affected by the substitution of the most frequent bigram bc with . For decreasing such a frequency, we look up its respective bigram with a linear scan in , which takes time. However, since this decrease is accompanied with a replacement of an occurrence of bc, we obtain total time by charging each text position with time for a linear search in . With the same argument, we can bound the total time for sorting the characters in to overall time: Since we spend time on sorting characters preceding or succeeding a replaced character, and time on swapping a sufficiently large new bigram composed of and a character of with a bigram with the lowest frequency in , we charge each text position again with time. Putting all time bounds together leads to the main result of this article:
Theorem 2.3**.**
We can compute Re-Pair on a string of length in time with bits of working space including the text space, where is a fixed constant, and is the number of terminal and non-terminal symbols.
Output
Finally, we show that we can store the computed grammar in text space. More precisely, we want to store the grammar in an auxiliary array packed at the end of the working space such that the entry stores the right hand side of the non-terminal , which is a bigram. Thus the non-terminals are represented implicitly as indices of the array . We therefore need to subtract bits of space from our working space after the -th turn. By adjusting in the above equations, we can deal with this additional space requirement as long as the frequencies of the replaced bigrams are at least three (we charge two occurrences for growing the space of ).
When only bigrams with frequencies of at most two remain, we switch to a simpler algorithm, discarding the idea of maintaining the frequency table : Suppose that we work with the text . Let be a text position, which is in the beginning, but will be incremented in the following turns while holding the invariant that does not contain a bigram of frequency two. We scan linearly from left to right and check, for each text position , whether the bigram has another occurrence with , and if so,
- (a)
append to , 2. (b)
replace and with a new non-terminal to transform to , and 3. (c)
recurse on with until no bigram with frequency two is left.
The position , which we never decrement, helps us to skip over all text positions starting with bigrams with a frequency of one. Thus, the algorithm spends time for each such text position, and time for each bigram with frequency two. Since there are at most such bigrams, the overall running time of this algorithm is .
Remark 2.4** (Pointer Machine Model).**
Refraining from the usage of complicated algorithms, our algorithm consists only of elementary sorting and scanning steps. This allows us to run our algorithm on a pointer machine, yielding the same time bound of . For the space bounds, we assume that the text is given in words, where a word is large enough to store an element of or a text position.
2.4 Step-by-Step Execution
Here, we present an exemplary execution of the first turn (of the first round) on the input . We visualize each step of this turn as a row in Fig. 1. A detailed description of each row follows:
Row 1:
Suppose that we have computed , which has a constant number of entries444In the later turns when the size becomes larger, will be put in the text space.. The highest frequency is five achieved by and . The lowest frequency represented in is three, which becomes the threshold for a bigram to be present in such that bigrams whose frequencies drop below this threshold are removed from . This threshold is a constant for all later turns until is rebuilt (in the following round). During Turn 1, the algorithm proceeds now as follows:
Row 2:
Choose as a bigram to replace with a new non-terminal (break ties arbitrarily). Replace every occurrence of with while decrementing frequencies in accordingly to the neighboring characters of the replaced occurrence.
Row 3:
Remove from every bigram whose frequency falls below the threshold. Obtain space for by aligning the compressed text . (The process of Row 2 and Row 3 can be done simultaneously.)
Row 4:
Scan the text and copy each character preceding an occurrence of in to .
Row 5:
Sort characters in lexicographically.
Row 6:
Insert new bigrams (consisting of a character of and ) whose frequencies are at least as large as the threshold.
Row 7:
Scan the text again and copy each character succeeding an occurrence of in to (symmetric to Row 4).
Row 8:
Sort all characters in lexicographically (symmetric to Row 5).
Row 9:
Insert new bigrams whose frequencies are at least as large as the threshold (symmetric to Row 6).
2.5 Implementation
We provide a simplified implementation in C++17 at https://github.com/koeppl/repair-inplace. The simplification is that we (a) fix the bit width of the text space to 16 bit, and (b) assume that is the byte alphabet. We further skip the step increasing the bit width from to . This means that the program works as long as the characters of fit into 16 bits. The benchmark, whose results are displayed in Sect. 2.5, was conducted on a Mac Pro Server with an Intel Xeon CPU X5670 clocked at 2.93GHz running Arch Linux. The implementation was compiled with gcc-8.2.1 in the highest optimization mode -O3. Looking at Sect. 2.5, we can see that the running time is super-linear to the input size on all text instances, which we obtained from the Pizza&Chili corpus (http://pizzachili.dcc.uchile.cl/). Section 2.5 gives some characteristics about the used data sets. We see that the number of rounds is the number of turns plus one for every unary string with an integer since the text contains only one bigram with a frequency larger than two in each round. Replacing this bigram in the text makes empty such that the algorithm recomputes after each turn. Note that the number of rounds can drop while scaling the prefix length based on the choice of the bigrams stored in .
The reference list from the paper itself. Each links out to its DOI / PubMed record.
- 1Aggarwal and Vitter [1988] A. Aggarwal and J. S. Vitter. The input/output complexity of sorting and related problems. Commun. ACM , 31(9):1116–1127, 1988.
- 2Bannai et al. [2019] H. Bannai, M. Hirayama, D. Hucke, S. Inenaga, A. Jez, M. Lohrey, and C. P. Reh. The smallest grammar problem revisited. ar Xiv 1908.06428 , 2019.
- 3Batcher [1968] K. E. Batcher. Sorting networks and their applications. In Proc. AFIPS , volume 32 of AFIPS Conference Proceedings , pages 307–314, 1968.
- 4Bille et al. [2017 a] P. Bille, I. L. Gørtz, and N. Prezza. Practical and effective Re-Pair compression. ar Xiv 1704.08558 , 2017 a.
- 5Bille et al. [2017 b] P. Bille, I. L. Gørtz, and N. Prezza. Space-efficient Re-Pair compression. In Proc. DCC , pages 171–180, 2017 b.
- 6Boyer and Moore [1991] R. S. Boyer and J. S. Moore. MJRTY: A fast majority vote algorithm. In Automated Reasoning: Essays in Honor of Woody Bledsoe , Automated Reasoning Series, pages 105–118, 1991.
- 7Chan et al. [2018] T. M. Chan, J. I. Munro, and V. Raman. Selection and sorting in the “restore” model. ACM Trans. Algorithms , 14(2):11:1–11:18, 2018.
- 8Charikar et al. [2005] M. Charikar, E. Lehman, D. Liu, R. Panigrahy, M. Prabhakaran, A. Sahai, and A. Shelat. The smallest grammar problem. IEEE Trans. Information Theory , 51(7):2554–2576, 2005.
