2,002 Escherichia coli genomes · PanGBank 11587 · Bacformer
Every E. coli genome in PanGBank’s pangenome of the species, walked gene by gene from its replication initiator, and a genomic language model, Bacformer, asked at every position what comes next. Read in its own vocabulary it names the next gene nine times in ten, as often as the pangenome graph does, more often than the graph where the genomes part, and it hesitates where the pangenome is plastic.
Rows: each protein of the path as the model reads it; columns: the start token, the proteins before the path when a genome’s own path brings them (summed), and the path itself. The model is causal: a protein sees only those before it. Much of the weight rests on the start token, where a head goes when nothing in the path concerns it (an attention sink); leave it out to see the rest, each row renormalised. Attention shows where the model looks, not why it calls what it calls.
What the model gets right and where it doubts, measured on the dnaA window: 80 genes from dnaA in all 2,002 genomes, 130,837 calls, each on a genome’s real proteins.
The spine starts at the origin. Columns 0 to 5, dnaA, dnaN, recF, gyrB, yidB and yidA, sit in essentially every genome, persistent and outside any region of plasticity. The model names dnaN, recF and gyrB in 99.7 to 100% of genomes, yidB in 93% and yidA in 99%.
The first fork is the dgo operon, galactonate catabolism, columns 6 to 11 after yidX. About 85% of genomes carry it, and panRGP puts it in a region of plasticity in nine genomes out of ten. The model is not lost there. Inside the operon it names each gene in 87 to 99% of genomes, with hardly any hesitation. A variable region can keep a predictable internal order.
Then a long persistent stretch, cbrA to yicS through the ilv and uhp operons, where the model names the next gene in 92% of genomes and seldom hesitates. Around columns 42 to 52 the graph opens: half the calls there fall in a region of plasticity, and the model’s hesitation peaks, up to 1.4 bits. This is where about fifty genomes carry the LEE pathogenicity island of enteropathogenic strains (ler, esc, ces, tir, eae), and a few hundred carry other type III secretion and sugar transport genes.
The window ends in the waa locus, which builds the lipopolysaccharide core: waaA in most genomes, then a spread of glycosyltransferases that differ between core types.
Each call is joined to the gene actually at that position, with its PPanGGOLiN partition and whether panRGP places it in a region of genomic plasticity.
| PPanGGOLiN partition of the gene | calls | model top-1 | via the exemplar bank | entropy |
|---|
| region of genomic plasticity | calls | model top-1 | via the exemplar bank | entropy |
|---|
Accuracy follows the partition. Read in its own vocabulary, the model names the next gene 95% of the time when it is persistent, 80% when shell, 29% when cloud, and 94% outside a region of plasticity against 78% inside.
So does its hesitation, by a wide margin. Entropy is 0.21 bits outside a region of plasticity and 0.83 inside, 0.16 bits on persistent genes and 3.3 on cloud genes. The effect holds under three checks:
This reverses the reading from one chromosome. On the 79 loci of O157:H7 Sakai alone, entropy was 0.44 bits inside regions of plasticity and 0.55 outside, which suggested the model’s confidence tracked nothing. Two thousand genomes show the opposite. It is not uniform either: the dgo operon sits in a region of plasticity in most genomes, yet the model is calm there and mostly right.
Against the graph. Predicting the next family from the previous one alone, the most frequent successor learned on the other half of the genomes, gives 90.0%; the model gives 88.8%. Where the graph is certain the model adds nothing. After a fork, where two or more families follow, the model is right 79.6% of the time against 72.0% for the graph: it reads upstream context that single adjacencies do not hold.
| columns | calls | mean entropy | top-1, own vocabulary | via the exemplar bank |
|---|
Every call is made on the real proteins of a real genome, so this measures the model, not a substitution. The anchor is what makes the difference. Along the replication cassette its first guess is right in every genome, out of fifty thousand families, and its distribution is nearly a point mass. It stays right nine times in ten along the persistent stretch, and drops where the genomes vary, down to three times in four in the waa locus.
The model’s families are its own. Bacformer predicts among 50,000 clusters of its training set. An exemplar bank built from 37 genomes covers 9,814 of them; where a family’s cluster is missing from it, the bank scores the model wrong even when it is right. At yidB the model predicts the same cluster in 1,969 of 1,976 genomes, one the bank does not hold: 0.2% by the bank, 92.6% read in the model’s own vocabulary. That reading is a decoder, P(family | cluster): the families actually present when the model predicts a cluster, learned on one half of the genomes and applied to the other. On an invariant column any consistent model looks right this way; the forks are where it is tested.
PanGramGraph reads the pangenome graph of a species with the grammar of gene order that a genomic language model learned: the graph shows what the genomes carry, the model what it expects next.
The genomes, gene families, partitions and regions of plasticity all come from PanGBank (pangenome 11587, GTDB_refseq v2.0.0, GTDB R232, 2,002 genomes), built with PPanGGOLiN, Gautreau et al. 2020, PLOS Computational Biology 16(3):e1007732, with regions of plasticity from panRGP, Bazin et al. 2020, Bioinformatics 36(Suppl_2):i651. PanGBank data are CC BY-SA 4.0, as is the GTDB taxonomy it adapts. The language model is Bacformer, Wiatrak et al., bioRxiv 2025.07.20.665723, on ESM-2 protein embeddings, Lin et al. 2023, Science 379:1123.
| what | script | output |
|---|---|---|
| the dnaA window of the 2,002 genomes, 80 genes each | pgb_window.py | pgb/window.json |
| their proteins embedded with ESM-2, and Bacformer’s call at every position | pgb_model.py | pgb/model_calls.npz |
| the graph of the dnaA window, the decoder, perplexity and ghosts | pgb_graph.py | pgb/graph_pgb.json |
| the 540 complete chromosomes, their proteins and the model’s call at every gene | pg_chrom.py, pg_embed.py, pg_calls.py | pgb/chrom*, pgb/calls_* |
| the pangenome’s own numbers, what each genome is searched by, and the map of the chromosome | pgb_page_meta.py | pgb/pangenome_info.json, pgb/genome_meta.json, pgb/chrom_map.json |
| any other window of the chromosome, when the page asks | pgb_region.py | served live |
| distant families that go together within lineages, and their sets | pg_epistasis.py --ctrl_content, pg_region_sets.py | pgb/region_sets.json |
| the same at close range, 50–500 genes apart in separate insertions, replicated in lineage-disjoint halves | pg_epistasis.py --halves lineage, pg_region_sets.py (close mode) | pgb/region_sets_close.json |
| what each link is in the genomes: one element at several spots, one tract, separate insertions, one insertion site, replicated in lineage-disjoint halves | pg_link_flags.py | pgb/link_flags.json, pgb/link_flags_close.json |
| the knockout engine: full-precision passes over whole chromosomes, the decoder of the other lineage half, whole-element controls, self-checks | pg_knock.py | pgb/knock/base_* |
| the in-silico knockout of every link, and the model’s verdict on each arc | pg_knockout.py --all | pgb/knock/all_arcs.json, close_arcs.json, pilot_report.md |
| the genomes carrying each element at its spot | pg_region_carriers.py | pgb/region_carriers.json |
| what the model does without one element, on demand | serve_live.py /influence (pg_knock.influence) | served live |
| the live model: calls in one genome, drawn paths, windows, attention | serve_live.py | 127.0.0.1:8765 |
| this page | build_page.py | standalone/pangramgraph.html |
Why every genome. The graph was first drawn from eleven complete chromosomes. The adjacencies the model expected and none of the eleven carried were then looked up in PanGBank’s 2,002 genomes: they were there 2.7 times as often as chance. What the model expects beyond a small sample exists in the species, which is why the page shows every genome.