Recurring Archetypes, the Daily Decomposition, and Two Ways to Compute Them
2026-10-03
News is mostly recurrence. A war, a scandal in the capital, another bad turn in the Middle East, a central bank surprising the market: the shape repeats while the names, places and pretexts change. Eigen Times is a newspaper that takes this literally. It treats the archive of news as a matrix, extracts its principal directions—the eigen-news, a few dozen recurring archetypes that explain most of the variance—and prints each day as a decomposition: how much of today projects onto each archetype, what is left over, and, for every story, what is the same as always and what is different this time.
This article describes how Eigen Times was built as a complete system: a 1.09-million-article corpus assembled from public archives (the Guardian, 1999→; Wikipedia’s Current events portal, 2002→; Wikinews, 2004–2026); two independent constructions of the eigenbasis, one in term space (the singular value decomposition of a centred term-frequency–inverse-document-frequency (TF-IDF) matrix) and one in embedding space (the eigenvectors of the covariance of sentence embeddings), both rotated into named axes; a story layer that clusters each day’s articles and threads them into episodes; the statistics that turn coordinates into journalism—Hotelling’s for variance-adjusted retained distance, residual energy for discarded squared length, a novelty ratio, per-archetype profiles, precedents; and the newspaper itself, a static site of 10,102 daily issues. We compare the two bases against the human-assigned event categories of the Wikipedia portal, which are never used in fitting, and find that both recover them at similar rates (42.4% and 40.0% section purity) while their matched axes correlate at 0.57–0.75. The entire backend is Rust; the pipeline runs on a laptop.
A second edition, added after the first was published, extends the archive to 16.5 million articles across 175 years by ingesting the New York Times archive (September 1851→) and refits the eigenbasis with day-balanced weights, so that every calendar day since 1851 counts the same. The two editions are served side by side with a switch on every page. On the same human-labelled events the 175-year basis recovers categories as well as the modern one (section purity 0.435 against 0.431) while spreading today’s stories across all sixty axes instead of piling most of them onto one. Fitting it forced the computation to stream: the orientation of axes, the term loadings and the story clustering were rewritten to run in flat memory over a corpus fifteen times larger than the first, and the paper reports how.
Newspapers are organised by desk—World, Politics, Business—because that is how newsrooms are organised. Readers, however, experience the news as a small repertoire of recurring situations, and much of what a seasoned reader does when reading a headline is to recognise which situation it is and then look for what departs from the template. “Another regional war” is the recognition; “but this time two great powers are on opposite sides, and it is six weeks before an election in one of them” is the reading.
Eigen Times is an attempt to make that reading mechanical, honest and daily. Its thesis is that the space of news articles has low effective dimension: a few dozen directions carry most of the variance of a quarter-century of reporting, and those directions are the archetypes. Once the archetypes are known, a day’s news can be measured rather than merely categorised. Each story becomes a vector; its projection onto the basis says which archetype it instantiates and at what intensity; the part of the vector the basis cannot reproduce measures variation outside those learned directions; and the archetype’s own history—its typical profile, its previous episodes—gives the reader the precedents against which “different this time” can be stated as a number before it is stated as a sentence.
The contributions of this article are:
The newspaper is running at eigentimes.com; the source is at github.com/alexy/eigentimes. The refinement loop is an implemented research direction whose scheduling remains separate, as described below.
Paper version 0.2.1. This correction defines symbols and operators before use, separates raw and named coordinates and their scales, and makes the scope of matrix identities and refinement bounds explicit. It preserves the reported datasets, fitted models, numerical results, thresholds, citations, and figures. The numerical results and deployment account describe the editions reported in August and September 2026; this patch is not a new corpus evaluation or operations report. The mathematical companion supplies longer prerequisite explanations.
Automated journalism has mostly meant automated writing: templated prose generated from structured data. The Associated Press began publishing computer-written earnings reports with Automated Insights in 2014; the Washington Post’s Heliograf covered the 2016 Olympics and elections; Reuters’ Lynx Insight (2018) and Bloomberg’s Cyborg turned market data into draft stories. Carlson [4] framed the phenomenon as “the robotic reporter” and examined how journalists narrated its arrival; Graefe’s Guide to Automated Journalism [10] surveyed the practice and its limits, chiefly that generation from structured data covers only the small fraction of news that arrives structured. Eigen Times sits on the other side of the newsroom: it does not write stories, it reads them, and what it automates is the editorial act of recognition and comparison.
The closest research lineage is Topic Detection and Tracking. The TDT pilot study [1] defined the tasks—segmenting a stream, detecting first stories, tracking known events—that any system reading a news stream must solve, and first-story detection in particular [22] is the ancestor of what this article calls the residual: the part of today that no existing direction explains. Industrial news aggregation, notably the clustering behind Google News [6], solved the daily grouping problem at scale. Eigen Times reuses the TDT decomposition of the stream into stories (a day’s cluster) and episodes (a chain of stories across days), but its unit of recognition is not the event; it is the archetype, a direction that recurs across many events.
Latent Semantic Analysis [8] was the first demonstration that the singular vectors of a term–document matrix are meaningful directions, and it remains the most interpretable: a component is literally a weighted word list. Probabilistic successors—probabilistic Latent Semantic Analysis (pLSA) [13] and Latent Dirichlet Allocation (LDA) [2]—recast the directions as topics; dynamic topic models [3] let topics drift through time, which is the model-side counterpart of the refinement problem in Refinement and identity. Topic models have been applied to news at scale [21], and meme-tracking [19] followed short phrases through the news cycle. Leetaru’s GDELT [18] took the opposite approach and coded the world’s news into a fixed event ontology; its themes and actors are a hand-built basis where Eigen Times wants a learned one.
Sentence embeddings changed what a document vector is. Word vectors [20] made semantic similarity linear; Sentence-BERT [23] produced document vectors in which cosine similarity tracks meaning rather than vocabulary; the BGE family [24] used here is a compact descendant. BERTopic [11] clusters such embeddings and names the clusters with class-based TF-IDF, which is close in spirit to how Eigen Times names its embedding axes (v1: the embedding eigenbasis), except that Eigen Times keeps the axes orthogonal and continuous rather than discrete.
The numerical ingredients are classical. Varimax rotation [16] makes a factor basis readable without changing the subspace. Hotelling’s [14] and the squared prediction error [15] are the standard pair for monitoring a process against a principal-component model—here, monitoring the news against its own history. Randomized singular value decomposition (SVD) [12] makes the term-space decomposition of a million-row sparse matrix a matter of minutes. The Davis–Kahan theorem [7] bounds how far separated eigendirections can move when the covariance is perturbed; its stability conclusion requires a suitable eigenvalue separation. Archetypal analysis [5] is the non-negative alternative to rotation for producing unipolar axes, and the Kuhn–Munkres assignment [17] matches axes across refinements and across bases.
Wikipedia’s Portal:Current events is a curated daily bulletin, one page per day since January 2002, with each event filed under a section such as Armed conflicts and attacks or Politics and elections. It has been used as a source for multi-document summarisation [9]. Eigen Times uses it twice: as a compact, consistent source of events, and—because the sections are human category labels that were never shown to the fitting procedure—as the held-out check that the eigenvectors recover categories people would choose.
We use real-valued column vectors for individual observations; a data matrix stores their transposes as rows. The notation specifies real coordinates, a superscript denotes transpose, is Euclidean length, and is an all-ones column of the required row count. A matrix’s shape lists rows then columns. The operator forms a diagonal matrix, denotes the identity of the indicated size, and a negative matrix power denotes an inverse on positive retained eigenvalues. Summation indices run over the specified observations or coordinates; bars around a scalar mean absolute value. The mathematical companion develops these prerequisites pictorially.
The newspaper’s unit of presentation is a story: articles from one day grouped as reporting one event. Let index a story, its publication day, and its embedding-space vector, with in the deployed embedding system. An embedding is a numerical representation produced by a fixed text model. A story also has a sparse term vector , where counts vocabulary terms. Term-frequency–inverse-document-frequency (TF-IDF) weights within-document counts by how rare terms are across documents. Story energy, denoted , is the number of distinct sources times , normalised so its mean over a day’s stories is one; is the natural logarithm.
Story energy is a presentation weight, distinct from the weights used to fit the basis. In the deployed fits the observations are articles: let index an article, its vector, its fitting weight, and the number of fitted articles up to a specified cutoff day. Let have rows , and let . The first edition uses uniform article weights; the second uses the day-balanced weights defined below. The same covariance formula could also be applied to weighted story rows, but that is not the deployed fitting population.
A covariance matrix averages products of centred coordinates. Let denote total fitting weight, the weighted mean, and the weighted covariance:
An eigenvector is a direction that multiplication by scales without turning; its eigenvalue is that scale, equal here to variance along the unit direction. Let be the orthonormal eigenvector matrix and its diagonal eigenvalue matrix, ordered by decreasing eigenvalue. The full decomposition is . Retain positive directions in , with . Column and value refer to retained raw axis , for . Their weighted combinations form the news subspace. The matrix is the retained covariance approximation, not generally the full covariance.
The singular value decomposition (SVD) expresses a matrix using orthonormal left and right directions and nonnegative singular values. Let , the smaller dimension. Define the weighted centred matrix , where has square-root weights on its diagonal. In its thin SVD, has shape , is a diagonal matrix of descending singular values, and has shape : . The first columns of give ; let contain the corresponding columns of and the leading diagonal block of . Then . The ordinary coordinate matrix is , with shape . Thus alone is not the matrix of unweighted article coordinates. The loading columns describe directions; coordinates rank their historical instances.
For component selection, let denote an estimated isotropic noise variance and the Marchenko–Pastur upper noise edge. The reference expression is . It assumes an idealised independent, equal-weight noise model; weighted and dependent news observations need additional justification before treating it as a calibrated cutoff. An explained-variance curve reports the sum of retained eigenvalues divided by the sum of all eigenvalues. The deployed bases use .
An eigenvector’s sign is arbitrary, and nearly equal eigenvalues allow large changes of individual directions within almost the same subspace. Two conventions make axes printable. Sign: each is oriented so that its coordinate distribution has nonnegative skewness, the third centred moment divided by the cube of its standard deviation. A heavy positive tail then identifies the named pole when skewness is nonzero. Named axes: let be an orthogonal rotation, satisfying , and the named directions. Varimax chooses to concentrate lexical loadings on individual axes. Projection residuals and full quadratic statistics are computed in the raw basis; displayed coordinates, names, and canons use the named basis.
For embedding-axis naming, let be the aligned article TF-IDF matrix and its unweighted mean term column. Let be the matrix of raw coordinates divided by their fitted standard deviations. The inverse square-root power acts on the diagonal eigenvalues. Let be column of , and its lexical loading column. The centred cross-product ranks associated words; stacking these columns gives , whose rotated loadings are . This can print a component as troops · offensive · ceasefire · shelling. The term-space LSA fit instead obtains its lexical loadings directly from the right singular vectors of the term matrix; both constructions then apply varimax. Naming the embedding axes with standardised raw scores while rotating unstandardised article coordinates is a naming heuristic, not an exact attribution of every final coordinate to words.
Let be the issue day and an individual observation vector measured in the selected reference basis. For the term-space model the same algebra uses a TF-IDF column and replaces embedding dimension by vocabulary dimension . In the proposed chronological refitting protocol that reference is fitted through day ; production currently retains fixed fitted versions. Let be raw coordinates, their reconstruction, and the residual:
Hotelling’s denotes the variance-adjusted retained squared distance; denotes discarded squared length. Let be the fraction of centred squared length discarded, defined by the ratio when ; the implementation returns zero when centred energy is zero:
These equations measure one vector; the article pipeline applies them to individual articles. Story cards store the mean of their member articles’ values, which need not equal the nonlinear statistics evaluated at the story’s mean vector. A high represents an unusual intensity or mixture within familiar directions; a high represents variation outside the retained subspace, not proof of a new event. For the named basis define , with entries , and let be column of . Its fitted covariance is ; its diagonal variances are , where indexes raw axes. They cannot generally be replaced by the raw . Dividing named coordinates by their marginal standard deviations does not remove off-diagonal covariance and is not full whitening. Full whitening would rescale and decorrelate all coordinates. The raw above remains distinct from a sum of squared marginally standardised named coordinates.
For printed dominance, apply named coordinates to the story’s mean projected vector. Let denote named axis ’s empirical sample standard deviation over the ten years ending at the latest loaded story date, floored at by the implementation. The operator returns the index attaining the largest value. Choose the greatest signed standardised coordinate, without absolute values or mean subtraction; all candidates may be negative. The exact decomposition is
The three terms are the named archetype, its mixture with other retained directions, and the residual. The scale is a story-window statistic, distinct from both the raw eigenvalue and the fitted rotated-axis variance.
Let be the energy-weighted mean of stories published on day , and its named-axis spectrum: and . The entry is axis ’s daily coordinate. A -score subtracts a reference mean and divides by its positive reference standard deviation. Applying it to daily coordinates using the site’s era statistics marks axes as loud, usual, or silent. In the audited renderer those era moments use the loaded edition’s daily spectra; they are not a separate preceding-days-only fit for each historical issue.
For each archetype , its dominant stories define a profile: the mean and standard deviation of every named coordinate over the chosen reference stories. Here in a displayed profile deviation denotes the relevant coordinate’s profile standard deviation, not a raw eigenvalue or an SVD singular value. “Same as always” is the set of strong profile axes a story also carries; “different this time” is a departure by more than . Cosine similarity is the dot product of two nonzero coordinate vectors divided by their lengths’ product. Precedents are the nearest earlier stories with the same dominant axis, one per episode, by cosine in named-axis coordinates.
| Source | Articles | Span | Text | Access |
|---|---|---|---|---|
| The Guardian (Open Platform) | 954,274 | 1999–2026 | full body | public test key, 720 req/min |
| Wikipedia Current events | 117,720 | 2002–2026 | one-line bulletins with sections | MediaWiki API, 31 days per call |
| Wikinews (English) | 21,172 | 2004–2026 | full text | 47 MB dump; the site closed in 2026, so the archive is complete |
| The New York Times (Archive API) | 15,407,541 | 1851–2026 | headline and abstract (index abstracts before the 1980s, ledes after) | developer key, one month per call, 2,100 months |
The first three sources were the fitted corpus of the first edition (1.09 million articles). The New York Times archive—headlines and abstracts for every item the paper indexed since 18 September 1851—was added for the second edition (§Two editions): 2,100 monthly calls, embedded as they arrived at about 550 articles per second on a laptop, for a corpus of 16,500,781 articles over 63,270 days. Its text changes register along the way: until the 1980s the “abstract” is the index’s terse summary (“Commerce Dept repts mfrs sales at record; illus”), with placeholder headlines for untitled items (“Article 8 – No Title”); afterwards it is the lede paragraph, for a decade prefixed with a literal “LEAD:”. Those conventions are cleaned before the text is tokenised for naming (§Two editions), and only for that source. Every source is read by an adapter split into a pure parser, unit-tested on fixtures, and a rate-limited, retrying fetcher; the whole backfill took one afternoon.
Two properties matter more than volume. Length: archetypes recur on scales of months to decades, so the basis needs decades. Consistency: a change in source mix shows up as a fake change in the world, so backfill and live stream must be embedded with the same pinned model and clustered by the same procedure.
Raw articles are append-only Parquet in a Hive layout, one file per
(source, month), written atomically so that a finished
partition is the resume marker for a backfill. Sail—a Spark-compatible
engine in Rust—is the query engine over the layout (et sql
registers raw/articles as a view with the partition
columns), while the numeric pipeline reads the same files directly with
arrow-rs. Every derived table (embeddings, coordinates, statistics,
stories) is keyed by the model or basis version that produced it, so any
day can be re-materialised against any basis.
Single-linkage clustering joins articles connected by any chain of above-threshold pairwise similarities. Per day, articles from all sources are clustered by single-linkage on cosine similarity (0.72 in embedding space; 0.6 in the diffuser LSA space). A story’s representative is the member with the most text from a full-text source, so headlines come from newspapers rather than bulletin lines; a story’s section is the human section of a bulletin member, if any—newspaper desk names are deliberately not used, since only the bulletin sections are event categories. A centroid is the mean of a cluster’s member vectors. A story on day continues yesterday’s episode when its centroid agrees with a story of day at cosine ; episodes are chains, so a 600-day war is one episode, and “episode day 7” on a card means the seventh day of that thread. The deployed story layer has 842,278 stories over 10,102 days; 24 February 2022 yields a 62-article, three-source lead story, “Russia launches attack on Ukraine”, on episode day 7—the thread begins with the build-up.
Both constructions produce the same objects—an orthogonal basis, a rotation to named axes, per-article coordinates, term loadings—and everything downstream (stories, spectra, profiles, precedents, the site) is computed identically for both. They differ in what a document is.
Each article is tokenised (lower-cased, common stop words excluded, and Snowball-stemmed to remove word endings), counted into a 50,000-term vocabulary (document frequency between 20 and 30% of the corpus), and encoded as a TF-IDF row. For a present term, let be its within-article count and its inverse-document-frequency weight. Its unnormalised value is ; absent terms have zero weight. The row is then -normalised, meaning divided by its Euclidean length. The corpus term matrix has 1,093,166 rows and 149.4 million non-zero entries.
Let be the random test matrix in randomized SVD, with trial columns. The algorithm approximates the leading singular directions through such products and subspace iteration, repeated multiplication that sharpens the leading span. In the uniform first edition, column means are subtracted implicitly: . This avoids storing a dense centred term matrix. The top 60 singular triplets—paired left and right directions with a singular value—are computed with 20 oversampling columns and two rounds of subspace iteration, deterministic for a seed. The whole fit—two tokenisation passes over 1.6 GB of text, the SVD, the varimax rotation of the loading matrix—runs in about five minutes on a ten-core laptop.
The rotated axes are word lists by construction, and they are strikingly clean. Among the sixty: bank · financial · credit · rate · loan · Barclays; party · Labour · Tory · conservative · leader · election; police · arrest · officer · protest · investigate · suspect · murder; price · rise · market · oil · inflation · rate · FTSE; Trump · Donald · president · Biden · Republican · immigrant; climate · emissions · carbon · global · warming · temperature; Israel · Palestinian · Gaza · Hamas · Netanyahu; flight · airline · passenger · airport · plane · crash; European · Europe · France · Germany · Brussels · euro; court · judge · rule · case · trial · legal; Russian · Russia · Ukraine · Putin · Moscow · Kremlin; covid · case · vaccine · pandemic · lockdown; house · property · home · mortgage · buyer; protest · demonstrator · activist · arrest; Korea · north · south · nuclear · Kim · Pyongyang; Ireland · northern · Irish · IRA · Sinn · Belfast; Brexit · deal · Johnson · Boris · negotiate · referendum; asylum · immigrant · refugee · seeker · migrant · border; prison · sentence · charge · jail · trial · convict; energy · gas · electricity · nuclear · carbon · renewable; attack · bomb · terrorist · terror · security · Islam; Syrian · Syria · Assad · rebel · Turkey · Damascus. The Gaza axis, the Ukraine axis and the Brexit axis are each one archetype with many episodes.
Each article’s headline and first 300 characters are embedded with a
pinned local model (bge-small-en-v1.5, 96 tokens (model
text units), classification-token (CLS) pooling (using the model’s
designated summary-token vector), 384 dimensions, run through the Open
Neural Network Exchange (ONNX) Runtime). Embedding is the only expensive
step: about 45 documents per second per process, so two shards took
roughly four hours for the corpus.
The covariance eigendecomposition is then exact up to numerical solver precision. Let be the weighted first-moment sum , and the weighted second-moment sum . Together with total weight , these are sufficient statistics for the mean and covariance: summaries from which both can be reconstructed without retaining all rows. A first pass accumulates — is —in parallel over partitions and merges them by addition. With , the identity gives the covariance. A dense symmetric solver decomposes it in milliseconds; the top 60 eigenvectors carry 55% of the variance. A second pass projects every article, orients each component by skewness, and writes coordinates and per article. A third pass re-tokenises the corpus with the LSA vocabulary and computes the centred lexical loadings defined above. A varimax rotation of that matrix gives the naming rotation.
The rotated embedding axes are mostly topical—Trump · president · Republican · Senate · Obama; covid · vaccine · coronavirus · pandemic · hospital; climate · energy · emissions · carbon · fuel; flight · airline · passenger · airport · aircraft; court · judge · sentence · trial · legal; Russian · Russia · Ukraine · Putin · Kremlin · missile; China · Chinese · Beijing · India · Hong Kong; car · vehicle · driver · road · manufacturer; church · pope · Catholic · bishop · Vatican; Ireland · northern · IRA · Belfast · Sinn—but a few are axes of register that no term matrix can see: think · thing · know · seem · want · question · feel (opinion and interview), resign · leave · down · fall · cut · suspend · cancel (the vocabulary of ending), claim · say · statement · deny · reject · ask (contested assertion). Whether these are archetypes or artefacts is an editorial question the comparison page leaves to the reader.
Both are eigendecompositions of a centred second-moment matrix; the difference is whether the document is a bag of words or a point in a semantic space. The term basis is interpretable by construction and has no neural embedding model to pin; the embedding residual measures variation outside the retained semantic subspace and can include model artefacts as well as novel content. The embedding basis also clusters stories far better (in LSA space, same-event articles rarely exceed cosine 0.7; in embedding space the 9/11 day collapses into a 34-article lead story at 0.72). Their eigenvalue spectra differ in shape: LSA’s leading singular values decay slowly, and its top axes are broad (markets, party politics), while the embedding covariance concentrates variance in fewer directions. The Evaluation section quantifies the agreement.
A possible daily refinement maintains the sufficient statistics already defined. Let be the set of new fitting observations on day , and let subscripts and denote accumulated states before and after their addition. For fixed weights, the updates are , , and . A fresh eigendecomposition follows—exact up to numerical precision and cheap at . If a later arrival changes a day’s normalising weight, earlier contributions from that day must also be updated.
What is not free is identity: the symmetric eigensolver
eigh returns eigenvectors in eigenvalue order with
arbitrary sign. Components can swap when eigenvalues cross, and nearly
degenerate groups can rotate. “Archetype 18” must therefore be tracked.
Kuhn–Munkres assignment chooses the one-to-one matching with greatest
total absolute cosine; signs are then chosen so matched dot products are
positive. Orthogonal Procrustes aligns a group by the rotation or
reflection minimising squared differences between corresponding
directions. Warm-starting varimax means beginning its optimisation from
the previous rotation. A component with no match above 0.8 is
born and announced; one whose eigenvalue falls below the chosen
noise edge is retired but kept in the registry so old issues
still resolve.
For a conditional stability statement, let be the acute angle between corresponding unit eigenvectors before and after an update. Let be the smallest absolute difference between the matched new eigenvalue and any other eigenvalue of the old covariance (excluding the old axis being compared), and let the matrix norm denote the spectral norm, its largest singular value. In an applicable separated-eigenvalue case the theorem gives . The bound does not identify individual axes inside an unresolved eigenvalue cluster.
To state an update bound explicitly, let be the new batch’s fraction of total weight, and let be its largest distance from the old mean. For a nonempty batch, weighted covariance updating gives . If batch fractions decrease at the rate , covariance and batch distances remain bounded, and the relevant separation stays positive, the per-update angle is : bounded by a constant times for sufficiently large . This conditional shrinking-step bound alone does not prove convergence of the sequence of bases.
Two ordering rules keep such a chronological protocol honest. Measure day against the basis through day before updating its statistics; otherwise today’s observations partly explain themselves. Retain the full historical archive rather than introducing unreported recency weighting. The first edition uses uniform article weights and the second the day-balanced weights below; era statistics serve a different comparison purpose.
Matching, alignment, statistics, and rotation have implementations
and tests in et-basis. Their automatic nightly refitting
schedule remains future work (Limitations and next steps); the deployed
editions measure new articles in fixed bases.
The first edition’s basis is the basis of 1999–2026. With the Times archive in place the question is what weight to give 175 years: by article count the modern era dominates (the Guardian alone contributes about as many articles per day as the Times ever did), and by wall-clock time it is fifteen per cent of the record. The second edition takes the second view and weights each article by the inverse of the number of articles published on its day. Let index an article, its publication day, and the number of fitted articles on day . For a day with articles,
so that a day in 1887 and a day in 2024 contribute equally to and (§The mathematics; the covariance formulae are unchanged). For term-space fitting, let be the weighted mean term column. The LSA of the second edition is the SVD of with the same weights, so both bases are balanced the same way. This fitted term mean is distinct from the unweighted lexical-naming mean . The total weight is the number of days with news, 63,270.
Fitting on the Times exposed a register problem that the
modern sources never had. The strongest terms of several rotated axes
were titl · articl · page · front · editori · correct · repts:
the vocabulary of the newspaper’s own index rather than of events. Three
rules, applied only to Times documents before tokenising,
remove it: placeholder headlines lose their boilerplate (“Article 8 – No
Title” is dropped, “Letter to the Editor 3 – No Title” keeps its kind),
the “LEAD:” marker is stripped, and a source-specific stop list drops
the index’s annotation words (illus, por,
repts, mfrs, page, correction,
editorial and the like). The rule lives in one place,
et-cli’s terms module, and is shared by the
LSA fit, the eigen term loadings and the nightly projection; the
embedding axes themselves are untouched by it, since embeddings are
computed from the raw text. After the fix the sixty axes of the 175-year
basis read as news: quarterly earnings; the presidency; deaths and
funerals; Israel and Palestine; sports; the boroughs of New York; the
courts; Congress and taxes; banking; corporate appointments; police and
crime; war and the military; elections; the performing arts.
We measured the two eigenbases on the same 20,471 Wikipedia events of 2023–2026, whose human sections were never used in fitting. Section purity is the fraction of labelled events whose assigned axis’s majority section matches their own label. For two orthonormal loading matrices in the same embedding space, the singular values of their cross-product are principal cosines: they compare the retained spans independently of individual axis labels. A matched-axis cosine instead compares the paired directions themselves; bars denote its absolute value.
| v1 basis (1999–2026) | v2 · 175 years | |
|---|---|---|
| explained variance of the top 60 axes | 0.550 | 0.545 |
| section purity on 2023–2026 events | 0.431 | 0.435 |
| axes that dominate at least one event | 58 / 60 | 60 / 60 |
| largest axis’s share of the events | 58% | 27% |
| mean principal cosine between the two 60-dimensional spans | 0.875 | |
| principal directions with cosine above 0.9 | 42 / 60 | |
| median of Kuhn–Munkres-matched axes | 0.32 |
The old news did not overwhelm the new. The 175-year basis explains
today’s events exactly as well as the modern one and distributes them
better: the first edition sends 58 per cent of labelled events to a
single armed conflict axis, the second keeps its largest axis
to 27 per cent and separates conflict, disasters, law and crime, and
elections. The last three rows say how: the spans of the two
bases nearly coincide (42 of 60 principal directions agree to within
26°), but the individual axes within that span have rotated, as the
Davis–Kahan bound permits when eigenvalue gaps (here of order
)
are smaller than the perturbation a new weighting introduces. Since the
newspaper prints axes, not spans, the two editions are different
newspapers of the same news, and the deployed paper carries both: the
root edition is the first, /v2 the second, and every page
has a masthead switch that shows both editions with the current one
marked.
A day page (Figure 1) opens with the spectrum: for each basis, the axes ranked by their share of the day’s energy, each bar coloured when its -score against the era statistics is beyond , followed by a Silence line listing archetypes that are usually present and are not. On 24 February 2022 both bases put russian · russia · ukrain · putin at ; the LSA also flags covid and nation · million · union · strike, the eigenbasis unit · injur · forc · target.
Then the stories, ranked by energy times so that big and unexplained rises. Each card carries the dominant archetype of each basis as a badge, the human section if a bulletin member had one, the counts of articles and sources, the episode day, and novelty, and then one panel per basis: the top four coordinates as bars; Same as always—the strongest axes of the archetype’s profile that this story also carries; Different this time—the axes on which the story departs from the profile by more than , with direction and magnitude; and the five closest precedents from distinct episodes, each a link to its day. For the invasion story both panels honestly report nothing beyond 1.5σ of this archetype’s profile—it is a textbook instance of its archetype—while the precedents differ instructively: the LSA finds the same week’s build-up briefings and 26 February 2014 (Crimea, 0.97), the eigenbasis reaches for “Ukraine: what will happen now?” (2 March 2014) and “Ukrainian president: Russian troops have crossed border” (28 August 2014).
The day after 11 September 2001 (Figure 2) shows the delta panel doing its job. The spectrum lights attack · bomb · terrorist · terror at , european at and market at ; the lead story, 34 articles under the terrorism archetype, is different from that archetype’s profile in one direction—flight · airline · passenger · airport, more than usual—and its precedents are “Airports put on alert for bombs” (December 1999), “US bases in Italy on terror alert” (January 2001) and “Bin Laden is suspect in rocket raids” (November 1999).
Below the stories, The Residual lists the day’s stories with the highest : the ones the basis explains least, which is where the genuinely new hides.
An archetype page (Figure 3) shows the axis’s full term list, the number of stories it dominates, its energy by year as a bar per year—the Ukraine axis has spikes in 2014 and from 2022—the distribution of human sections among its stories (1,084 Armed conflicts and attacks, 529 International relations), and the canon: the purest instances over the years, one per episode, by coordinate.
The comparison page (Figure 4) is where the two algorithms meet: the number of axes, the number of articles with coordinates, the section purity of each basis, the matched-axis table, and every axis of each basis with its terms, story count and majority section. An all-issues page lists every day since 1 January 1999.
Wikipedia’s bulletins carry a human section per event. The sections were never used to fit either basis. For each basis we define an axis’s majority section as the most common section among the labelled stories it dominates, and section purity as the share of labelled stories whose dominant axis’s majority section is their own. The LSA basis scores 42.4%, the embedding eigenbasis 40.0%. Both are far above chance (the largest section is under a quarter of the labelled stories), and both are limited by the same thing: the sections are eight coarse categories while the axes are sixty fine ones, so an Armed conflicts bulletin about Gaza and one about Ukraine sit on different axes that both have Armed conflicts as majority—counted as hits—while Politics and elections is spread across a dozen political axes whose majority sections differ.
Correlation here is the covariance of two coordinate series divided by the product of their standard deviations; the table abbreviation “corr” denotes it. Matching the sixty LSA axes to the sixty embedding axes by Kuhn–Munkres on the absolute correlation of story coordinates (a deterministic 100,000-story sample) gives pairs whose correlations run from 0.75 down to about 0.5. The clearest pairs are the ones a reader would draw by hand:
| corr | v0 · LSA axis | v1 · eigen axis |
|---|---|---|
| +0.67 | russian russia ukrain putin ukrainian moscow | russian russia ukrain putin ukrainian moscow |
| +0.67 | parti labour tori conserv leader elect | elect parti labour vote minist tori polit |
| +0.62 | court judg rule case trial legal justic | court judg case sentenc trial law legal |
| +0.62 | trump donald presid biden republican | trump presid donald republican senat |
| +0.61 | climat emiss chang carbon global warm | climat energi emiss gas carbon fuel |
| +0.59 | covid case vaccin pandem coronavirus | covid vaccin health case coronavirus |
| +0.59 | flight airlin passeng airport plane | flight airlin passeng airport plane air |
| +0.57 | european europ franc germani french | european europ franc french germani brexit |
The negative correlations in the full table are also informative: the LSA’s police · arrest · investigate · murder axis pairs at with the embedding’s world · company · market · develop axis—the two bases agree that crime reporting and business reporting are opposite ends of one direction, and disagree only about which end to name.
The term basis cannot see register; the embedding basis cannot help seeing it. The embedding basis clusters events far better and gives a meaningful residual; the term basis produces crisper names and needs no model. On the evidence of Recovery of human categories neither dominates, and the deployed paper prints both.
The first edition was fitted on a laptop in an afternoon, and its
code held what it needed in memory: every article’s embedding and
coordinates for orienting the axes, and the whole document–term matrix,
twice, for the term loadings. At 16.5 million articles that design was
killed by the operating system in the third pass of
et eigen, at about forty gigabytes. The fix was not a
bigger machine but a change of shape: every quantity the fit needs is a
sum over documents, and sums can be accumulated over partitions and
thrown away.
Covariance already was. The total weight , first-moment column , and second-moment matrix defined above are sums over article rows: , , and . Each partition accumulates its own contribution, and merging adds corresponding entries.
Orientation used to sort every axis’s coordinate column to decide its sign. For a fixed raw axis , let index article rows, be entry of the raw coordinate matrix , and count accumulated rows. Let be scalar sums of the first, second, and third powers: , , and . These scalar power sums are distinct from the SVD matrix . Let be the mean coordinate and the third central moment, the average cubed deviation from that mean. For it is
An axis is flipped when , exactly the rule the batch code applied to the full column (a test checks the two agree on every axis, including the merge of two partial accumulators).
Term loadings use the standardised raw score matrix , the term matrix , and the unweighted term mean defined above. Expanding the centred product gives . Let index disjoint row blocks, their row counts, and their aligned term and score matrices, and a column of ones. Since the blocks partition all rows,
Each partition therefore tokenises its own articles into a small
sparse block
,
projects and standardises its own embeddings into
,
adds
(a
dense matrix, 24 MB), the column sums of
and the column sums of
into an accumulator, and is dropped. Blocks may arrive in any order and
from any thread; accumulators merge by addition. A property test checks
that the block form equals the whole-matrix operator to
.
The same pass writes the partition’s coordinates and
rows, so the whole of et eigen is now three streaming
passes—covariance, orientation, measurement—and runs on the
16.5-million-article corpus in ten minutes at 0.3 GB resident.
Stories clustered the archive after loading every vector; a day’s clustering depends only on that day and, for episode threading, on the previous day’s stories, so the pass now runs month by month, carrying yesterday’s stories across the boundary and holding one month of articles at a time.
Two things still do not stream. The randomized SVD of the term matrix
needs
in memory—455 million non-zeros for this corpus, a peak of 31 GB—which a
64 GB laptop affords; block products could remove that requirement too.
For example, let
count test columns and let
be a dense row-aligned test block;
and the column sums needed for centring both decompose over row
partitions. This is the obvious next step. And the site’s in-memory
index, which serves any day from memory and finds precedents across the
whole archive, holds all 13.4 million stories of the second edition
(about 20 GB). That last fact decides where things run: the GitHub
runner (7 GB, 14 GB of disk) builds the root edition nightly and never
pulls the second edition’s tables; the second edition is maintained from
the laptop by a script that pulls the runner’s fresh articles and
embeddings, measures them against the fitted bases without refitting,
extends the stories, exports the recent issues and deploys them under
/v2.
Two failure-mode decisions were made along the way. Partition reads that used to be swallowed into empty results now fail the fit with the partition’s name, and every partition’s articles are checked id-by-id against its embeddings before they are measured, so a stale or partial embedding can no longer silently shift a basis.
| pass | before | after |
|---|---|---|
| eigen: orientation | all vectors and coordinates in memory (≈30 GB) | power sums per axis (0 GB) |
| eigen: term loadings | two copies of plus (≈20 GB) | one accumulator per thread (24 MB) |
| stories | all vectors and metadata (≈25 GB) | one month at a time |
| LSA: randomized SVD | in memory | unchanged (31 GB peak) |
| site index | all stories | unchanged (20 GB for 13.4 M stories) |
The system is a Rust workspace of ten crates behind one binary,
et:
| crate | role |
|---|---|
et-core |
domain types: Article, Source,
YearMonth, with validating constructors |
et-sources |
archive adapters: pure parsers over fixtures, rate-limited retrying fetchers |
et-store |
Parquet layout, atomic partition writers and readers, a Spark Connect session to Sail |
et-embed |
the pinned embedding model |
et-text |
tokeniser, vocabulary, sparse TF-IDF rows with implicit centring, LSA |
et-basis |
covariance accumulation and eigendecomposition, randomized SVD over a linear-operator trait, varimax by pairwise rotations, Kuhn–Munkres matching, |
et-stories |
daily single-linkage clustering, energy, episode threads |
et-models |
persistence of fitted models as long-format Parquet, so Sail can query them |
et-site |
the in-memory index, the statistics, the pages, an axum server with per-IP rate limiting, and the static export |
et-cli |
et backfill · embed · lsa · eigen · stories · serve · export · sql |
The code follows a normative style guide inherited from the author’s
QueryGraph work: algebraic data types for domain alternatives, a
functional core behind an imperative shell, typed errors at library
boundaries, tests in sibling files, and
fmt/clippy -D warnings/test as
gates. Two implementation notes are worth recording. A plane, or Givens,
rotation changes only two coordinate axes at once. Kaiser’s varimax
method sweeps over pairs, choosing an angle to improve the
loading-concentration objective. It is used rather than the SVD-polar
fixed-point iteration, which repeatedly recomputes a whole orthogonal
rotation from an SVD, because the latter oscillates on symmetric
configurations (it bounced between two mirror-image 30° states on a test
case) while the pairwise method increases the criterion monotonically.
For a sparse input matrix
and dense block
with the same row count, the sparse operator computes
by per-thread partial accumulation over row chunks, which is what makes
randomized SVD on a 149-million-non-zero matrix a matter of seconds per
product.
The site loads the whole index of the first edition in about three minutes—1.09 million article records, two 60-dimensional coordinate tables, 842,278 stories, the era statistics and profiles—and serves any day from memory; the second edition’s index (16.5 million records, 13,421,090 stories) loads in about one minute from local disk and holds 20 GB. For deployment it is exported as 10,227 static objects (about 2 GB) to a private S3 bucket behind CloudFront, with an AWS WAF rate-based rule (600 requests per IP per five minutes) as the edge rate limit and a Route 53 zone for the domain.
The delta is quantitative only. The design calls for a per-archetype slot schema—belligerents, trigger, external powers, weapons class for a war; office holder, allegation, evidence, stage for a scandal—filled by a language model for today’s story and its precedents and diffed. That layer, and the location of the residual in the story’s own sentences (embedding sentences and taking those most aligned with ), are designed but not built; the deployed paper says how far a story is from its archetype’s profile, not yet what is different in words. Its honesty is a feature—the invasion of Ukraine reads as a textbook instance of its archetype, which is true of its coordinates—but the reader wants the actors.
Names are term lists. Without a language model the axes are named by their loadings; russian · russia · ukrain · putin is unambiguous, unit · injur · forc · target less so.
The basis is the basis of the sources read. Three Anglophone sources make “a bad turn in the Middle East” an archetype and “a bad turn in the Sahel” a residual. Per-source contributions to each axis should be tracked and printed.
The nightly loop measures but does not yet refit. Both editions are rebuilt every night—the root edition by a GitHub Actions runner, the second from a laptop—by refreshing the sources, embedding and measuring the new articles against the fitted bases, extending the stories and exporting the recent issues. The refinement mathematics is implemented and tested but not scheduled: the colophon reporting drift against its bound, the RSS and Google News adapters, and the Time Machine (any date against any basis version) are the next build.
Register axes. Whether the embedding basis’s opinion, ending and assertion axes are archetypes of news or artefacts of the embedding is unresolved; the comparison page is the instrument for deciding. The 175-year basis adds a variant of the question: obituaries, marriage announcements and letters to the editor are recurring newspaper content and form axes of their own, which a reader of today’s page may or may not want counted as news.
Eigen Times reads a quarter-century of news as a matrix and prints each day as coordinates. The archetypes it finds—by two unrelated constructions—are the ones a reader would name, and its statistics give “how is it different this time” a number before a sentence. The system is small: one binary, a laptop, an afternoon of backfill, five minutes for one basis and a few hours for the other. What it lacks is the language layer that turns a excess on flight · airline · passenger · airport into the sentence a newspaper would print. That is the next issue.
The system and this article were built in a single day’s pair-programming session with Claude (Fable 5) in Claude Code, from a one-paragraph brief to the deployed site. The Guardian Open Platform, the Wikimedia Foundation and the Wikinews community made the corpus possible.