wordgraph — the word graph
The Elephant's graph (docs/animals.md). A dense, bounded association graph over word ids: which subjects a verb takes, what is eaten, what a dog is, which adjectives go with a noun. It is built by passes over parsed text (Wikipedia tier 1, earlier parses in CoNLL-U), keeps only the strongest neighbours of every word under every relation, and is read in place from one file, so many threads of one process can share it.
It sits beside kg, not inside it. kg stores facts: one simple sentence becomes a fact vertex with its members,
provenance per sentence, u64 heap ids, Ukrainian grammar. The word graph stores no sentence and no fact: it stores
counted, scored word–word associations over the en dictionary's lemma numbers. In the terms of
Worlds and knowledge it is an important-graph projection (operational, in RAM, prepared
indexes), rebuilt from passes whose provenance it keeps; it is not the global graph's record of claims.
Model
Nodes. A node is (word, UPOS). A word is a lemma number of the built-in en dictionary (en::dict::lemma,
append-only, so numbers are stable) or, for a lemma outside the dictionary, OOV_TAG | r with r the line of an
append-only registry (oov-registry.tsv next to the graph). Only content words take part: NOUN, PROPN, VERB, ADJ,
ADV. A lemma is lowercased unless it is a PROPN. An out-of-dictionary node is kept only if it occurs in at least
min_oov (20) counted events.
Edges. Typed by relation, from the UD head to the dependent:
| relation | from | example |
|---|---|---|
nsubj, nsubj:pass, obj, iobj | verb (or other predicate) | nsubj(bark, dog), obj(eat, apple) |
obl:<prep>, nmod:<prep> | head | obl:in(live, city), nmod:of(capital, country) |
agent (obl:agent), poss (nmod:poss) | head | agent(elect, voter), poss(owner, dog) |
amod, compound, advmod, xcomp, conj | head | amod(dog, small) |
isa | copular subject → nominal predicate | isa(dog, animal) from «a dog is an animal» |
prop | copular subject → adjectival predicate | prop(snow, white) |
A negated predicate gives no nsubj, obj, isa or prop. Conjuncts of a core argument inherit its relation,
and a conjoined verb without a subject inherits the first verb's subject (one level of the enhanced-UD propagation).
The rules are in src/extract.rs; its tests are small trees.
Scores. Over the merged counts c(h,r,d) with marginals c(h,r,·), c(·,r,d) and N_r:
PMI = log2(c·N_r / (c(h,r,·)·c(·,r,d))) and LMI = c·PMI. A pair needs c ≥ min_count (3) and PMI > 0.
Keep only the important. Per (head, relation) the top k (32) dependents by LMI are kept (forward lists), and
per (dependent, relation) the top k heads (reverse lists). Why LMI for the choice: raw PMI ranks rare pairs first
(a pair seen 3 times between two rare words beats «dog barks»); G² (Dunning 1993) ranks by evidence, and its
observed-cell term is 2·ln2·LMI, so LMI ranks like G² on the frequent pairs that matter for hints at the cost of
one multiplication (the local-MI weighting of Evert 2005 and Baroni & Lenci 2010's Distributional Memory, which is
the same typed-tuple design). PMI is stored as the edge weight, because it is scale-free: plausibility queries
compare it across words of different frequency. Bound: edges ≤ 2·k·(number of (node, relation) groups).
Quantized weights. PMI in 1/16 bit (u8), count as log2 in 1/8 steps (u8). A forward or reverse edge costs 6 bytes (target u32, PMI u8, count u8) plus its share of the group table.
Passes and build
A pass reads one source into parts: one part per input unit (a tier-1 shard of 500 articles, or a CoNLL-U file), each
a zstd stream of sorted unique (key, count) records with its own sorted OOV table, plus a sidecar with counts and
the part's SHA-256. meta.json keeps the provenance: source, inputs, the parse models seen in the records, the
dictionary fingerprint, the date and, for a control, the shuffle seed. A pass resumes: a part whose files exist is
kept.
The build merges all parts of all passes with a streaming k-way merge (the monotone OOV rank map keeps the merged
stream sorted without re-sorting), twice: first for the marginals, then for the candidates. Then it selects the top
k both ways, assigns registry ids to the kept OOV words and writes the file. Merging is commutative: the same set of
passes, in any order, with the same parameters and registry, gives the same bytes.
Passes are additive. Two passes over the same text count it twice; that is the caller's choice (and recorded).
Exceptions: the list of the wrong and the white list
Parse errors that the counts repeat are fixed by hand, through two lists in this crate: seeds/wrong.tsv (the list
of the wrong) and seeds/right.tsv (the white list). Columns: head lemma, relation, dependent lemma, kind, note,
source phrase. Both are hints, never bans, so a joke or a figurative reading can still use a listed edge.
| kind | effect |
|---|---|
compound_reading (wrong) | where the phrase stands as consecutive words ending at an end of the edge («eating disorder», «hot dog», «swimming pool»), the extractor does not count the edge and counts it as suppressed (suppressed:<phrase> in the part); elsewhere the edge is counted and penalised |
parse_error, figurative (wrong) | counted; queries return the edge with its plausibility × 0.25 and its note |
correct (white) | a listed pair without an edge answers 4 bits |
A graph carries the text of the lists it was built with, and answers with them (Graph::judged); the writer hint
(plausibility_isa, rank), the role score and the absurdity check use the judged value, and plausibility keeps
the counted one. The seed holds the two operator examples, 22 gerund compounds that the 50 k graph shows as verb +
object (swimming pool, living room, operating system, running mate …), one parse error («dogs are prone to …»)
and one figurative line («time is money»); wordgraph lists check --graph G shows every entry against a graph and
wordgraph lists add wrong|right FILE … appends a checked line (then rebuild the crate and the graph). The gerund
compounds are one systematic parser error (a VBG modifier read as a verb with an object); the list names its frequent
cases, the parser should learn the pattern.
Role swaps: the nonsense matrix
For every clause with two or more arguments in different roles (nsubj, nsubj:pass, obj, iobj, obl:<prep>,
agent), the pass also counts each pair of arguments with their roles traded: «the dog chases the cat» adds
~nsubj(chase, cat) and ~obj(chase, dog). Transpositions give the same per-(word, role) totals as all permutations,
up to a constant per clause size. The swaps are relation codes of their own (SWAP | code, printed ~nsubj), counted
in the same parts; no sentence is kept. Swaps are defined on dependency roles, not on positions, so the same code
serves languages with other word order; the part records its language.
The build compares each swap count s(h, ~r, d) with the real count c(h, r, d) of the same pair and keeps the swap
edge when s ≥ min_count and contrast = log2((s+1)/(c+1)) ≥ 1 bit, ranked by s·contrast: swap edges are kept only
where they do not coincide with real use. Graph::role_score(h, r, d) is the judged plausibility minus that contrast,
minus half of how much better the part-of-speech swap reads, for nsubj/obj («the bark dogs» against «the dog
barks»: the dependent as the verb, the head as the noun; looked up in the real lists, nothing extra stored). Like the
hand lists, it is a hint: a joke can still say «the bark dogs».
Parse scorer. score::tree_score sums, over a candidate tree's typed pairs, the role score for core relations and
the judged plausibility for the others (prepositional attachment, modifiers). wordgraph rerank GRAPH MODEL DEV adds
λ · tree_score to the parser's score of its k best trees; λ is chosen on one half of the sentences (by parity) and
applied to the other, so no number is chosen where it is reported. The test set is not used.
Graph format 2
The file keeps, per list, the number of candidates it had before pruning and the LMI of its last kept edge (sections
22–25), and the hand lists (26–27). Graph::absence(h, r, d) says why a pair has no edge: NotCandidate (certain:
a list that kept all its candidates cannot miss a candidate; so seen fewer than min_count times, with PMI ≤ 0, with
an end outside the kept vocabulary, or never), MaybePruned { below_lmi } (both lists were full), Unknown (a
format-1 file). Format-1 files still open; all earlier methods are unchanged.
The whole of Wikipedia
peta/tools/wikigraph (internal) is the Elephant's pass over the whole English Wikipedia dump: streams read in place
through wikicorpus's reader, articles of namespace 0 that are neither redirects nor disambiguation pages, cleaned as
the Wikipedia school reads them, parsed with en.bin in memory, extracted and counted. Only count parts and their
provenance are written: no text, tree, CoNLL-U or cache. A unit grows in chunks of 64 streams until it holds 20 M
events or 1 000 streams, so the memory stays bounded on the dump's oldest, longest articles; a part is named by its
stream range (dump-AAAAAA-BBBBBB) and the pass resumes at the first range not done. A STOP file stops it between
chunks. Tier 1 is counted again here with the current model and one cleaning, and the whole-Wikipedia graph is built
from this pass alone, so no article is counted twice. The shuffled control of a finished pass is made from its parts
(wordgraph control), without parsing again. Before the build, wordgraph compact tamps the parts into fewer, larger ones (each
open part holds a zstd window during the merge); the build over the compacted pass gives the same lists.
Pass v5 (current). wikigraph v5 reads the articles in page-view order (a year of user views from wikicorpus's
graph/articles.tsv, most viewed first, ties by page id, disambiguation pages out; 6 786 209 articles), so every
checkpoint covers the most viewed articles first. Each article is read from the dump by stream offset and annotated
as wikicorpus annotates (text, sentences, en trees, frames, pragmatics). A unit writes its count part (events from
the record's own CoNLL-U) and its treestore files (trees/rank-A-B.w.mts, two writer threads, level 19 with the
context coder, store model trained on 250 articles of the pass's own output); the store files are complete before the
part's done mark. Past the first million ranks, windows of a million ranks are read in stream order, so one stream
decode serves about four articles; inside a window the view order is not kept. A budget guard stops the pass when the
store reaches 250 GB or the disk's free space falls under 300 GB. Earlier stream-order passes (v3, v4) were stopped
and are not part of the graph: no article is counted twice. runs/post-pass.sh (outside the repo) builds and
measures a graph from the parts at any time; a watcher runs it at 10, 30, 50, 70 and 90 % of the order and, tamped,
at the end.
Prospective predictions for v5 (2026-11-06, before its first checkpoint). 230–360 M sentences; 240–390 CPU-hours of annotation (about 3.4 times the trees-only pass: frames and pragmatics); with 10 annotation threads on a box at load about 12, 1.1–1.8 days; store about 25 GB; count parts about 8 GB; peak memory under 3 GB.
The Elephant (docs/animals.md) builds the graph, spins sentences' roles into the absurdity graph (the role swaps), and tamps the knowledge and absurdity graphs.
File and API
One file, sections as flat little-endian arrays, 8-aligned (layout table in src/graph.rs): nodes sorted by
(word, UPOS); relation codes; forward and reverse CSR (node → relation groups → ranked edges); OOV words (by id and
by string); provenance JSON. No pointer, no deserialization: Graph::open maps the file (memmap2), checks the
section table and the CSR offsets once (O(N + G)), and every query reads the bytes in place. Graph is
Clone + Send + Sync (an Arc over the map), so threads of one process — the Owls, later — share one copy;
reads need no lock.
let g = wordgraph::Graph::open(path)?;
let bark = g.node("bark", Some(UPos::VERB)).unwrap();
let subj = wordgraph::rel::parse("nsubj").unwrap();
g.edges(bark, subj, Dir::Fwd); // typical subjects, best first
g.plausibility(bark, subj, dog); // PMI bits, 0 = no evidence
g.plausibility_isa(bark, subj, poodle); // with the isa back-off: (score, Via)
g.rank(bark, subj, &[dog, tree, bank]); // the writer's hint: candidates for a missing slot
Commands
cargo build --release -p wordgraph
G=$MOVA_DATA/graph
wordgraph pass --out $G/passes/wiki-t1-00-10 --name wiki-t1-00-10 --wiki $MOVA_DATA/corpus/wikipedia-en/tier1/shards --shards 0..10 --jobs 6
wordgraph pass --out $G/passes/ctl-shuffle --name ctl-shuffle --wiki … --shards 0..10 --shuffle 1 # negative control
wordgraph pass --out $G/passes/school --name school --conllu /path/to/all.conllu
wordgraph build --out $G/wiki-5k.wg $G/passes/wiki-t1-00-10
wordgraph stats $G/wiki-5k.wg
wordgraph query $G/wiki-5k.wg subjects bark # also: objects, isa, modifiers, does, show, near, edge, hint
wordgraph query $G/wiki-5k.wg hint bark nsubj dog,tree,bank
wordgraph absurdity $G/wiki-5k.wg global/data/absurdity-roles.tsv
wordgraph rerank $G/wiki-all.wg $MOVA_DATA/en/models/en.bin $EN_UD_DIR/UD_English-EWT/en_ewt-ud-dev.conllu --k 8
wordgraph control --in $G/passes/wiki-all-v3 --out $G/passes/wiki-all-v3-shuffled --name wiki-all-v3-shuffled --seed 1
wordgraph lists check --graph $G/wiki-all.wg
Heavy runs go through tools/jobs (jobs run --mem-gb 4 …).
Checks
extracttests: subjects, objects, conjunct propagation, copularisa/prop, negation, OOV interning, a malformed tree refused, key packing order.passtests: the control shuffle keeps every marginal, is seeded, and changes the pairs.graphtests: a hand-built file reads back; a corrupt section table, a truncated file and an edge target beyond the node table are refused; quantization is monotone and bounded.tests/build.rs: two builds of the same passes are byte-identical; passes given in another order give the same bytes; every (node, relation) list holds at mostkedges; a shuffled control loses the subject–verb signal that the real pass has.exceptionstests: the seed lists parse; a short row, an unknown relation, a white kind in the wrong list and a duplicate are refused; phrase matching does not underflow.extracttests (rules v3): role swaps only between different roles of one clause, none for a one-argument clause; «eating disorder» suppressed and counted, and «eats … disorder» (negative control) counted.passtests: a part round-trips with its OOV frequencies; an empty OOV table reads.tests/build.rs(more): a listed wrong edge (time isa money) keeps its count, is still retrievable, answers with the penalty and its note; an unlisted edge (dog isa pet) is unchanged (negative control); a white-listed pair is at least 4 bits; «the cat chases» is a swap and «the dog chases» is not, and the role score ranks them so.evaltests: AUC and Spearman show both colours.
Prospective predictions (written 2026-11-06, before the first real run)
- 5 000 articles (shards 0–9, about 2.2 M sentences, 55 M tokens): about 25 M events; pass 30–120 s with 6 threads, peak below 1.5 GB; build below 2 GB and 2 min; about 100 k nodes, 2–4 M edges, file 20–50 MB.
- 50 000 articles: pass 5–15 min, build below 4 GB; about 300 k nodes, 8–15 M edges, file 80–200 MB.
- Subjects of «bark» include dog; of «elect» voters, people, council or parliament; objects of «eat» food words (meat, fish, food); is-a of «dog» animal, breed or mammal.
- Absurdity matrix, subject column (real-world scale): AUC of normal (0) against absurd (3–4) between 0.65 and 0.80 on the 5 k graph, higher on 50 k; the shuffled control within 0.5 ± 0.05. Coverage is limited by the 405 verbs × 601 nouns being frequent words, so most pairs are covered by nodes, but many plausible pairs have no edge (pruned or unseen): expect evidence for 20–40 % of normal pairs and under 5 % of absurd ones.
Prospective predictions for the whole of Wikipedia (written 2026-11-06, before its first build)
- Pass: about 6.9 M articles and 230 M sentences (from 360 evenly spaced streams), about 1.5 billion real events and 0.5 billion swap events, 10–20 h with 8 threads on the shared box, parts 3–6 GB.
- Build: 200–400 M merged triples, 20–40 M candidates, 1–3 h, peak 2–3.5 GB (the dependent marginals and the candidates dominate); graph file 150–400 MB.
- Absurdity matrix, subject column: AUC 0.60–0.68 (50 k: 0.566), evidence for normal pairs 30–45 % (50 k: 16 %), and the share of absent normal pairs that were not even candidates below 70 % (50 k: 84 %). Object column AUC 0.68–0.74 (50 k: 0.646). Role-score AUC at least 0.02 above the plain one (the swaps add negative evidence for absurd pairs). The shuffled control within 0.03 of its 50 k values.
- k-best reranking on EWT dev: a LAS gain at least the 50 k graph's +0.26 points.
Measurements (2026-11-06)
Source: Wikipedia tier 1 (enwiki-20260901, trees by en.bin:31a93401fe7135b1), shards 0–9 (5 000 articles) and
0–99 (50 000), extraction rules version 2, k = 32, k_core = 128, min_count = 3, min_oov = 20. A shared box
(load 10–20 on 20 cores), 6 threads, every run under tools/jobs with a 4 GB limit.
| 5 000 articles | 50 000 articles | predicted (5 k / 50 k) | |
|---|---|---|---|
| sentences | 1 952 517 | 12 087 029 | 2.2 M / — |
| events | 18.0 M | 103.1 M | 25 M / — |
| pass: wall, peak RAM | 6 s, 0.59 GB | 35 s, 0.57 GB | 30–120 s / 5–15 min, < 1.5 GB |
| pass on disk | 53 MB | 337 MB | — |
| merged distinct triples | — | 35.1 M | — |
| candidates (c ≥ 3, PMI > 0) | — | 4.44 M | — |
| build: wall, peak RAM | 11 s, 0.23 GB | 58 s, 1.25 GB | < 2 min, < 2 GB / < 4 GB |
| nodes (outside the dictionary) | 46 184 (19 825 words) | 135 115 (86 699 words) | 100 k / 300 k |
| forward + reverse edges | 0.59 M + 0.66 M | 2.07 M + 2.83 M | 2–4 M / 8–15 M |
| file | 10.0 MB | 38.0 MB | 20–50 MB / 80–200 MB |
The predictions overestimated everything: fewer events per token than assumed (only content–content arcs count), and the LMI/PMI/min-count filter keeps 4.4 M of 35 M triples. A query (open the map, look up, print) takes 7 ms of wall time. 50 k nodes by UPOS: PROPN 73 231, NOUN 40 211, ADJ 12 153, VERB 7 520, ADV 2 000; 259 relation codes.
Determinism. Two builds of the 50 k pass: byte-identical (SHA-256 20bf205c…). Parts of shards 0–2 rebuilt in
a fresh directory with 2 and with 3 threads: byte-identical to the 6-thread pass.
Samples (50 k). Subjects of «elect»: Assembly, voter, member, council, constituency, Congress, Parliament, assembly, parliament, ward. Subjects of «bark»: dog (PMI 10.3, n ≈ 10), Samantha. «parse»: no node (the verb is not in 50 000 encyclopedic articles with a subject seen 3 times). Objects of «eat»: food, meat, disorder (a parse error: «eating disorder»), fruit, meal, fish, flesh, egg. Is-a of «dog»: animal, species, mammal, sausage («hot dog»), pet, threat, breed. Verbs with subject «dog»: go, have, eat, bark, bite. Hint «bark nsubj tree,dog,bank,river» → dog (direct), the rest without evidence; «eat obj stone,apple,idea,bread» → bread, apple (direct), stone 0.75 through isa material, idea none.
Absurdity matrix (global/data/absurdity-roles.tsv, real-world scale, 243 346 verb–noun pairs; covered = both
words are nodes). AUC: normal (0) against absurd (3–4), ties half. Evidence: share of pairs with an edge.
| graph | column | covered | AUC | AUC with isa back-off | Spearman ρ | evidence at score 0 / 4 |
|---|---|---|---|---|---|---|
| 5 k | subject | 228 068 | 0.533 | 0.552 | 0.156 | 7.4 % / 0.52 % |
| 50 k | subject | 235 750 | 0.566 | 0.615 | 0.218 | 16.1 % / 1.89 % |
| 50 k shuffled control | subject | 230 599 | 0.521 | 0.519 | 0.087 | 11.9 % / 6.41 % |
| 5 k | object | 228 068 | 0.581 | 0.600 | 0.225 | 17.3 % / 0.85 % |
| 50 k | object | 235 750 | 0.646 | 0.684 | 0.293 | 32.7 % / 3.13 % |
| 50 k shuffled control | object | 230 599 | 0.544 | 0.563 | 0.122 | 22.7 % / 10.4 % |
The direction is right and grows with data: an edge is 8.5 times (subject) and 10.5 times (object) likelier for a
normal pair than for an absurd one, against 1.9 and 2.2 in the control. The control is not at 0.5: it keeps every
marginal, so frequent words still meet by chance, and normal pairs are made of more frequent words. It is the
frequency-only baseline; the association signal is the difference. The AUC stays low because most normal pairs have
no edge at all (84 % of the subject ones): absence is weak evidence. The prediction (AUC 0.65–0.80 for subjects)
missed. k_core = 128 was chosen after seeing this table at k = 32 (subject AUC 0.537 / 0.581 with back-off,
object 0.592 / 0.639, extraction rules version 1), so the numbers above are not independent of that choice.
A pass over the Wikipedia school treebank (all.conllu, 127 096 sentences, 1.08 M events, 0.9 s) shows the CoNLL-U source; its articles overlap tier 1, so it is not merged into the graphs above.
Absence (2026-11-06, 50 k graph rebuilt in format 2 from the same passes). Among normal subject pairs (score 0)
without an edge, 33 315 were not candidates at all and 6 510 possibly pruned; for objects 20 381 and 5 714. The
missing evidence is mostly missing counts, not the bound k.
k-best reranking (2026-11-06, 50 k graph, rules v2 without swaps). EWT dev 2.18 (2 001 sentences, 25 148 words),
en.bin 2483516a, k = 8, λ grid 0–100, chosen on the other fold: λ = 10 in both folds. LAS 0.7974 → 0.8003 (fold 0)
and 0.8120 → 0.8143 (fold 1); overall 0.8047 → 0.8073 (+0.26 points); 98 sentences change; the 8-best oracle is
0.8536. Two runs give identical output.
French
A pass, a part and a graph have a language (rel::Lang: en, fr); the relations and the role swaps are the same,
defined on dependency roles (UD, nivre-2020, demarneffe-2021). Per language:
- Word ids. An English lemma is an
endictionary number when listed; every French lemma goes through the OOV registry, so «chat» (cat) never meets the English «chat», andmin_oovapplies to every French word. A French graph records its language in section 28 and looks words up only among its own (Graph::language,word_id). English graphs keep their bytes (no section 28: the 5 000-article graph built by the new binary is byte-identical to the old binary's, 2026-11-06); a reader older than this rejects a French graph. - Case words. French
caselemmas fill the preposition slot above everyennumber (FR_PREP_BASE, an append-only table of the 37 case lemmas of UD French GSD, guillaume-2019):nmod:de,obl:dans. «des» and «du» left whole as a determiner of anobl/nmod(the contraction of «de» with the article) count as «de». - Negation. «ne», «pas», «jamais», «guère», «nullement», «aucunement» (
Polarity=Negin GSD); «point» and «plus» are left out (mostly a noun and a comparative). - Clitics. Clitic pronouns are pronouns and take no part (no edges, no swaps). A subject whose verb has the
passive reflexive clitic (
expl:pass, «la maison se construit») is a passive subject; Mova's French parse readsexpl:passasexpl(its relation set has no French subtypes), so this applies only to input that carries it. - Lowercasing by Unicode case («État» → «état»).
- Hand lists per language:
seeds/fr/wrong.tsv(compound readings «pomme de terre», «chemin de fer», «hôtel de ville», «boîte de nuit», «mise en scène», «prise de conscience», «poids lourd», «fait divers», «coup de foudre»; idioms «tomber dans les pommes», «poser un lapin», «avoir le cafard», «casser les pieds», «coûter un bras») andseeds/fr/right.tsv(animal sounds, «manger du pain»); fingerprintb644f9b47deae49b(2026-11-06). A phrase is matched by form before the edge's end, so «pomme de terre» does not cover «pommes de terre». - One language per build: passes of two languages are refused.
The French pass. peta/tools/wikigraph-fr (internal) reads the French Wikipedia dump (frwiki-20261001) in the
in-link order of its article graph (wikicorpus scan --lang fr, wikicorpus graph: 2 635 316 ranked articles; there is
no French page-view file, so the score is the inbound links alone), cleans the frequent French templates, splits
sentences and tokens in GSD's conventions, parses with Mova's own parser trained on UD French GSD (fr.bin), gives
lemmas and UPOS from the GSD lexicon (Mova has no French morphology: lemma by (form, tag), by form, else an edit script
by tag and suffix as in Morfette), and writes count parts rank-A-B (language fr) and treestore files of the
records (text, sentence rows, CoNLL-U). Measured 2026-11-06 on UD French GSD dev:
| value | |
|---|---|
parser, LAS (own tags / gold tags; French subtypes read as their base, as en parse-eval reads them) | 87.09 / 89.78 |
| parser, LAS with the French subtypes (obl:mod, aux:tense, expl:pass … count as errors) | 78.80 |
| UAS (own tags) | 91.12 |
tokenizer (from # text), word F1 | 97.87 (P 98.35, R 97.39) |
| lemma accuracy on gold tokens (known forms / unseen forms / content words) | 97.68 (98.42 / 87.91 / 96.16) |
| UPOS accuracy (known / unseen) | 97.03 (97.56 / 90.03) |
On 1 000 articles evenly spaced over the order: 27 688 sentences, 214 632 events, 5.8 MB of store (from 50.3 MB of JSON); on the top 1 000 articles: 330 110 sentences, 3.67 M events, 88 MB of store; about 3.4–3.5 CPU-ms per sentence including the store, 0.6–1.25 GB. Projection for the whole pass: about 73 M sentences, 0.57 G events, a store of 15–20 GB, parts under 3 GB, about 70 CPU-hours (12 h with six free cores, about 2.3 days at the 1.3 cores a nice-15 job got on the box at load 30–40). A 300-article range with 6 and 3 threads: part and store files byte-identical. A graph of the top 1 000 articles: «guerre» is modified by mondial, second, civil, froid; «roi» is the subject of mourir, acheter, accorder, construire; «Paris» is a ville, a capitale.
Tamping papers
tamp/ (elephant-tamp) is the Elephant's tamping step over papers: statements that many papers
say alike are collapsed into shared approach nodes (WordNet synonyms confirmed by this graph, anchor salience from
Graph::freq), and each paper keeps its important statements with references to the nodes.
Whole-dump passes (2026-11-06). Trees-only, in stream order (v3/v4, 8 threads): 6 000–7 000 sentences/s; the dump's first streams hold its oldest, longest articles (70 articles and 14 000 sentences a stream against about 27 and 900 on average), so fixed 1 000-stream units passed 4 GB (stopped by jobs) and units are bounded by 20 M events. Checkpoint at 9 536 streams (v3: 438 k articles, 37.8 M sentences, 391 M events; build 128 s, 1.07 GB, 119 MB): subject AUC 0.584 (isa back-off 0.639, role score 0.623), object AUC 0.672 (0.707); normal subject pairs with an edge 21 % (50 k: 16 %); absent normal subject pairs that were not even candidates 63 % (50 k: 84 %). v3 stopped (plural compound phrases), v4 stopped at 31 392 streams for v5. v5 sample (250 view-ordered articles): 0.196 s per article on one thread in the tail (0.030 s the random stream read), 1.55 s in the top 50 000; store 3.6 KB and 18.2 KB per article. First v5 unit (ranks 0–6 144): 1.75 M sentences, 20.8 M events, 180 MB of store, 4 746 s on a box at load 30–60 (the pass at nice 15 got 3–5 cores), peak 2.1 GB. Its graph alone: EWT dev LAS 0.8047 → 0.8066.
Limits
- The graph is as good as the parser:
en.bintrees on Wikipedia, not checked by a teacher. A systematic parse error becomes a systematic edge. - Wikipedia is encyclopedic: «elect» and «found» are frequent, «bark» and «eat» are rare. The counts say what Wikipedia writes, not what is common sense; school and tale parses are other passes.
- Only one word per side: a multiword name contributes its head token; numbers, pronouns and function words are out.
- PMI without smoothing, a hard
min_countand a hardk: rare but true pairs can be pruned, and absence of an edge is weak evidence of implausibility. - No word sense beyond UPOS: «bank» the river side and the institution share a node.
- CC BY-SA: graphs built from Wikipedia are CC BY-SA data, kept under graph, never in git, never shipped (Data and model licences).
- French: no French morphology (lemmas and UPOS from the UD French GSD lexicon, unseen forms by a suffix script); the French parse has no French relation subtypes; «des» is never split, so its «de» is read back only under a noun modifier; the hand lists hold a few seed entries.
Sources (French pass)
- Stefan Evert 2005, The Statistics of Word Cooccurrences (evert-2005): LMI for the choice of the kept edges.
- Ted Dunning 1993 (G²) and Marco Baroni & Alessandro Lenci 2010 (Distributional Memory): the evidence ranking and the typed-tuple design (no cards; docs/usage.tsv).
- Sebastian Schuster & Christopher Manning 2016: the one-level enhanced-UD propagation of conjuncts.
- Universal Dependencies: nivre-2020, demarneffe-2021; UD French GSD: guillaume-2019 (CC BY-SA 4.0 data, trains
fr.bin). - Grzegorz Chrupała, Georgiana Dinu & Josef van Genabith 2008, Learning Morphology with Morfette: the edit-script lemmas of the French pass (no card; docs/usage.tsv).
- English and French Wikipedia (CC BY-SA 4.0): the passes' text; their graphs and stores are internal data.
Sources
Cited in the code as «(author-year)»; library cards under the research library's papers/:
| short id | card | where it is used |
|---|---|---|
| dunning-1993 | dunning-1993-accurate-methods-statistics-surprise-coincidence | G² and why LMI ranks like it (compile.rs) |
| levy-2015 | levy-2015-improving-distributional-similarity-lessons-learned | PMI's rare-pair bias, minimum counts, context smoothing left out (compile.rs) |
| pado-2007 | pado-2007-dependency-based-construction-semantic-space | typed dependency co-occurrence as the space (compile.rs) |
| turney-2010 | turney-2010-frequency-meaning-vector-space-models | the family of count-based word spaces (compile.rs) |
| hearst-1992 | hearst-1992-automatic-acquisition-hyponyms-large-text | isa from text patterns; the copula is ours (extract.rs) |
| nivre-2017 | nivre-2017-universal-dependencies | the relation inventory (extract.rs) |
| mikolov-2013 | mikolov-2013-distributed-representations-words-phrases-their | negatives by corruption, the idea behind the role swaps (extract.rs) |
| resnik-1993, resnik-1997 | resnik-1993-semantic-classes-syntactic-ambiguity, resnik-1997-selectional-preference-sense-disambiguation | class-based back-off of argument plausibility (graph.rs) |
| collins-2005 | collins-2005-discriminative-reranking-natural-language-parsing | reranking the parser's k best (score.rs) |
| zeman-2018 | zeman-2018-conll-2018-shared-task-multilingual | LAS (score.rs) |
Works without a card (rows in docs/usage.tsv): Church & Hanks 1990 (PMI), Evert 2005 and Baroni & Lenci 2010
(local MI, Distributional Memory), Schuster & Manning 2016 (enhanced UD), Smith & Eisner 2005 (contrastive
estimation), Knuth 1997 (Fisher–Yates), Steele et al. 2014 (splitmix64), Hinnant 2013 (civil-from-days), Mann &
Whitney 1947 (AUC as U), Spearman 1904, O'Neil et al. 1996 (LSM-tree compaction, the tamping).
Further sources
- Scores: PMI of church-1990 (I1 directional counts, I4 minimum count, I5 blind to compositionality); G² of dunning-1993 (I1 the 2×2 table, I4 significance comparable across rare and common events); LMI of evert-2005 and baroni-2010; typed dependency triples scored by mutual information as in lin-1998.
- Enhanced-UD conjunct propagation: schuster-2016.
- Role swaps as contrastive negatives: corrupted triples of bordes-2013 and negative sampling of mikolov-2013; the related contrastive estimation of Smith & Eisner 2005 is outside the library.
- k-best reranking check: collins-2000, koo-2005, charniak-2005.
- Adjacency layout: compressed sparse rows (bell-2009); k-way merge of sorted runs (Knuth 1998, outside); zstd parts (Collet 2016, outside).
This page in Markdown → https://md.mova.elamur.ai/free/en/docs/wordgraph/