The region score \(S\): matches minus \(C\) per mismatch, stopped by the give-up margin \(X\)
The search finds an exact k-mer that query and target share, 12 letters of a reduced alphabet (two letters, hydrophobic and polar, by default), and walks outward from it along the diagonal. The stretch it keeps is a region, and Figure 1 draws one.
From each end of the seed the search walks one residue at a time: \(+1\) at a match, \(-C\) at a mismatch, remembering the best score seen. When the score has fallen \(X\) below that best, the walk stops and the region is cut back to where the best was. Between unrelated sequences a mismatch costs more than a match earns, so the walk usually dies within a few residues of the seed. Across a shared domain it climbs until the domain ends. Walks on one diagonal that meet are merged into one region.
What the walk’s settings do, and what this region is worth
- \(X\), the give-up margin (
--extend-xdrop, default 8). The walk remembers the best score it has reached. As soon as the current score is \(X\) points below that best, the walk stops and the region ends where the best was. With \(C = 2\), four differing positions in a row cost 8 and end it; a mix of matches and mismatches can carry it further. A larger \(X\) lets the walk cross a short patch of mismatches inside a real shared domain, and also lets it wander further into unrelated sequence before it gives up. - Shared-domain stretch: a knob of this model, not of kmerseek. It is the number of residues on each side of the seed where the target carries the same domain as the query, so the classes match more often than chance; beyond it the two sequences are unrelated and match only by chance. In a real search this is however far the shared domain extends past the seed.
- \(\kappa\) inside that stretch, the copy rate. The model builds the target from the query one position at a time, flipping a weighted coin at each position. Heads, with probability \(\kappa\): the target gets the class the query has there, as a conserved position would. Tails, with probability \(1 - \kappa\): the query is ignored and the target's class is drawn from the target's own composition, as at a position that has drifted, or between unrelated sequences. So \(\kappa\) is the fraction of positions matching above chance in the chosen alphabet; the rest match only as often as chance allows. At \(\kappa = 0\) the stretch is no different from unrelated sequence. 0.46 is the value measured for hp_thomas_dill2 in aligned Pfam pairs at 20–30% identity (analysis notebook 230). Part 2 shows that this copy rate is Cohen's \(\kappa\) and measures it on a real pair.
Fixed in the walk: \(k = 12\) for the seed, \(C = 2\), and the two compositions, which section 2 lets you set. Draw another pair redraws the random sequences with the same settings.
The chance match rate \(\Pr(\text{match} \mid \text{unrelated})\) comes from the two compositions
\(f_{\text{query}}(c)\), \(f_{\text{target}}(c)\): the share of class \(c\) in the query, in the target; for two classes, \(h\) and \(p\).
Where the shares and the alphabet tables come from
The shares are counted from the two sequences of the pair: not from the database, and not from a uniform assumption. The sliders stand in for those counts. What the theory assumes is only that each position is independent of its neighbours; that is the part real sequences break, and \(r_{\text{database}}\) in section 5 measures by how much.
The tables are kmerseek's own, from src/rust/alphabets.rs, clusters in the order their classes are numbered; the hp alphabets write their classes as h and p (and c for cysteine in hp_lehninger_hpc3), every other alphabet writes a class as its first residue in lowercase. A F I L M V is hydrophobic and D E H K N Q R S T polar in every hp table; they disagree only about C, G, P, W and Y.
The expected score per position, and the boundary \(\Pr(\text{match} \mid \text{unrelated}) = C / (1 + C)\)
Equation (1) gives one position of an unrelated pair two possible scores: \(+1\) at a match, \(-C\) at a mismatch. A match happens with probability \(\Pr(\text{match} \mid \text{unrelated})\). The expected score per position, written \(\mathbb{E}[\text{score per position}]\), is the average of the two scores weighted by how often each happens: how far a long stretch of unrelated positions drifts, per position, on average. Equation (3) is that weighted average, then the same thing with the brackets multiplied out.
Karlin–Altschul needs this number to be negative. Then a long unrelated stretch drifts down, a stretch that climbs high is rare, and it can be given a probability. Setting Equation (3) to zero gives the boundary, \(\Pr(\text{match} \mid \text{unrelated}) = C / (1 + C)\), which is \(2/3\) at \(C = 2\). It is a limit on the chance match rate, not on the E-value.
Why a positive average score leaves no E-value
If the average were zero or positive, chance alone would keep the score climbing, a high score would be the normal outcome, and it would mean nothing. A pair of unrelated sequences that already match at more than two positions in three by luck cannot be assessed at all, whatever the score. Whether one region is significant is a different question, answered by \(E\) itself in section 4. Section 3 turns the same requirement into the value of \(\lambda_{\text{pair}}\): the expected score is the slope of the curve in Figure 3 at \(\lambda = 0\), and only a negative slope lets the curve come back to zero.
\(\lambda_{\text{pair}}\), nats per score point, converts the score to nats and vanishes at the boundary
Karlin and Altschul (1990) showed that between two random sequences the number of regions scoring at least \(S\) falls as \(e^{-\lambda S}\). \(\lambda\) converts points to nats, the natural-log unit of evidence (one nat is \(1/\ln 2 = 1.44\) bits). It is solved from this pair's own class shares, so the page calls it \(\lambda_{\text{pair}}\). Equation (4) defines it: the positive number that makes the left side equal 1.
\(\Pr(\text{match} \mid \text{unrelated})\) from Equation (2b); \(C\) from section 1; \(\lambda_{\text{pair}}\) the only unknown. At \(C = 2\) the root has a closed form, Equation (5), derived below.
Derive Equation (5), and why dividing by \(y - 1\) is safe
At \(C = 2\) the positive root has a closed form, Equation (5), written with \(u\) as shorthand for \(\Pr(\text{match} \mid \text{unrelated})\) in this equation and its derivation only:
Four steps, all at \(C = 2\) and with \(u = \Pr(\text{match} \mid \text{unrelated})\). First, put \(C = 2\) into Equation (4):
Second, write \(y = e^{\lambda_{\text{pair}}}\), so \(e^{-2\lambda_{\text{pair}}} = 1 / y^{2}\) and \(y > 0\). Multiply both sides by \(y^{2}\) and move everything to the left. The equation in \(\lambda_{\text{pair}}\) becomes a cubic in \(y\):
Third, \(y = 1\) (that is, \(\lambda_{\text{pair}} = 0\)) is always a root: \(u - 1 + (1 - u) = 0\). That is the root the page keeps saying to ignore. Dividing the cubic by \((y - 1)\) removes it and leaves a quadratic; multiply the right side out to check it gives the cubic back:
Nothing is divided by zero here. The line above is a statement that two expressions are equal for every \(y\), and checking it needs multiplication only. A product is zero when one of its factors is zero, so the cubic is zero when \(y = 1\) or when the quadratic is zero, and the root we want comes from the second case, where \(y\) is not 1 and \(y - 1\) is not 0. The quadratic at \(y = 1\) is \(3 \cdot u - 2\), which is 0 only at \(u = 2/3\): there the root we want has met the one we threw away, and \(\lambda_{\text{pair}} = 0\).
Fourth, solve the quadratic \(u \cdot y^{2} - (1 - u) \cdot y - (1 - u) = 0\) with the usual formula. Under the square root, \((1 - u)^{2} + 4 \cdot u \cdot (1 - u)\) has the common factor \((1 - u)\), and \((1 - u) + 4 \cdot u = 1 + 3 \cdot u\). Taking the \(+\) sign gives the larger root, which is the positive one because the other has a negative numerator:
\(\lambda_{\text{pair}} = \ln y\), which is Equation (5). Check against the boundary of section 2: at \(u = 2/3\) the numerator is \(1/3 + \sqrt{(1/3) \cdot 3} = 4/3\) and the denominator is \(4/3\), so \(y = 1\) and \(\lambda_{\text{pair}} = 0\), which is where the expected score per position reaches zero. Below \(2/3\) the fraction is above 1 and \(\lambda_{\text{pair}}\) is positive. A number to hold on to: at \(u = 1/2\), \(y = 1/2 + \sqrt{5/4} = 1.618\) and \(\lambda_{\text{pair}} = 0.481\) nats per score point; putting \(y = 1.618\) back into the first line gives \(0.809 + 0.191 = 1\). For any other \(C\) the same substitution gives a polynomial of degree \(C + 1\) in \(y\) (when \(C\) is a whole number), which is why the code bisects instead.
Solving \(\lambda\) per pair is in the spirit of BLAST's composition-based statistics (Schäffer et al. 2001), which adjust per query. kmerseek's code and its CSV columns write \(a\) for what this page writes \(\Pr(\text{match} \mid \text{unrelated})\).
\(\lambda_{\text{pair}}\) for every alphabet: fewer classes, more chance matches, fewer nats per point
Hover a dot or a name to read it off; click one to make it the alphabet of the whole page (the drop-down in section 2 follows).
The E-value \(E\) multiplies the search space \(K \cdot m \cdot n\) by the chance of a score of at least \(S\)
- \(E\): the expected number of regions scoring at least \(S\) between an unrelated query of length \(m\) and a database of \(n\) residues. Smaller is better.
- \(m\): query length in residues. \(n\): residues in the database.
- \(K\): one number per database, the fraction of the \(m \cdot n\) grid squares that can start an independent region; measured when the index is built, section 5.
- \(r_{\text{database}}\): one number per database, the share of a region's score that counts as evidence; 0.865 on Swiss-Prot, so 40 points are judged as 35. Section 5.
- bits: the same evidence on BLAST's bit scale; larger is better.
What \(r_{\text{database}}\) takes off, what stands in for \(n\), and the four grey pairs
\(r_{\text{database}}\): the database factor, the share of a region's score that counts as evidence. The E-value judges a region with score \(S\) as if it had scored \(r_{\text{database}} \cdot S\) between sequences whose positions were independent. On Swiss-Prot, \(r_{\text{database}} = 0.865\): a region scoring 40 is judged like one scoring 35. The 5 points taken off are matches that real proteins hand out for free: a helix, a strand or a membrane stretch keeps one class for several residues in a row, in both sequences, so neighbouring positions match together without the two proteins being related. A plain number with no unit, one per database, measured on that database's own sequences when its index is built (section 5): 1.00 would mean positions are independent; SCOPe40 domains give 0.947, full-length proteins 0.83–0.87. In the code it is ka_r_database. Figure 10 draws the discount and Figure 11 the histogram behind it.
\(n\) is the index's k-mer count, which stands in for the residues in the database. --ka-k overrides \(K\).
In an hp alphabet a chance match rate near 0.40 is a polar protein against a membrane protein. They almost never match by luck, so the same \(S\) is rare between them. 0.64 is two membrane proteins, which match on most positions for free. That line is nearly flat because 0.64 sits just under the boundary \(2/3\), where \(\lambda_{\text{pair}}\) reaches 0.
\(K\) and \(r_{\text{database}}\) are measured when the index is built
The seven steps in full, and why \(K\) is measured, not computed
- Sample 200 sequences from the database.
- Search them as they are, and search the same sequences shuffled so that every neighbouring pair of residues stays as common as it was (the reference).
- Bin every region from both searches by \(x = \lambda_{\text{pair}}\,S\) in half-nat bins and count.
- Per bin, take the log ratio of the two counts, \(r(x)\).
- Fit a straight line through the eight lowest bins that both searches fill with at least 30 regions. That is the baseline; \(\sigma\) is the scatter of those bins around it. A constant ratio is a difference in \(K\); a gentle tilt is sequence structure the reference lacks.
- Walk up in \(x\). The first bin whose ratio sits more than \(2\sigma\) above the line is where the real queries have something the same sequences, shuffled, cannot have: relatives. The fit stops there.
- Fit \(\ln c_{\text{real}}(x)\) on the eight highest bins that made it in: \(r_{\text{database}}\) is minus the slope, \(K\) comes from the intercept.
In \(E = K \cdot m \cdot n \cdot e^{-\lambda_{\text{pair}} \cdot r_{\text{database}} \cdot S}\) the product \(m \cdot n\) is the number of squares in the grid of query positions against target positions, and the formula treats every square as a fresh chance at a region. That overcounts: squares next to each other on one diagonal belong to the same excursion, and a region is counted once, at its peak. \(K\) is the fraction of those \(m \cdot n\) squares that are real, independent starting points, so it is well under 1. For plain ungapped scoring Karlin and Altschul (1990) give a formula for \(K\) from the score distribution. kmerseek cannot use it, because its regions are not plain excursions. A region exists only if it contains an exact 12-mer seed, the give-up margin \(X\) ends it, and walks on one diagonal are merged. Each of those removes chance regions, mostly low-scoring ones, and the formula knows nothing about them. So \(K\) is measured instead, the way BLAST measures its gapped constants by simulation: it is what is left of the intercept of Equation (7a) once the sizes and the bin width are divided out, Equation (7c):
The seed requirement is why kmerseek's values (0.0227 on SCOPe40, 0.0180 on Swiss-Prot, 0.0139 on UniRef50) are several times smaller than BLAST's ungapped \(K\): most squares of the grid can never start a region because no 12-mer matches there. \(K\) is fitted once per database, pooled over every pair, so it is a region-weighted average; the theory says \(K\) varies with composition too, but far less than \(\lambda\) does, which is why the composition goes into \(\lambda_{\text{pair}}\) and not into \(K\).
A simulation of the calibration you can push on
What the simulation assumes, and why the fitted slope is below the true value
Two things pull the fitted slope under the true average. Relatives enter gradually, so the bins just below the stop already carry a few. And a mixture of queries with different \(r_{\text{database}}\) is shallower than their average at high \(x\), because the shallow ones dominate there. The equation at the top of the page uses the constants measured for the chosen database and does not move with these sliders.
The two figures below are not kmerseek's calibration output. They come from a small simulation inside this page that makes up region counts with the shapes described above, so you can change things the real run does not let you change and watch what each one does to the fitted slope. In the simulation each calibration query departs from independent positions by its own amount, drawn around the r_database you set with the spread you set, and has its own share of relatives. The pooled counts are the sum over queries, so the r_database the fit recovers is a region-weighted average over them. Relatives add a shallow tail that begins around 9 nats, the reference is the chance curve without relatives, and Poisson noise is added to every count. The switches change what the reference keeps, how many queries you spend, how much they differ, and how the stop rule is set. The readouts compare the fitted slope with the true average the simulation was given.
What moves \(r_{\text{database}}\), and what does not
How \(\rho\) enters the model, and why the coral dot sits under the curve
\(r_{\text{database}}\) is fitted on chance regions only: the bins below the stop rule, where the real queries and the same queries shuffled agree. Relatives never enter those bins, so \(\kappa\), which describes relatives, cannot tilt them; it can only decide where the fit stops. What tilts them is run structure inside the sequences. Real proteins have helices, strands and membrane stretches where one class repeats for several residues in a row. The Karlin–Altschul model assumes each position is drawn on its own.
Figure 9 puts one knob on that. \(\rho\) (rho) is the neighbour repeat rate: at each position, with probability \(\rho\) the sequence keeps the class of the position before, and otherwise draws a class from its composition. Both sequences of a pair get it, so the chance that two neighbours carry the same class is \(\rho + ((1 - \rho) \cdot \Pr(\text{match} \mid \text{unrelated}))\), the same shape the coin of section 1 gives a match inside a shared domain, with \(\rho\) in place of \(\kappa\). At \(\rho = 0\) the positions are independent and the fitted slope is close to 1, the Karlin–Altschul value. When neighbours repeat, a lucky match tends to be followed by another lucky match, chance regions run longer, the tail of the counts falls more slowly, and \(r_{\text{database}}\) drops below 1. The shuffled reference keeps the neighbour pairs, so it keeps \(\rho\) and the stop rule still fires only on relatives.
Drag \(\rho\) and the whole tail tilts; the purple curve on the right is that tilt. Drag \(\kappa\) and the chance bins do not move, but the pink line does. Relatives pile up at the score their shared domain can reach: at \(\kappa = 0.46\) that is just above the chance regions, so the stop rule fires early and the fit is cut off where the tail is still bending, which is why the coral dot sits under the curve. At \(\kappa = 0\) there is nothing to stop on, and at \(\kappa = 0.9\) the relatives climb past every chance region, so in both cases the fit runs to the end of the counts. This is the same effect the readout of Figure 7 calls relatives entering gradually. The alphabet and \(C\) from section 2 and \(X\) from section 1 also enter, because they set which chance regions exist; the sliders of section 2 do not, because this model draws both compositions at the Swiss-Prot average.
What \(r_{\text{database}}\) means
The histogram behind \(r_{\text{database}}\)
Three references, and what each one keeps
The reference only has to lack relatives and keep everything that makes chance regions. The closer it is to the real sequences, the flatter the baseline and the later the stop rule fires. On SCOPe40 domains all three references, fitted on their own shuffled runs, give the same line (slope 1.04, K 0.064–0.070): runs and periodicity add nothing there. The numbers the search uses (0.947 and 0.0227) come from the runs on real sequences in section 5, cut off where the stop rule fires. On full-length proteins they differ.
| reference | keeps | loses | what happens |
|---|---|---|---|
| plain shuffle | composition | runs, helix and strand periodicity | it is the independent-positions model itself, so its slope is the closed form (1.0 in x). Real membrane pairs have runs it lacks, so on the raw score the stop rule fired at score 23 and the fit read the seed end. Swiss-Prot: slope 1.015. |
| shuffle keeping dipeptides | composition, the number of runs and their average length | the longest runs, periodicity | the current reference. In a two-letter alphabet, keeping neighbouring pairs fixes how often h follows h and p follows p. Past raw score 49 it carries 5–20× more regions than the plain shuffle: that is run structure. Swiss-Prot: slope 0.942. |
| reversed | composition, runs, periodicity | almost nothing | closest to the real curve (Swiss-Prot: 0.858 against 0.865), but a helix or a strand reads the same backwards, so reversed relatives still share single elements with their forward family: the small tail at high x in the reversed column. The reference keeps some relatives. |
One region, start to finish: from class shares to an E-value
What two letters can carry, the copy rate κ, bits per position, the alphabet trade, short motifs and the best C, is Part 2, its own page.
Glossary
Alphabetical: Latin letters first (upper and lower case together), then Greek by name, then words and options.
- \(a\): what kmerseek's code and CSV columns call \(\Pr(\text{match} \mid \text{unrelated})\).
- \(b\): in Figure 6, the intercept of the fitted line, \(\ln(K\,L_{\text{queries}}\,N_{\text{database}}\,w_{\text{bin}})\); Equation (7c) turns it into \(K\).
- bits: the same evidence as \(E\) on BLAST's bit scale, \(((\lambda_{\text{pair}} \cdot r_{\text{database}} \cdot S) - \ln K)/\ln 2\); larger is better.
- \(C\): mismatch penalty,
--extend-mismatch-penalty. Default 0 (extension off); every number on this page uses 2. Part 2 asks which \(C\) is best. - \(c(x)\): regions per half-nat bin at \(x\); \(c_{\text{real}}\) from the real queries, \(c_{\text{reference}}\) from the same queries shuffled.
- \(E\): expected number of chance regions scoring at least \(S\) between an unrelated query of length \(m\) and a database of \(n\) residues, Equation (6a). Smaller is better.
- \(f_{\text{query}}(c)\), \(f_{\text{target}}(c)\): the share of class \(c\) in the query, in the target, counted from that sequence; for two classes these are \(h\) and \(p\).
- \(h_{\text{query}}, h_{\text{target}}\): hydrophobic share of the query, of the target, under the chosen alphabet. \(h\) is a class letter and nothing else.
- \(K\): the per-database constant in front of \(E\), the fraction of the \(m \cdot n\) grid squares that can start an independent region; from the intercept of the calibration fit, Equation (7c).
--ka-koverrides it. - \(k\): seed length; a region is found only if it contains an exact shared k-mer. 12 in the calibration.
- \(L_{\text{queries}}\): residues in the calibration queries.
- \(m\): query length in residues.
- \(N_{\text{database}}\): residues in the index, as used in the calibration fit.
- \(n\): database residues in \(E\) (the index's k-mer count stands in).
- \(p_{\text{query}} = 1 - h_{\text{query}}\), \(p_{\text{target}} = 1 - h_{\text{target}}\): polar shares. \(p\) is a class letter and nothing else.
- \(\Pr(\text{match} \mid \text{shared domain})\), the shared-domain match rate: fraction of aligned positions, between two sequences that carry the same domain, where the classes match; Part 2, Equation (1a).
- \(\Pr(\text{match} \mid \text{unrelated})\), the chance match rate: how often one query position and one target position, drawn independently, carry the same class; Equation (2b).
- \(r(x)\): the log ratio \(\ln c_{\text{real}}(x) - \ln c_{\text{reference}}(x)\), Equation (7b); what the stop rule reads.
- \(r_{\text{database}}\): the database factor, one per database, no unit: the share of a region's score that counts as evidence, so \(E\) judges a score \(S\) as if it were \(r_{\text{database}} \cdot S\) between independent positions (0.865 on Swiss-Prot: 40 points are judged as 35). The rest are matches that runs of one class in real proteins hand out for free. Measured as minus the slope of \(\ln c(x)\) in the calibration; multiplies \(\lambda_{\text{pair}}\) in the exponent of \(E\).
ka_r_databasein the code. - \(S\): region score, matches minus \(C\) times mismatches, Equation (1).
- \(u\): shorthand for \(\Pr(\text{match} \mid \text{unrelated})\) inside Equation (5) only.
- \(w_{\text{bin}}\): the bin-width factor in Equations (7a) and (7c), \(1 - e^{-r_{\text{database}}\,w}\) with \(w = 0.5\) nat.
- \(X\): the give-up margin (BLAST's X-drop), default 8; the walk out of a seed stops when its score has fallen \(X\) below its best (Figure 1).
- \(x = \lambda_{\text{pair}}\,S\): the score in nats; the axis the calibration works on.
- \(\kappa\) (kappa), the copy rate, Cohen's \(\kappa\): the match rate above chance as a fraction of the room above chance; measured in Part 2.
- \(\lambda_{\text{pair}}\) (lambda): nats per unit of score for this pair, the positive solution of Equation (4); 0 when \(\Pr(\text{match} \mid \text{unrelated}) \ge C/(1+C)\).
- \(\sigma\) (sigma): scatter of the eight baseline bins around their line; the stop rule fires at \(2\sigma\).
- 30: the minimum count for a bin to be used in the calibration.
- alphabet: the classes the 20 amino acids are merged into before anything is scored (
--alphabet; the partitions are insrc/rust/alphabets.rs); chosen in section 2. - nat: the natural-log unit of evidence; one nat is \(1/\ln 2 = 1.44\) bits.
--ka-null: which null the calibration reports:database(default; real sequences, cut off where the stop rule fires),shuffled,shuffled-dipeptide,reversed.--ka-reference: the reference the stop rule compares the real counts against, defaultshuffled-dipeptide.