The data that is not a number

The search that starts in the middle

The same pattern, the same six occurrences, the same index — and 830 interval extensions, or 303, according to which end the search begins at. A pattern cut into three pieces has an error-free one, and only a search with two ends can start there.

Four ways of consuming one pattern. The same index, the same six positions found, the same check against comparing the pattern at every position of the text — and 830, 877, 447 or 303 interval extensions to get there.

Nothing about the problem changed between those numbers. What changed is the order the pattern’s characters were fed to the index in, and the constraints the search was allowed to impose because of that order.

The same occurrences, at four sizes of treeA pattern of 15 characters within 2 substitutions, over 4,000 characters of four symbols. All four schemes found the same 6 positions — that is checked against comparing the pattern at every position — and they walked 830, 877, 447 and 303 interval extensions to do it. The gap between the first and the last is 2.74x, and it comes from one thing: a search that begins at a piece it insists is exact spends nothing on the errors that piece cannot have.right to left8302.74xleft to right8772.89xone per piece4471.48xthe scheme3031.00xinterval extensionsall four found the same 6 positions15 characters · k = 22.74x apart
Fig. 1 Four schedules, one problem. All four found the same six positions; the bars are what each spent finding them.

The pigeonhole, used differently

Cut a pattern of m characters into k + 1 pieces. If the whole pattern matches somewhere with at most k errors, those errors fall into the pieces, and k errors cannot touch k + 1 pieces — so at least one piece matches exactly.

That is the observation the filter strand is built on. The q-grams an error cannot destroy uses it one way: search for each piece exactly, then verify the neighbourhood of each hit. The pieces are found by an exact matcher, the verification is a table computation, and the index is a lookup device.

A search scheme uses the same observation inside the index. Instead of finding a piece and then leaving, the search begins at a piece, matches it with the budget set to zero, and then extends outwards through the rest of the pattern with the budget opened up.

The difference matters because of where the two spend their work. A filter’s cost is a candidate count times a verification cost, and the filter that proposes everything is the essay about that product collapsing. A scheme’s cost is a tree, and the tree’s shape is decided in its first few levels.

Why the first levels are what matter

An index walk with an error budget branches. At each pattern character it can match — one extension — or spend an error, which means trying every other symbol.

So the tree multiplies. On four symbols, an error at some depth turns one live state into up to four, and each of those goes on to produce its own subtree. The branches an error opens measured the multiplier at between eight and twenty per error depending on the alphabet, which is a very steep growth for k of two or three.

A constraint applied at depth two therefore removes far more than the same constraint applied at depth twelve: it removes everything the pruned branch would have produced. Forbidding errors in the first piece searched is the strongest constraint of that kind available, and its whole value depends on that piece being searched first.

The gap widens with the budgetThe same pattern at three error budgets. Every tree grows by roughly a factor of ten per error — the branching a walk with a budget does — and the schemes grow more slowly than the plain search does, because the constraint they impose bites harder the more branches there are to cut. At one error the scheme's tree is 1.31x smaller and at 3 it is 3.14x smaller. The vertical axis is logarithmic.110010³errors allowedinterval extensionsright to leftone per piecethe scheme18 characters · four symbols3.14x at k = 3
Fig. 2 The tree against the budget, for three ways of consuming the pattern. Every line rises steeply; what the schedules change is how steeply.

It is worth putting a number on how steep that is, because the intuition understates it. At two errors over four symbols the plain walk explores 772 extensions for a pattern of eighteen characters; at three it explores 2,932. The pattern did not change and the text did not change — one more error was allowed, and the tree nearly quadrupled.

Against that, the cost of any constraint imposed at the root is enormous and the cost of the same constraint imposed near the leaves is nearly nothing. That asymmetry is the whole reason a schedule is worth having, and it is the reason a schedule is worth having more the larger the budget is.

What a unidirectional search cannot do

A backward search consumes the pattern from its last character. Its intervals are always the rows for a suffix of the pattern, and to reach the middle piece it must pass through the last piece first.

So it cannot demand that the middle piece be exact without having already searched the pieces to its right, with the budget open, and paid for the tree that produces. By the time it arrives at the middle, the constraint it could impose there is worth very little.

What it can do is the pigeonhole’s weaker form: run k + 1 separate searches, the i-th of which insists that piece i is exact. Each of those searches still has to reach piece i, so all but one of them is doing exactly the walk described above. That is the pigeonhole row on the plates, and it is a genuine improvement — 447 extensions against 830 — because most of its searches die early.

A bidirectional index removes the “has to reach it” clause. A search can begin at any piece and grow outwards, so the exact piece is at the root of the tree in every one of the scheme’s searches.

The three searches, and where each allows its errorsA pattern of 15 characters is cut into 3 pieces, and a search is an order to visit them in plus a ceiling on the errors allowed so far after each. Every one of these three demands its FIRST piece be matched exactly — the number in its first cell is zero — which is a far stronger condition than "some piece is exact", because it applies from the first character rather than after the fact. Two of them then allow at most one error after their second piece where a scheme built straight from the pigeonhole would allow 2. The order matters because a piece can only be searched when it touches what has already been searched: the third search starts in the middle and grows outwards, which no index extended in one direction can do.piece 1piece 2piece 35 characters5 characters5 characterssearch 11 → 2 → 3≤ 01st≤ 12nd≤ 23rdsearch 22 → 1 → 3≤ 12nd≤ 01st≤ 23rdsearch 33 → 2 → 1≤ 23rd≤ 22nd≤ 01stthe ceiling on errors so far, and the order each piece is visited ink = 2 · 3 pieces3 searches
Fig. 3 The three searches of the scheme, drawn as an order and a ceiling. Every one of them starts at a piece it insists on matching exactly, and the third starts in the middle.

The measurement

Fifteen characters, two errors allowed, four thousand characters of four-symbol text with the pattern planted six times at two errors each.

schedule searches extensions dead ranks
right to left 1 830 391 13,806
left to right 1 877 423 14,976
one search per piece 3 447 323 7,044
the scheme 3 303 205 4,916

All four found the same six positions with the same error counts, checked against comparing the pattern at every position of the text.

A factor of 2.74 between the first row and the last, on a problem where nothing but the order of consumption differs. And the ranks fall further than the extensions do — 2.81 — because a scheme’s dead extensions are concentrated where intervals are wide and each of them is one rank rather than a subtree.

How much of each tree finds nothingEvery extension either narrows the interval or empties it. An emptied one is the search learning that a branch holds no occurrence, which is work done to rule something out — and it is where a scheme's benefit shows up, because a tighter bound turns a whole subtree into one dead extension. 47% of the right-to-left search's extensions are dead against 68% of the scheme's, on trees whose sizes differ by 2.74x.right to left830391 deadleft to right877423 deadone per piece447323 deadthe scheme303205 deadextensions · solid is the ones that narrowed an interval15 characters · k = 26 occurrences
Fig. 4 How much of each tree is an extension that finds nothing. Ruling a branch out is work; a schedule’s benefit is turning a subtree into a single dead extension.

The saving grows with the budget

At one error the ratio is 1.31. At two it is 2.22. At three it is 3.14.

That is the shape the argument predicts, and it is worth checking rather than assuming, because the opposite is also arguable — a larger budget means more errors to distribute, which means more searches in a pigeonhole-style scheme and more overlap between them.

What settles it is that the tree multiplies and the searches only add. Three searches at k = 2 explore three trees, but each is rooted at a constraint that removes a factor of the branching, and a factor beats a summand as soon as the budget is more than one.

budget one search one per piece the scheme
1 157 120 120
2 772 347 266
3 2,932 935 935

At k = 1 and k = 3 the scheme and the pigeonhole coincide, because at those budgets the enumeration that produced the scheme finds nothing better than the pigeonhole’s bounds. At k = 2 it finds a triple whose bounds are tighter, and that is where the extra factor of 1.3 comes from.

The gap widens with the budgetThe same pattern at three error budgets. Every tree grows by roughly a factor of ten per error — the branching a walk with a budget does — and the schemes grow more slowly than the plain search does, because the constraint they impose bites harder the more branches there are to cut. At one error the scheme's tree is 1.41x smaller and at 3 it is 13x smaller. The vertical axis is logarithmic.110³10⁴errors allowedinterval extensionsright to leftone per piecethe scheme18 characters · twenty-six symbols13x at k = 3
Fig. 5 The same three budgets over twenty-six symbols. The lines are steeper and further apart, which is the alphabet making both the branching and the pruning stronger.

Why three searches are not three times the work

A reader meeting a scheme for the first time reasonably asks why running three searches is not three times as expensive as running one.

The answer is that two of the three die almost immediately. Each search begins by matching its own piece exactly, and an exact match of five characters over four symbols narrows the interval to a handful of rows or to none at all. A search whose first piece has an error in it — which is two of the three, for any given occurrence — finds nothing at that first step and stops.

So the three searches share the text between them rather than each paying for all of it, and the total is closer to one search’s worth of surviving states plus two searches’ worth of first pieces. The plate that shows this is the dead-extension one: the scheme’s 303 extensions include 205 that found nothing, which is a higher share than the plain walk’s 391 of 830 and a much smaller number.

Where the pieces go

A pattern of fifteen characters cut into three pieces gives five, five and five. That is the obvious cut and it is not obviously right.

The first piece searched is the one that must be exact, and a longer first piece is a stronger constraint — it narrows the interval further before any branching starts. A shorter first piece leaves more of the pattern to be searched under the budget. The best cut is therefore not equal, and it depends on the alphabet, since a piece’s selectivity is σ to the power of its length.

Nothing here optimises it. The pieces are equal, and that is stated rather than tuned, because a strand comparing four schedules has to hold everything else fixed — a scheme measured with its own best cut against a plain search measured with an arbitrary one would be measuring two things at once. What the equal cut costs is a deferral, and it is a real one: the same pattern with pieces of six, five and four is a different measurement and nothing here takes it.

The equal cut also interacts with the budget in a way worth naming. At k errors there are k + 1 pieces, so a longer pattern is cut into the same number of longer pieces and a larger budget cuts the same pattern into more, shorter ones. At three errors a fifteen-character pattern gives pieces of four, and a four-character exact match over four symbols selects one row in 256 — which on a text of four thousand characters is fifteen candidate rows before any branching starts.

That is the point at which the first piece stops being a strong constraint, and it is the same collapse the filter strand measured from the outside. The difference is where each is left: a filter that stops being selective has to verify everything, while a scheme that stops being selective has merely lost its advantage and still walks a bounded tree.

The equal cut is not arbitrary after all

The deferral above is stated as a deferral, and part of it can be closed with arithmetic rather than with a second measurement — the part that matters, because it decides whether the equal cut is a shortcut or the answer.

The reasoning that makes an unequal cut look attractive considers one search: a longer first piece is a stronger constraint, so lengthening it prunes harder. That is true and it is about the wrong object. A scheme runs k+1k+1 searches, and lengthening one piece shortens another, so the quantity to minimise is the total over all of them.

Write it out. A first piece of length \ell matched exactly over σ\sigma symbols leaves about n/σn/\sigma^{\ell} surviving rows on a text of nn characters, and each of those roots a subtree. The scheme’s total root count is therefore inσi\sum_i n\sigma^{-\ell_i}, with the lengths constrained to sum to mm.

And σ\sigma^{-\ell} is convex. A sum of a convex function over arguments with a fixed total is smallest when the arguments are equal — which is the opposite conclusion to the one-search reasoning, and it is not close.

The numbers say how not close. Fifteen characters over four symbols on four thousand characters of text: an equal cut of five, five and five leaves 4000/45=3.94000/4^5 = 3.9 rows per search and 11.7 across the three. A cut of six, five and four leaves 0.98, 3.9 and 15.6 — 20.5, three-quarters again as many roots, because the four-character piece selects one row in 256 and gives back far more than the six-character piece saves. Push it to seven, five and three and the total is 66.7, nearly six times the equal cut.

The asymmetry is the whole of it. Lengthening a piece divides its survivors by σ\sigma; shortening one multiplies them by σ\sigma. Multiplication and division of the same factor do not cancel when they are applied to numbers of different sizes, and the shortest piece is always the largest term in the sum.

So the equal cut is optimal for this term, and the term is the dominant one — the remaining walk’s size varies with mim - \ell_i far more gently than σi\sigma^{-\ell_i} varies with i\ell_i, since one is roughly linear in the remaining length and the other is exponential in the piece length. What is left of the deferral is a second-order correction that could shift the best cut by a character, in the direction of very slightly unequal pieces, and cannot reach the six-five-four that the one-search argument recommends.

That reverses the note above, and it is worth saying which of the two arguments was wrong rather than quietly replacing it. The one-search argument was right about its own object and applied to the wrong one, which is the same error as pricing a filter by its selectivity and forgetting that the q-grams an error cannot destroy has to search for every piece. The budget forces k+1k+1 pieces; the pieces are searched; the cost is a sum, and a sum of a convex function has its minimum where nothing is short.

One consequence for the branches an error opens’s collapse at large budgets: it cannot be delayed by cutting cleverly. At three errors the equal cut gives pieces of four and is already the best available, so the fifteen candidate rows before branching are a floor rather than an artefact of a lazy partition.

The alphabet, and why the effect survives it

A wider alphabet branches more per error, which makes the tree bigger, and empties intervals faster, which makes branches die sooner. Both effects are large and they pull in opposite directions.

Measured across three alphabets at two errors: the plain right-to-left search explores 625 extensions on two symbols, 772 on four, and 4,560 on twenty-six. The scheme explores 298, 266 and 904. So the branching wins on both, and the scheme’s advantage widens — from 2.1 to 5.0 — because the thing it removes is precisely the branching.

The tree against the alphabet, with and without a scheduleA larger alphabet cuts both ways and this is the plate that shows which way wins. Each extra symbol is another branch to try at every node, so the tree grows; and each extra symbol makes an interval empty sooner, so the branches die faster. Measured over 3 alphabets at 2 errors: 625 extensions at 3 symbols and 4,560 at 27 for the plain search, against 298 and 904 for the scheme. Both axes are logarithmic.1010³symbols in the alphabetinterval extensionsright to leftthe scheme18 characters · k = 25.04x apart
Fig. 6 The tree against the alphabet, with and without a schedule. Both rise; the gap between them widens, because what a schedule removes is what the alphabet supplies.

That is a more useful result than the headline factor, because it says where the technique earns its keep. On DNA — four symbols, which is where index-based approximate matching is most used — the factor is 2.9. On text it is 5.

What it costs, in the other currency

The tree falls by 2.74 and each of its nodes costs more, because a bidirectional extension is 16.5 ranks on four symbols against a backward step’s two. An interval that grows at both ends is the essay about that, and the reason it is stated here as well is that a ratio quoted without it is a ratio in a currency nobody pays.

In ranks the scheme is 4,916 against 13,806 — still a factor of 2.8, because both sides here are bidirectional extensions and the σ term cancels. The comparison that would not cancel is against a unidirectional walk over a single index, which spends two ranks an extension and would come out ahead in ranks while exploring nearly three times the tree.

That comparison is not drawn, and the reason is that it compares two structures rather than two schedules. The operation that closes the gap — asking a wavelet tree for every child at once — exists, is what real implementations use, and is not built here.

An extension is not one rankA backward search step is two ranks. A bidirectional step is two ranks and the width of the interval for every symbol that sorts before the one being added, because that total is how far the other interval's start moves — so an extension costs about 2σ ranks rather than 2. Measured on the same pattern against three alphabets: 10.3 ranks an extension at 3 symbols and 141.6 at 27, of which 18% are spent on the smaller symbols alone. A wavelet tree asked for all its children at once would answer them together; that is a different operation from the one this collection has been counting, and it is not built here.050100150510152025symbols in the alphabetranks an extension10.316.8141.6two symbolsfour symbolstwenty-six symbols15 characters · k = 214x apart
Fig. 7 What an extension costs, against the alphabet. Every node count in this strand is honest; every rank count is an upper bound on what an implementation with the compound operation would spend.

The other end of the same idea

It is worth noticing that the filter strand reached the same wall from the other side and stopped.

The filter that proposes everything measured a seed-and-extend filter collapsing as the seeds get short: at some budget the pieces are too short to be selective and the candidate count becomes the text. The response there was to make the seeds longer by allowing fewer of them, which needs a different partition.

A scheme is the same move made inside the index. Its first piece is a seed and its “verification” is the rest of the walk, and the reason it does better is that the walk is bounded by the index rather than by the text — a candidate that a filter would verify against m characters, a scheme rejects with one dead extension.

What is checked

Every schedule finds exactly what exhaustive comparison finds — positions and error counts, at two budgets, on all four schedules. That is the check the whole strand rests on, and it is against comparing the pattern at every position rather than against another search, because two searches sharing a framework can share a mistake.

Every scheme covers every distribution of the errors. That is a finite condition and it is enumerated rather than argued; a schedule nobody writes down is the essay about what happens when it does not hold.

And a scheme with one search removed must lose occurrences. It does — seven of eight found — and it is worth knowing that the loss is small enough to look like nothing. Every position it reports is real, at the right error count, and the ones it misses are the ones whose errors fell the way the missing search covered.

Every way the errors can fall, and which search takes itWith 2 errors over 3 pieces there are exactly 10 ways the errors can be distributed, counting the ones that use fewer than the whole budget. A scheme is valid when every row has at least one search against it, and that is a finite check rather than an argument — which is how the 3 searches here were chosen, by enumerating every order whose prefixes are contiguous against every pair of bound vectors and keeping a covering set. 0 of the 10 rows are covered twice, and that redundancy is what makes a defect in one search invisible in the answer: the other search hands the occurrences back.errors, by piecetaken by(0, 0, 0)1(0, 0, 1)1(0, 0, 2)1(0, 1, 0)1(0, 1, 1)1(0, 2, 0)3(1, 0, 0)2(1, 0, 1)2(1, 1, 0)3(2, 0, 0)30 of 10 covered more than once · the numbers are the searchesk = 2 · 3 pieces10 distributions
Fig. 8 The condition that makes a list of searches a scheme, drawn: every way the errors can fall, and which search takes it.

What this does not do

Two things, and both are worth stating because a factor of three invites over-reading.

It does not change what is found. Every schedule returns the same positions with the same error counts, and that is checked rather than assumed. A scheme is a way of not looking at branches that cannot hold an answer; it is not an approximation and it has no accuracy to trade.

And it does not touch the index’s size. The structure is two transforms whatever schedule runs over it, which is the finding the structure that was supposed to halve records. A reader who cannot afford the doubling cannot have any of this, and the plain walk over one index remains what they should build.

What it does change is the relationship between the budget and the cost. An unconstrained walk’s tree multiplies by about ten per error; a scheme’s multiplies by about six. That is still exponential, and at five errors both are hopeless — the honest reading is that a schedule buys one or two errors of headroom rather than a different growth.

The same occurrences, at four sizes of treeA pattern of 18 characters within 2 substitutions, over 3,000 characters of twenty-six symbols. All four schemes found the same 4 positions — that is checked against comparing the pattern at every position — and they walked 4,560, 4,384, 1,242 and 904 interval extensions to do it. The gap between the first and the last is 5.04x, and it comes from one thing: a search that begins at a piece it insists is exact spends nothing on the errors that piece cannot have.right to left4,5605.04xleft to right4,3844.85xone per piece1,2421.37xthe scheme9041.00xinterval extensionsall four found the same 4 positions18 characters · k = 25.04x apart
Fig. 9 The same four schedules on twenty-six symbols, where the gap is widest. The ordering of the four is stable across every alphabet and budget measured here.

What decides this, and what does not

The schedule is worth a factor of between two and five, and nothing in the pseudocode of an index walk names it. That is the shape of finding this collection keeps recording — the queue decides the class, and the pseudocode does not name it is the earliest instance — and it is why the next essay is about the schedule itself rather than about what it buys.

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.

AlphabetApproximate matchingBacktrackingBidirectional indexConstant factorError budgetFilterIntervalMeasurementPigeonholeSearch schemeTrade off