The other axis

The structure paid for before the first query

The two grids that make a phrase index's search proportional to its answer are 3,240 bits on an 8,892-bit index — twenty-seven per cent of the whole structure, answering nothing on their own, and 38% of them is rank directory rather than payload — the lower-order term of the published bound, measured.

Every structure in this field is bought before it is used. A search that is proportional to its answer instead of to a range is worth having, and the price is paid at construction time by everyone, including the reader who never searches at all.

The two grids are 27% of the structure and answer nothing on their ownEvery part of the phrase index over a collection of 16,384 characters with z = 156 phrases. The parse itself and the three orderings of its boundaries are the index; the two grids are what makes its search proportional to the answer. Together they are 3,240 bits of 12,132, of which 896 is rank directory rather than payload — the o(z log z) that the published bound puts under the leading term, drawn at the size where it is 28% of it.phrase table5,148 bitsboundary orders3,744 bitsintersection grid1,696 bitspropagation grid1,544 bitsz = 156 · ⌈log₂ z⌉ = 8 levels16,384 characters · 12,132 bitsgrids 26.7% · 20.8 bits a point
Fig. 1 Every part of the phrase index over a collection of sixteen thousand characters. The two lower bars answer no query on their own.

The bill

On a collection of thirty-two copies of five hundred and twelve characters — sixteen thousand characters, z=156z = 156 phrases:

  • the phrase table (lengths, sources, literals): 5,148 bits
  • the three orderings of the boundaries: 3,744 bits
  • the intersection grid: 1,696 bits
  • the propagation grid: 1,544 bits

Total 12,132 bits, of which the two grids are 3,240 — 26.7%. They answer nothing: no occurrence, no character, no count. What they do is convert a search proportional to zz per occurrence into a search proportional to logz\log z per occurrence, which is the previous essay’s subject and is worth 1.76 times at this size.

Twenty-seven per cent of a structure for 1.76 times on a query is not obviously a good trade, and saying so is the point of this essay. A structure whose query cost is quoted and whose size is not has had the easier half of its case made for it, and this field’s literature quotes the query cost in every abstract. The trade gets better as the collection grows, in both columns and at different rates, and the shape of that is the only honest way to decide.

Where the bits go, exactly

A permutation grid over zz points at L=log2zL = \lceil\log_2 z\rceil levels is zLzL bits of payload. At z=156z = 156 and L=8L = 8 that is 1,248 bits for the intersection grid, and the file reports 1,696 — so 448 bits, 36% on top, are the rank directory.

That directory is the o(zlogz)o(z \log z) in the published bound, and it is not small here. Both grids together are 2,344 bits of payload and 896 of directory: the lower-order term is 38% of the leading one, at the size a plate can draw.

The two grids are 28% of the structure and answer nothing on their ownEvery part of the phrase index over a collection of 16,384 characters with z = 148 phrases. The parse itself and the three orderings of its boundaries are the index; the two grids are what makes its search proportional to the answer. Together they are 3,232 bits of 11,372, of which 896 is rank directory rather than payload — the o(z log z) that the published bound puts under the leading term, drawn at the size where it is 28% of it.phrase table4,588 bitsboundary orders3,552 bitsintersection grid1,632 bitspropagation grid1,600 bitsz = 148 · ⌈log₂ z⌉ = 8 levels16,384 characters · 11,372 bitsgrids 28.4% · 21.8 bits a point
Fig. 2 The same accounting on four-symbol text, where the phrase count is larger and every share moves with it.

The directory is a two-level structure over each level’s bit vector: an absolute counter every 512 bits and a relative one every 64. Its size is z/512log2z+z/649\lceil z/512\rceil\lceil\log_2 z\rceil + \lceil z/64\rceil \cdot 9 bits per level, which falls as a share when zz grows and does not vanish. This collection has measured the same arithmetic twice before, under what is still proportional to n and in the index that is smaller than the text, and the lesson is the same each time: a term written o()o(\cdot) is a term whose constant was not worth writing down, not a term that is small.

The saving is a number in two currencies

Before any of that can be netted off, the thing the grid is bought for has to be priced, and it prices very differently depending on which count is used.

The saving, counted twiceOne query on a collection of 16,384 characters with 32 occurrences. Counted in candidates the grid is 132x cheaper: 4,355 phrase examinations become 33. Counted in the operations those examinations cost, it is not: descending the wavelet tree and lifting each reported point back to its column takes 2,209 ranks and 262 selects, so the honest ratio against the filter's scan is 1.76x. Two currencies, one query, and the ratio between them is 75x.candidates, filtering4,355candidates, grid33rank and select, grid2,471occurrences32one query · same collection · same answer16,384 characters · 32 occurrencescandidates 132x · operations 1.76x
Fig. 3 One query on sixteen thousand characters with thirty-two occurrences. Counted in candidates the grid is 132× cheaper — 4,355 phrase examinations become 33. Counted in the operations those examinations cost it is 1.76×, because descending the wavelet tree and lifting each reported point back to its column takes 2,209 ranks and 262 selects. Two currencies, one query, and a factor of 75 between the ratios.

The larger of those two numbers is the one that gets quoted, and it is the one the bits are not being spent on.

The grid replaces something, which nobody credits

The marginal cost of the intersection grid is not 1,696 bits. It is 1,696 minus what the grid makes unnecessary.

The filtering policy needs rankInPrefix — an array taking a boundary to its rank in the prefix order — because that is what it reads for each candidate. The grid does not: it holds the same relation, in a form that supports a query the array cannot answer. So one of the three stored orderings, 1,248 bits of the 3,744, is dead once the grid is there.

The honest marginal cost is therefore 448 bits, or about a third of one ordering. That is a very different number from 1,696, and it changes the trade from “a quarter more structure for a factor of two” into “three per cent more structure for a factor of two”, which is a trade anybody would take.

Neither number is wrong. The first is what the two grids cost; the second is what adding them costs a structure that then drops what they replace. The plate carries the first because it is what is built, and this section carries the second because it is what a reader deciding should use.

What each policy looks at, and what the answer isOne pattern of 8 characters against a collection growing from one copy to 32. The filter examines 105 candidates at one copy and 4,355 at 32, while the answer grows from 1 occurrences to 32 — 136.1 candidates per occurrence, and rising. The grid examines 33, which is 1.03 per occurrence and is the answer plus the split points it was found at. Both axes are logarithmic.1,00010,00011010010³candidates examined12481632characters in the collection · copies abovefiltering a rangethe gridthe answerEnglish-like · m = 8132x fewer candidates
Fig. 4 What the bits buy, in the currency that flatters them. In operations the same query is 1.76 times, and both are true.

How the share moves as the collection grows

This is the column that decides, and it moves the right way.

The phrase table is z(2log2n+log2σ)z(2\lceil\log_2 n\rceil + \lceil\log_2\sigma\rceil) bits — it grows with the text through the logn\log n term, even when zz does not. The grids are zLzL bits, which depend on zz alone. So on a collection that grows by repeating, the index grows slowly and the grids do not grow at all.

Measured across the sweep: 30.7%, 29.8%, 28.9%, 28.2%, 27.4%, 26.7% of the structure at one, two, four, eight, sixteen and thirty-two copies. Drifting steadily down — because log2n\log_2 n climbs from 10 to 14 across the sweep while log2z\log_2 z stays at 8, so the index grows and the grids do not.

On a collection whose phrase count grows — near-copies rather than copies, or unrelated documents — the grids grow with zlogzz\log z and the index with zlognz\log n, so the share is decided by logz\log z against logn\log n and the grids stay under a half. There is no regime in which they dominate, which is worth stating because a reader who has met a range structure whose cost was O(zlog2z)O(z\log^2 z) would reasonably expect one.

What one produced occurrence costs each policyThe propagation produces every occurrence copied from one already found. The walk examines every copying phrase whose source starts early enough, which is 242 phrases per produced occurrence at 2 copies and 140 at 32 — a number set by the size of the phrase table and not by the answer. The grid examines 1.00. Both axes are logarithmic.10,000110100phrases per occurrence2481632characters in the collection · copies abovewalking the sourcesthe stabbing queryEnglish-like · m = 8140x apart at 32 copies
Fig. 5 And what the same growth does to the query: the walk’s cost per occurrence climbs towards the phrase table’s size while the grid’s stays at one.

The propagation grid’s point set is not a permutation of the same thing

A detail that decides the second grid’s size and is easy to skip.

The intersection grid’s two coordinates are ranks of the same zz boundaries in two total orders, so it holds zz points. The propagation grid’s points are the copying phrases only — the literals copy from nowhere and cannot contain an occurrence — so it holds fewer. On this collection 137 of the 156 phrases copy, and 19 are literals, which is why the second grid is 1,544 bits against the first’s 1,696.

That nineteen is not noise. It is the alphabet-sized floor under zz that the phrases a text copies from itself measures: a text over σ\sigma symbols has at least σ\sigma literal phrases, one per symbol, whatever else it does. On a very repetitive collection most of what a phrase index stores is that floor, and the propagation grid is the one structure here that does not pay for it.

Both grids, and whether a reader needs both

They answer different questions and a reader may want only one.

The intersection grid accelerates the crossing search, which on this collection examines three candidates. Removing three candidates is worth nothing at all, and on a pattern whose halves are common it is worth a great deal — the previous essays’ plate over pattern lengths is the measurement, and it is a property of the pattern rather than of the collection.

The propagation grid accelerates the part that costs 4,352 of the 4,355 candidates. On a repetitive collection it is the whole of the saving.

So on the collection here the right structure is the propagation grid alone: 1,544 bits, 14% of the index, and essentially all of the query saving. The intersection grid earns its 1,696 on short patterns, small alphabets and collections where crossing occurrences are common, and it earns nothing on this one.

That is why the sizes are reported as parts and not as a total. A total answers “what does the accelerated index cost”, and the question a reader actually has is which of the two structures is needed, which a total cannot be asked.

A longer pattern has more split points and a smaller answer at eachThe crossing-occurrence search at five pattern lengths on 8,192 characters. A pattern of m characters is split at m − 1 places and each split is its own rectangle, so the number of queries grows with the pattern while the answer at each shrinks. The filter examines 5 candidates at m = 4 and 8 at m = 16; the grid examines 1 and 8. The two converge, because at a long pattern the ranges are already small and there is nothing left for a rectangle to remove.43658712111615candidates examinedpattern length · split points belowfilteringthe gridEnglish-like · z = 15616 occurrences throughout
Fig. 6 Where the first grid earns its bits: short patterns, where the intervals are large and the answers are few. At sixteen characters both policies examine the same eight candidates.

The arithmetic at a size no plate here can draw

Every number above is measured at z=156z = 156, and the trade it describes is decided by quantities that move with zz. Both sides of it can be written down, which is worth doing because the conclusion at the measured size and the conclusion at a realistic one are different.

The space ratio is zLzL over z(2log2n+log2σ)z(2\lceil\log_2 n\rceil + \lceil\log_2 \sigma\rceil) — grid over phrase table — which is log2z\log_2 z over about 2log2n2\log_2 n. On a repetitive collection zz is far below nn, so the ratio is well under a half and falls as the collection grows.

The query ratio is the range over the logarithm: the filter walks something proportional to zz per occurrence and the grid descends log2z\log_2 z levels. At z=156z = 156 that is 156 against 8 before constants, and after constants it was measured at 1.76. At zz a million it is a million against twenty.

So the space side is roughly constant in zz and the query side grows like z/logzz/\log z. There is no size at which building the grid gets worse and there are sizes at which it is the difference between a search and a scan. The measured 1.76 is not the trade; it is the trade evaluated at the smallest size that can be drawn, which is a limit is not a prediction read the other way — the asymptotics are right here and the constant is what a plate can show.

The two ladders here pull against each other

There is a second structure in the essays beside these whose whole purpose is to make zz larger, and it is worth putting the two together because a reader building both pays twice.

A parse that will not follow a long chain caps the copy depth, which is what the character that costs a chain charges for reading a character. It buys a shallow chain with phrases: a cap of eight on this collection takes zz from 156 to 170, and a cap of four takes it to 946.

Every one of those extra phrases is another point in both grids. At z=946z = 946 the grids are 946×10946 \times 10 bits of payload each rather than 156×8156 \times 8 — a factor of about seven and a half — while the index they sit on grows by the same factor. The share does not move much; the absolute cost does.

That is not an argument against either structure. It is the observation that the two questions these essays answer about one index have opposite signs in the same variable, and a plate showing either alone is drawn under an assumption about the other. The frontier at the end of the bounded-parse ladder is the picture that has both.

What it would cost to answer the question by storing the answer

The alternative nobody builds, offered for scale.

The search’s real question is “for each split point, which boundaries have this left half and this right half”. A structure answering it by storing the answers would be a table indexed by pattern, which is a table over every substring of the collection — larger than the text by orders of magnitude and the thing an index larger than what it indexes is about.

The other extreme is to store nothing and scan the text, which is nn per query rather than zz. The filtering policy is between the two, at zz per occurrence; the grid is at logz\log z. So the four options span the whole range from a table too big to build to a scan too slow to run, and the two in the middle differ by exactly the structure this essay prices.

That is the frame the frontier between time and space sets up for sorting, arriving in a compressed index: the interesting structures are never the endpoints, and the question is always which point on the curve the collection sits at.

The published bound, and what is not claimed

The structures the literature builds for this are zlogz(1+o(1))z\log z(1 + o(1)) bits with O((t+1)logz)O((t+1)\log z) query time, and some of them do better on the constant with a compressed representation of the bit vectors.

Nothing here claims that bound. What is reported is what these bit vectors and these directories cost, measured the way every other size on this site is measured, which is the same accounting the index that stores the runs and an index with z in its size use for the structures they build. The payload happens to be exactly zlog2zz\lceil\log_2 z\rceil; the rest is a directory whose parameters somebody typed.

The gap between “what the bound says” and “what this costs” is the directory, and it is 38%. A compressed bit vector would shrink the payload on a grid whose permutation has structure and would leave the directory where it is — which is the finding the transform that emits nothing reaches from the other direction, that a compressed representation is compressed only where the thing inside it has runs.

What a size like this is usually reported as, and why that is worse

Two conventions are common and both would have hidden something measured above.

A ratio to the text. “The index is 0.74 bits per character” is the form most papers in this field use, and on a repetitive collection it is a statement about how many copies were concatenated rather than about the structure. The whole point of a structure proportional to zz is that its size stops moving while the text grows, so bits per character falls by a factor of thirty-two across this sweep and reports a structure that never changed.

A single total. “The accelerated index is 12,132 bits” answers a question nobody has. A reader wants to know whether to build the second grid, and a total is the one shape of report that cannot say.

The convention this collection uses instead is parts, in bits, with the collection’s size stated beside them — the same one measuring what an algorithm keeps sets up for auxiliary space and what a window costs in bits for a streaming summary. It is more numbers and it is the only form in which the question of which structure is needed can be answered.

The marginal number is quoted for the wrong grid

The marginal accounting is done once, for the intersection grid, and the recommendation two sections later is to build the other one. Applying the same subtraction to the structure a reader is actually being told to build changes the closing figure by more than the argument that produced it.

The propagation grid holds the by-source relation, which is exactly what the third stored ordering holds — so if the reasoning that kills one ordering when the intersection grid arrives holds by symmetry, the same 1,248 bits go when the propagation grid does. That gives:

  • both grids — 3,240 gross, two orderings dead, 744 marginal, 8.4% of the index;
  • intersection only — 1,696 gross, 448 marginal, 5.0%;
  • propagation only — 1,544 gross, 296 marginal, 3.3%.

So the recommended structure — the propagation grid alone, which carries 4,352 of the 4,355 candidates and therefore essentially the whole saving — costs 296 bits on an 8,892-bit index. Not twenty-seven per cent, not three per cent for a quarter of the saving: three per cent for all of it.

That is a materially different sentence from the one this page opens with, and both are true of the same measurement. Twenty-seven per cent is what is built; 8.4% is what building both costs a structure that then drops what they replace; 3.3% is what the useful half costs. A reader deciding needs the third, and the page hands them the second, computed for the half it goes on to say is worthless here.

The general form is worth keeping, because it is a trap this collection’s own accounting convention sets. Reporting a size as parts makes a marginal analysis possible and does not make it consistent: the subtraction has to be redone for every configuration the parts allow, and there are three here. A single marginal number attached to a parts table reads as though it applied to the table, and it applies to one row of it.

Which sharpens the case rather than weakening it. At 3.3% the trade the operations a candidate count leaves out prices at 1.76 times on this collection is not a close decision at any size, and the arithmetic two sections up says the query side grows like z/logzz/\log z while the space side stays flat. The structure was always worth building; the number that says so is a tenth of the one on the plate, and it took the same subtraction the page already performs, done once more.

What is being claimed

The two grids are 3,240 bits against an index of 8,892, twenty-seven per cent of the structure, before any query.

Their payload is exactly zlog2zz\lceil\log_2 z\rceil and their directory is 38% on top of it. The lower-order term of the published bound is measured here rather than dropped.

The marginal cost is 448 bits, not 1,696, because the grid replaces one of the three stored orderings — and a reader deciding should use the marginal number while a reader counting what is built should use the other.

The share is flat as the collection repeats and falls slowly, because the index carries a logn\log n the grids do not.

And a reader on this collection wants one of the two grids, the propagation one, which is 14% of the index and essentially all of the saving. Which of the two earns its bits is a property of the collection and the pattern rather than of the structures, so the answer for a different corpus is a different measurement and not a different reading of this one.

Three compressed indexes, three measures of the same collectionEnglish-like of 512 characters repeated up to 32 times. The entropy-bounded FM-index follows n·H_k and grows with the collection, from 3,511 bits to 34,615; the r-index follows r and grows only in the width of a position; the phrase index follows z and is the smallest of the three at every point past the first, ending at 8,892 bits — 3.89x under the entropy-bounded structure. None of the three holds a text. Both axes are logarithmic.1,00010,00010⁴bits12481632characters in the collection · copies aboveFM-index, compressedr-indexphrase indexEnglish-like · divergence 0z 156 · r 233
Fig. 7 The three compressed indexes this field has built, for scale: the structure this ladder accelerates is the smallest of them, and the acceleration is a quarter of it again.

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

The 8 essays that link to this one and share the most of its objects, of 12 that link here.

The objects this essay names

Each one links to every other essay that touches it.

Bit vectorConstant factorGridIndex sizeLempel ziv parseMeasurementOutput-sensitivePermutationRange reportingSelf-indexSpace overheadTrade offWavelet tree