What is taught wrongly

Three savings in three currencies

The same six occurrences, found four ways. The table computes 60,000 cells and reads 60,000 characters. The counting filter computes 6,251 cells and reads 13,022 characters. The seed filter computes 3,325 and reads 550. The index walk computes none, reads none, and performs 18,645 ranks.

Four methods, one question: where does this pattern occur with at most two errors? Every one of them is required to find what the whole table finds, checked against it rather than against each other.

The table computes 60,000 cells and reads 60,000 characters. The counting filter computes 6,251 cells and reads 13,022. The seed filter computes 3,325 cells, reads 550 characters and performs 554 index ranks. The index walk computes no cell, reads no character, and performs 18,645 ranks.

Four ways to find the same 6 occurrences, k = 3A pattern of 20 characters against 3,000, with every method required to find what the table finds. The bar is cells computed; beside it are characters read and index ranks performed. The walk computes no cell and reads no character and spends 177,046 ranks; the counting filter reads every character and computes 24,843 cells; the seed filter reads 2,415 and computes 17,745. The three columns are three resources and are not added up, because which of them is scarce is a fact about the machine rather than about the question.the whole table60,00060,000r · 0rkcounting filter24,84315,369r · 0rkseed filter17,7452,415r · 1,377rkindex walk00r · 177,046rkn = 3,000 · m = 20 · q = 4cells drawn · r = characters read, rk = index rankscells computed6 occurrences
Fig. 1 Four methods on one question at a budget of three, with the bar in cells and the two other currencies beside it. Nothing here is summed.

Why there is no total

The three columns are three resources and this collection does not add them up.

A table cell is three additions and a minimum, over data that is in a row of an array. A character read is a memory access into a text, which may or may not be resident. An index rank is a directory lookup and a popcount over a bit vector, and on a wavelet tree it is several of them.

Which of the three is scarce is a property of a machine and of a deployment: a corpus too large to hold makes the reads the binding constraint, a cache-resident text makes the cells cheap, and an index in memory makes the ranks nearly free. No ranking of these methods survives changing that.

That is this collection’s standing position, established for two counters and now needed for three. The count somebody chose is the general argument and one run, two counts is the first case; the third currency arrives here because the index walk spends something the other three do not.

Four ways to find the same 6 occurrences, k = 3A pattern of 20 characters against 4,000, with every method required to find what the table finds. The bar is cells computed; beside it are characters read and index ranks performed. The walk computes no cell and reads no character and spends 202,896 ranks; the counting filter reads every character and computes 27,885 cells; the seed filter reads 3,059 and computes 22,477. The three columns are three resources and are not added up, because which of them is scarce is a fact about the machine rather than about the question.the whole table80,00080,000r · 0rkcounting filter27,88519,783r · 0rkseed filter22,4773,059r · 2,014rkindex walk00r · 202,896rkn = 4,000 · m = 20 · q = 4cells drawn · r = characters read, rk = index rankscells computed6 occurrences
Fig. 2 The same four at a longer text. The table’s two columns scale with it and the walk’s ranks barely move, so the ordering within any one column is a function of the text size.

The four methods in one paragraph each

The table. Fill an n×mn \times m dynamic-programming rectangle with a zero row at the top, and read off the columns whose final entry is at most kk. It is the baseline every other method is checked against, and it is the only one whose cost is completely independent of the budget: 60,000 cells whether kk is zero or three, because kk only decides which of the filled cells count as answers.

The counting filter. Slide a window of m+km + k characters, count how many qq-grams it shares with the pattern, and verify the windows reaching m+1q(k+1)m + 1 - q(k+1). It reads every character exactly once — 13,022 of them at k=2k = 2, which is the text plus the window bookkeeping — and computes a banded table per candidate.

The seed filter. Cut the pattern into k+1k+1 pieces, look each up exactly in a self-index, and verify a window around each hit. It reads almost nothing — 550 characters at k=2k = 2, four per cent of the text — because the index answers the exact queries and only the verification touches the text.

The index walk. Walk the backward search as a tree, spending budget on each mismatching step. It reads nothing at all and spends ranks.

Two filters, one question, two currenciesA pattern of 20 characters against 3,000, answered identically by both filters at every k — checked against the whole table rather than assumed. The bar is cells computed in the verification stage; the number beside it is characters read. The counting filter reads every character and the seed filter reads 15.9% of them, and whichever of the two is the scarce resource decides which filter is better. The table's own cost is 60,000 cells and 60,000 reads at every row.k = 0: q-grams34812,108 readsk = 0: seeds348120 readsk = 1: q-grams2,78412,597 readsk = 1: seeds1,728378 readsk = 2: q-grams6,78313,110 readsk = 2: seeds5,320880 readsk = 3: q-grams35,49016,818 readsk = 3: seeds22,4773,059 readsq = 4 · cells drawn · reads besidecells computed in verificationtable: 60,000 cells
Fig. 3 Two of the four in detail, at four budgets. The cells are drawn and the reads are beside them, and the two columns rank the filters differently.

The fourth column nobody spends

There is a resource none of the four columns names and it belongs in the comparison, because it is the one that decides whether three of the methods are available at all.

Two of them need a structure built in advance. The seed filter and the walk both query a self-index, which costs a suffix array to build and a few bits a character to keep — 8,426 bits over a collection of sixteen thousand characters for the run-length structure this phase measures, or 36,169 for an entropy-bounded one over eight thousand characters of English.

The other two need nothing but the text. The table and the counting filter can answer a query on a corpus that arrived a moment ago, and neither has a build step.

That is not a small distinction. A one-off query against a text nobody has indexed makes the two structure-free methods the only candidates whatever the plates say, and a corpus queried a million times makes the index’s build cost irrelevant. The four-way plate is drawn under the assumption that the index exists, which is stated here rather than left as an assumption.

An index larger than what it indexes is where this collection weighed that structure, and the index that is smaller than the text is where it became small enough to be a reasonable prerequisite.

Four ways to index 16,384 characters, weighedEach bar is what the structure retains, computed from its own shape rather than from a serialisation, on English-like of 16,384 characters. The dashed rule is the packed text at 81,920 bits. All four return the same occurrences for the same pattern; they differ in size by 147 times. One of them cannot answer at all unless the text is kept beside it, and its bar includes that text.suffix array + text311,29619.00 b/chcounter array per symbol5,407,710330.06 b/chFM-index, plain100,9476.16 b/chFM-index, compressed36,8042.25 b/chthe packed textone bar shaded darker needs the text · English-likesigma 21, sample 64
Fig. 4 The prerequisite two of the four methods carry. Its size is the fourth column, it is paid once, and nothing in the query plates shows it.

Where each one wins, by budget

At k=0k = 0 the seed filter is a single exact query: 232 cells, 80 characters, 304 ranks. The walk is 20 interval extensions and 304 ranks. Both are three orders of magnitude under the table and there is nothing to choose between them.

At k=1k = 1 the seed filter computes 1,152 cells against the counting filter’s 1,248, and the walk performs 1,700 ranks. All three are far under the table.

At k=2k = 2 the seed filter is ahead on cells (3,325 against 6,251) and far ahead on reads (550 against 13,022), and the walk’s ranks have grown elevenfold to 18,645.

At k=3k = 3 the seed filter computes 17,745 cells and reads 2,415 characters; the counting filter computes 24,843 and reads 15,369; the walk performs 177,046 ranks. Every method is within an order of magnitude of the table and the walk’s growth has become the dominant term in its own column.

The tree an error budget opens, against a rectangle that does not moveA pattern of 20 characters over 4 symbols in 3,000 characters. The rising line is the interval extensions the walk performs, multiplying by 17, 12, 10 for each error added; the flat line is the whole dynamic-programming table, which costs 60,000 cells whatever k is because k only decides which of the filled cells count as answers. They do not cross inside this range. The vertical axis is logarithmic.10010³10⁴errors allowed, kacts0123index walkthe whole table3,000 characters · m = 20 · 4 symbolsno crossing in range
Fig. 5 The walk’s column alone, against the table’s flat cost. Its shape is what makes the four-way comparison change hands as the budget rises.

What each method is a function of

The reason the ordering moves is that the four costs depend on different things, and a plate at one budget on one text cannot show that.

The table is nmnm: the text length times the pattern length, and nothing else.

The counting filter is nn reads plus a verification per candidate, and the candidate count depends on the threshold and on how often a random window shares that many qq-grams — so it depends on the alphabet, and it collapses when the threshold reaches zero.

The seed filter is a handful of index queries plus a verification per candidate, and its candidate count depends on how often a seed of length m/(k+1)m/(k+1) occurs by chance — so it depends on the alphabet and the text length, and it degrades as the seeds shorten.

The walk is a tree whose size depends on the budget, the alphabet and how quickly intervals empty — so it depends on the text length only weakly, and it grows by about an order of magnitude per error.

Four different functions of four different subsets of the same parameters. That is why the four-way plate is drawn at several budgets and several lengths rather than once.

The one thing they have in common

Every one of the four returns the same occurrences, and that is checked rather than assumed at every point of every sweep.

The check is not “the counts agree”. The table reports the positions where an occurrence ends and the three other methods report where one begins, so the comparison converts starts into the windows of ends they could produce and requires every end the table found to be covered.

That conversion is generous in one direction — a method reporting spurious starts still covers every real end — so it is paired with the exact check at k=0k = 0, where the answer sets must be equal. Together they are tight enough that a method returning too much or too little fails one of them.

Getting this wrong is easy and this collection has recorded doing so: the filter that feeds the table notes that starts and ends are different questions, which is the index field’s own note about count and locate one level up.

Three limits arriving at the same error rate

The most striking thing in the sweep is not any single column; it is that all three fast methods run out at about the same place.

The counting filter’s threshold reaches zero at q(k+1)=m+1q(k+1) = m+1, which for a twenty-four-character pattern at q=5q = 5 is k=4k = 4. The seed filter’s seeds fall below logσn\log_\sigma n at about the same budget: on the four-thousand-character sweep with a twenty-four-character pattern it proposes 649 candidates at k=4k = 4 and computes 160,446 cells against the table’s 96,000 — 1.7 times worse than not filtering. The walk crosses the table’s cost between k=3k = 3 and k=4k = 4.

Three mechanisms, three completely different arguments, one error rate: about a fifth of the pattern’s length.

That is not a coincidence and it is not a deep theorem either. At k/m0.2k/m \approx 0.2 a pattern stops constraining a window: enough of it can be changed that the surviving structure — a shared qq-gram, an intact seed, a prefix that still occurs — no longer separates an occurrence from a random stretch of text. Every method here exploits some such structure and they all lose it at once.

What a reader should do with four methods

The decision procedure is short and it is not “benchmark them”.

Compute the error rate. If k/mk/m is above about a fifth, none of the three fast methods will help and the table is the answer. That is arithmetic and costs nothing.

Ask which resource is scarce. If the text is not resident, the walk is the only method that never touches it. If the index is not built, the counting filter is the only method that does not need one. If both are available, the seed filter is cheapest on this instance in both of the columns it spends.

Then measure the candidate fraction, once, on a sample. That is the only number in this essay that cannot be computed in advance, and it decides the size of the verification, which is the dominant term for both filters.

Four ways to find the same 10 occurrences, k = 3A pattern of 24 characters against 3,000, with every method required to find what the table finds. The bar is cells computed; beside it are characters read and index ranks performed. The walk computes no cell and reads no character and spends 179,853 ranks; the counting filter reads every character and computes 16,400 cells; the seed filter reads 1,053 and computes 7,995. The three columns are three resources and are not added up, because which of them is scarce is a fact about the machine rather than about the question.the whole table72,00072,000r · 0rkcounting filter16,40014,148r · 0rkseed filter7,9951,053r · 766rkindex walk00r · 179,853rkn = 3,000 · m = 24 · q = 4cells drawn · r = characters read, rk = index rankscells computed10 occurrences
Fig. 6 The four methods at a longer pattern, where every fast method is further from the table because the same budget is a smaller error rate.

What happens if the pattern gets longer

Every number above is at one pattern length, and lengthening the pattern moves all four methods in the same direction for different reasons — which is the cleanest demonstration that the columns are not measuring one thing.

The table grows linearly: nmnm cells, so a pattern half again as long costs half again as much.

The counting filter improves: the threshold m+1q(k+1)m + 1 - q(k+1) rises with mm at fixed kk, so the same budget is a smaller error rate and fewer windows pass. At m=32m = 32 and q=5q = 5 the threshold at k=4k = 4 is 8 rather than 0, and the filter is alive where it had collapsed.

The seed filter improves too: the seeds are m/(k+1)m/(k+1) characters, so a longer pattern gives longer seeds, which occur by chance less often.

The walk improves: a longer pattern empties its intervals sooner, so the tree is smaller — 39,957 extensions at m=16m = 16 against fewer at m=20m = 20 for the same budget.

Three methods get better and the baseline gets worse, so every ratio in this essay understates the fast methods on long patterns. The error rate, again, is the variable that matters rather than the budget.

The three limits are functions of different variables

“Three mechanisms, one error rate” is the striking observation, and it dissolves under arithmetic in a way worth following, because the dissolution is what stops a fifth of the pattern from becoming a rule of thumb.

Write the two filter limits out. The counting filter’s threshold reaches zero at q(k+1)=m+1q(k+1) = m+1, so it dies at

km    1q.\frac{k}{m} \;\approx\; \frac{1}{q}.

The seed filter dies when its seeds stop being selective, which is when the seed length m/(k+1)m/(k+1) falls below logσn\log_\sigma n — the length at which a string starts occurring by chance in a text of nn symbols. So it dies at

km    1logσn.\frac{k}{m} \;\approx\; \frac{1}{\log_\sigma n}.

One of those contains no nn and the other is governed by it. They agree here because qq was chosen to be four or five and log44,000\log_4 4{,}000 is 5.98 — and qq is chosen that way on purpose, since a qq-gram shorter than logσn\log_\sigma n occurs everywhere and one much longer is destroyed by every error. The two thresholds coincide on this sweep because the same design rule set both of them, not because the two mechanisms are secretly one mechanism.

That matters as soon as the text is a realistic size. On a four-symbol corpus of a billion symbols, logσn\log_\sigma n is fifteen, so a well-chosen qq is fifteen and both limits fall to

km    115    0.067,\frac{k}{m} \;\approx\; \frac{1}{15} \;\approx\; 0.067,

which is a third of the rate measured here. A twenty-four-character pattern that tolerates four errors on four thousand symbols tolerates one on a billion, and every one of the fast methods is back at the table’s cost by two.

So the number to carry out of the sweep is not a fifth. It is 1/logσn1/\log_\sigma n, and the fifth is what that expression evaluates to on a text small enough for a full dynamic-programming table to serve as the baseline — which is the constraint the honest limit at the end of this essay already names, arriving as a bound on the conclusion rather than on the measurement. A limit is not a prediction is normally about extrapolating a class past its measured range; this is the same move with a constant, and the constant is the one a reader would most want to reuse.

It also says which of the three limits is the robust one. The walk’s cost grows by roughly an order of magnitude per error whatever the text is, so its crossing with the table moves only with the table’s own nmnm — slowly, and in its favour as the text grows. The two filters’ limits both tighten with nn. On a large corpus the walk outlives the filters, which reverses the ordering this sweep reports at its highest budget, and is the sort of reversal the filter that proposes everything is about: a filter that stops filtering does not fail, it quietly becomes the thing it was placed in front of.

The comparison this essay does not make

There is no wall-clock number anywhere in this collection and there is none here, which is worth restating at the point where a reader most wants one.

A rank, a cell and a character read have different costs on a real machine and the ratios depend on the cache, on the representation, on the branch predictor and on the size of the index. This collection has a counter for cache misses and one for branch mispredictions, and neither has been pointed at any of these four methods.

What the counts do give is invariance. The four columns above are the same on any machine, for the same inputs, and they will be the same in five years. A timing would not be, and counting instead of timing is the founding decision that makes every plate here reproducible.

The honest form of the recommendation is therefore conditional: given a machine’s ratios between the three currencies, the plates decide. Producing those ratios is somebody else’s measurement and this collection does not have it.

The same walk over three alphabets, k = 2Interval extensions for one pattern of 14 characters in 3,000, over two, four and twenty-six symbols. The branching factor is the alphabet, so a wider one should cost more — and between two symbols and four it costs 1.4x less, because a wider alphabet empties an interval sooner and because the answer itself is smaller: 89 strings are within 2 errors of the pattern over two symbols against 4 over four. Between four and twenty-six the branching wins. The cost is not monotone in the alphabet and neither effect is the answer alone.2 symbols5,39689 within 2 errors4 symbols3,7424 within 2 errors26 symbols10,3483 within 2 errorsn = 3,000 · m = 14interval extensionsk = 2
Fig. 7 One more variable the four-way plate holds fixed. The alphabet moves one method’s cost non-monotonically and the others’ hardly at all.

Why this is in the wrong-answers field

The statement this essay is against is the reasonable-sounding one: that among methods for the same problem, one is fastest and benchmarking finds it.

For this problem, at this budget, on this text, the four methods spend 60,000 cells, 6,251 cells and 13,022 reads, 3,325 cells and 550 reads, and 18,645 ranks. There is no number that ranks those, and constructing one requires choosing exchange rates that belong to a machine rather than to an algorithm.

The mistake it produces in practice is not choosing wrongly; it is choosing without knowing that a choice was being made. A benchmark on a machine with the text in cache reports the counting filter as competitive; the same benchmark on a corpus that does not fit reports it as hopeless; and neither run mentions that the difference is the exchange rate rather than the algorithms.

The block that is not a block is the same complaint about a model parameter, and two searches, one comparison count is the case where two algorithms agree in one currency and differ in another.

Four ways to find the same 6 occurrences, k = 2A pattern of 20 characters against 3,000, with every method required to find what the table finds. The bar is cells computed; beside it are characters read and index ranks performed. The walk computes no cell and reads no character and spends 18,645 ranks; the counting filter reads every character and computes 7,980 cells; the seed filter reads 550 and computes 3,325. The three columns are three resources and are not added up, because which of them is scarce is a fact about the machine rather than about the question.the whole table60,00060,000r · 0rkcounting filter7,98016,300r · 0rkseed filter3,325550r · 554rkindex walk00r · 18,645rkn = 3,000 · m = 20 · q = 5cells drawn · r = characters read, rk = index rankscells computed6 occurrences
Fig. 8 The same comparison with a different q for the counting filter, which moves one column and nothing else. Every parameter here belongs to one method.

The honest limit

Every measurement is on a four-symbol alphabet with occurrences planted by substitution, three thousand to four thousand characters, patterns of sixteen to twenty-four. That is the regime where a full table is affordable as a baseline, which is what makes the correctness checks possible and what limits the lengths.

The seed filter’s index is sampled at one value in thirty-two and the walk’s is the same index, so both carry a locate cost that is not in these columns. The counting filter and the table carry none, because their answers are positions already.

And the walk has no lower-bound pruning, so its rank column is an upper bound. That does not change the shape — the growth per error is what makes it lose at k=4k = 4 — but a careful implementation would move the crossing by some amount this collection has not measured.

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.

Approximate matchingBacktracking searchCounting filterEdit distanceError budgetFilteringFM-indexMeasurementQ gramSeed-and-extendSelectivityTrade offUnit of costVerification