Refresh multilingual benchmarks and fix overlapping gold evaluation
Recompute published benchmark results for all default language models, exclude Polish Polimorf, add Hebrew documentation, and record the current benchmark environment. Evaluate repeated surface forms as an overlapping gold cover and publish only applicable metrics for candidate policies.
This commit is contained in:
@@ -2,27 +2,27 @@
|
||||
|
||||
The table below describes the Radixor resources used to build speed and quality corpora. `Total tokens` is the complete dictionary token count used by quality benchmarks. `Already-root tokens` counts fields where the token is already equal to the line root. `Changed tokens` is the speed workload before the minimum-size repeat rule.
|
||||
|
||||
| Language resource | Dictionary rows | Total tokens | Already-root tokens | Changed tokens | Speed timing tokens |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| `cs_cz` | 5,113 | 56,612 | 10,049 | 46,563 | 46,563 |
|
||||
| `da_dk` | 4,179 | 32,256 | 8,356 | 23,900 | 23,900 |
|
||||
| `de_de` | 39,315 | 213,440 | 73,799 | 139,641 | 139,641 |
|
||||
| `es_es` | 65,059 | 926,393 | 120,121 | 806,272 | 806,272 |
|
||||
| `fa_ir` | 69 | 3,770 | 138 | 3,632 | 5,000 |
|
||||
| `fi_fi` | 57,027 | 1,865,215 | 110,525 | 1,754,690 | 1,754,690 |
|
||||
| `fr_fr` | 59,240 | 474,110 | 108,141 | 365,969 | 365,969 |
|
||||
| `he_il` | 2,358 | 61,071 | 4,715 | 56,356 | 56,356 |
|
||||
| `hu_hu` | 19,406 | 935,713 | 38,775 | 896,938 | 896,938 |
|
||||
| `it_it` | 10,009 | 337,546 | 20,004 | 317,542 | 317,542 |
|
||||
| `nb_no` | 17,929 | 90,757 | 33,376 | 57,381 | 57,381 |
|
||||
| `nl_nl` | 4,992 | 31,466 | 9,981 | 21,485 | 21,485 |
|
||||
| `nn_no` | 4,688 | 19,651 | 6,089 | 13,562 | 13,562 |
|
||||
| `pl_pl` | 9,990 | 132,308 | 19,957 | 112,351 | 112,351 |
|
||||
| `pt_pt` | 4,001 | 215,490 | 8,002 | 207,488 | 207,488 |
|
||||
| `ru_ru` | 37,410 | 806,279 | 74,808 | 731,471 | 731,471 |
|
||||
| `sv_se` | 12,371 | 110,468 | 24,731 | 85,737 | 85,737 |
|
||||
| `uk_ua` | 1,493 | 15,737 | 2,985 | 12,752 | 12,752 |
|
||||
| `us_uk` | 396,939 | 1,004,374 | 793,874 | 210,500 | 210,500 |
|
||||
| `yi` | 802 | 4,300 | 1,524 | 2,776 | 5,000 |
|
||||
| Default model ID | Version | SHA-256 | Language | Dictionary rows | Total tokens | Already-root tokens | Changed tokens | Speed timing tokens |
|
||||
| --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| `cs-cz-default` | `1.0.0` | `62afdaa6dc7a721b54a0dc278a0c648a63ad52a34a412d27b5b52fbcde9c1ce4` | `CS_CZ` | 5,113 | 56,612 | 10,049 | 46,563 | 46,563 |
|
||||
| `da-dk-default` | `1.0.0` | `3f7b670a0e7b872bda0381f5154ce058a4656297b39b7157b4ccf6560257cb90` | `DA_DK` | 4,179 | 32,256 | 8,356 | 23,900 | 23,900 |
|
||||
| `nl-nl-default` | `1.0.0` | `c098034adc42da2ca3e419160e6dd2c2b3868f8af334303b3a191e09caadaf5e` | `NL_NL` | 4,992 | 31,466 | 9,981 | 21,485 | 21,485 |
|
||||
| `us-uk-default` | `1.0.0` | `8c79122993499e437ea8b54b620832dca29019298f281c1f3132f4d1be885460` | `US_UK` | 396,939 | 1,004,374 | 793,874 | 210,500 | 210,500 |
|
||||
| `fi-fi-default` | `1.0.0` | `ca2628b3db31fee92f1b612ebbbd5e956a6dbbfb10e721325e55ef528f26072f` | `FI_FI` | 57,027 | 1,865,215 | 110,525 | 1,754,690 | 1,754,690 |
|
||||
| `fr-fr-default` | `1.0.0` | `a988658758952fd599dc7360e0234178a6d65ac46e5cedc7dcd325a7cb7e71d9` | `FR_FR` | 59,240 | 474,110 | 108,141 | 365,969 | 365,969 |
|
||||
| `de-de-default` | `1.0.0` | `cbfa038122823f02e4bdb54b0035492c356b6ecd80f11eb11290d7a7248a59f5` | `DE_DE` | 54,092 | 333,036 | 90,535 | 242,501 | 242,501 |
|
||||
| `he-il-default` | `1.0.0` | `9a47dc69bb7dab21aba0266b73cd74cdaeb17db94363796a0a56111ac8518256` | `HE_IL` | 2,358 | 61,071 | 4,715 | 56,356 | 56,356 |
|
||||
| `hu-hu-default` | `1.0.0` | `359d46a01d751ec823705ad7f3dd1cc8f6663feb1a9d13cb04d0c6fb51ab646e` | `HU_HU` | 19,406 | 935,713 | 38,775 | 896,938 | 896,938 |
|
||||
| `it-it-default` | `1.0.0` | `5e03be31c9761e30dbf24a47a5ced3d6ec949dabd31e92632fdd9f7c67fc2e12` | `IT_IT` | 10,009 | 337,546 | 20,004 | 317,542 | 317,542 |
|
||||
| `nb-no-default` | `1.0.0` | `f495bffb44e79d27993e6e2e65d4b1204b29365dc93f481b2d8b96766fc90fd9` | `NB_NO` | 17,929 | 90,757 | 33,376 | 57,381 | 57,381 |
|
||||
| `nn-no-default` | `1.0.0` | `900cf2005605aea2a3d8d731ec0b0c1f47fb4469b4ba6b9134145d4d026a0398` | `NN_NO` | 4,688 | 19,651 | 6,089 | 13,562 | 13,562 |
|
||||
| `fa-ir-default` | `1.0.0` | `b29a0d168a6a97f980666aa40b74a0edd8b6be4ab3320a7abfbb76b3529f4ea1` | `FA_IR` | 69 | 3,770 | 138 | 3,632 | 5,000 |
|
||||
| `pl-pl-unimorph` | `1.0.0` | `8191ed727097839cc808cbc5c56a1bd78b3c851e7733ad226ad9a51519a54721` | `PL_PL` | 9,990 | 132,308 | 19,957 | 112,351 | 112,351 |
|
||||
| `pt-pt-default` | `1.0.0` | `7a035ff330a6f0548f446cd0d6617bc1cf4751292125a3564d3a255c5d6f516d` | `PT_PT` | 4,001 | 215,490 | 8,002 | 207,488 | 207,488 |
|
||||
| `ru-ru-default` | `1.0.0` | `df7ea25e63a875eeec7a4185be685bd5372a3c568db85c34c44fdf5d8d980a40` | `RU_RU` | 37,410 | 806,279 | 74,808 | 731,471 | 731,471 |
|
||||
| `es-es-default` | `1.0.0` | `7a1ec94cfdb1e9a95431289d62dc5579cb2a532d99532eeda90290072e569721` | `ES_ES` | 65,059 | 926,393 | 120,121 | 806,272 | 806,272 |
|
||||
| `sv-se-default` | `1.0.0` | `d9be72e3d67c776622c4281e04e4063b9381e8f84a823d98ebf08888b82dff0c` | `SV_SE` | 12,371 | 110,468 | 24,731 | 85,737 | 85,737 |
|
||||
| `uk-ua-default` | `1.0.0` | `cf3f612cfff16cb7763f99c55851069489b883c3bdd1a6576cd8c57a97e07eae` | `UK_UA` | 1,493 | 15,737 | 2,985 | 12,752 | 12,752 |
|
||||
| `yi-default` | `1.0.0` | `f47de665c27dcd72833a82904e49c68a945bb5aca769a7ec5a0164e2c981a6d3` | `YI` | 802 | 4,300 | 1,524 | 2,776 | 5,000 |
|
||||
|
||||
Speed benchmarks process the complete changed-token dictionary sequence for the language. Only resources with fewer than 5,000 changed tokens are repeated to reach the minimum timing size; larger resources are not sampled or truncated.
|
||||
|
||||
@@ -6,16 +6,16 @@ This benchmark is the clearest demonstration of the Radixor quality/speed envelo
|
||||
|
||||
| Used rows | Actual row ratio | All exact | Changed exact | Root preserved | Speed ms/op | Error ms | ns/token |
|
||||
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| 100% | 100.000% | 97.478% | 97.197% | 97.552% | 28.578 | 7.571 | 135.8 |
|
||||
| 90% | 90.000% | 97.047% | 94.913% | 97.613% | 26.612 | 9.227 | 126.4 |
|
||||
| 80% | 80.000% | 96.635% | 92.768% | 97.661% | 23.331 | 8.106 | 110.8 |
|
||||
| 70% | 70.000% | 96.209% | 90.565% | 97.705% | 22.362 | 1.957 | 106.2 |
|
||||
| 60% | 60.000% | 95.750% | 88.384% | 97.703% | 16.497 | 2.026 | 78.4 |
|
||||
| 50% | 50.000% | 95.262% | 86.107% | 97.690% | 16.035 | 0.986 | 76.2 |
|
||||
| 40% | 40.000% | 94.753% | 83.855% | 97.643% | 16.459 | 0.664 | 78.2 |
|
||||
| 30% | 30.000% | 94.208% | 81.651% | 97.537% | 19.566 | 0.758 | 92.9 |
|
||||
| 20% | 20.000% | 93.633% | 79.366% | 97.416% | 14.616 | 0.487 | 69.4 |
|
||||
| 10% | 10.000% | 92.868% | 76.516% | 97.204% | 18.093 | 3.147 | 86.0 |
|
||||
| 100% | 100.000% | 97.478% | 97.197% | 97.552% | 20.627 | 2.117 | 98.0 |
|
||||
| 90% | 90.000% | 97.047% | 94.913% | 97.613% | 21.713 | 2.104 | 103.2 |
|
||||
| 80% | 80.000% | 96.635% | 92.768% | 97.661% | 17.408 | 1.438 | 82.7 |
|
||||
| 70% | 70.000% | 96.209% | 90.565% | 97.705% | 16.946 | 1.531 | 80.5 |
|
||||
| 60% | 60.000% | 95.750% | 88.384% | 97.703% | 15.735 | 1.278 | 74.8 |
|
||||
| 50% | 50.000% | 95.262% | 86.107% | 97.690% | 14.714 | 1.089 | 69.9 |
|
||||
| 40% | 40.000% | 94.753% | 83.855% | 97.643% | 15.090 | 1.254 | 71.7 |
|
||||
| 30% | 30.000% | 94.208% | 81.651% | 97.537% | 13.773 | 1.071 | 65.4 |
|
||||
| 20% | 20.000% | 93.633% | 79.366% | 97.416% | 15.396 | 2.497 | 73.1 |
|
||||
| 10% | 10.000% | 92.868% | 76.516% | 97.204% | 16.970 | 2.847 | 80.6 |
|
||||
|
||||
## Column Meanings
|
||||
|
||||
|
||||
@@ -4,53 +4,85 @@ The values below are environment-specific and must not be read as universal perf
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Benchmark date | 2026-07-06 (Europe/Prague) |
|
||||
| Focused comparison command family | `./gradlew jmh -Pjmh.includes='.*StemmerComparisonBenchmark.*' --no-daemon` |
|
||||
| English coverage command | `./gradlew jmh -Pjmh.includes='.*EnglishRadixorDictionaryCoverageBenchmark.*' --no-daemon` |
|
||||
| Speed result reports | `build/reports/jmh/stemmer-comparison-2026-07-06.csv`, `build/reports/jmh/stemmer-comparison-2026-07-06.txt`, `build/reports/jmh/english-coverage-2026-07-06.csv`, and `build/reports/jmh/english-coverage-2026-07-06.txt` |
|
||||
| Accuracy result reports | `build/reports/jmh/stemmer-comparison-2026-07-06.csv`, `build/reports/jmh/english-coverage-2026-07-06.csv`, and deterministic Radixor exact-root accounting over the same bundled language corpora |
|
||||
| Final comparison JMH scope | Stemmer comparison benchmarks only; internal `FrequencyTrie*` microbenchmarks were not run |
|
||||
| Coverage JMH scope | English Radixor dictionary coverage benchmark only |
|
||||
| Benchmark date | 2026-07-23 (Europe/Prague) |
|
||||
| Corpus command | `./gradlew benchmarkCorpusReport --no-daemon` |
|
||||
| Exact-root accuracy command | Direct JMH execution of the four `*BenchmarkQuality` classes selected in `stemmer-accuracy-2026-07-23.txt`; timing scores are discarded |
|
||||
| Stemming-quality command | `./gradlew stemmingQuality --no-daemon` |
|
||||
| Published speed command | `tools/run-published-speed-benchmarks.sh 2026-07-23` |
|
||||
| Published speed run interval | 2026-07-23 12:58:50 to 15:15:43 Europe/Prague (2 h 16 min 53 s, including idle intervals and both JMH suites) |
|
||||
| Stabilization intervals | 120 s before the main speed matrix; 60 s between the main matrix and coverage-speed suite |
|
||||
| Corpus and command report | `build/reports/jmh/benchmark-corpora.csv` |
|
||||
| Exact-root reports | `build/reports/jmh/stemmer-accuracy-2026-07-23.csv` and `.txt` |
|
||||
| Speed reports | `build/reports/jmh/stemmer-speed-2026-07-23.csv` and `.txt` |
|
||||
| English coverage accuracy reports | `build/reports/jmh/english-coverage-accuracy-2026-07-23.csv` and `.txt` |
|
||||
| English coverage speed reports | `build/reports/jmh/english-coverage-speed-2026-07-23.csv` and `.txt` |
|
||||
| Stemming-quality reports | `build/reports/stemming-quality/stemming-quality.csv` and `.md` |
|
||||
| Environment report | `build/reports/jmh/performance-environment-2026-07-23.txt` |
|
||||
| Selected speed methods | `build/reports/jmh/published-speed-benchmarks-2026-07-23.txt` |
|
||||
| Comparison scope | Same-language methods used by the 20 language pages; `PolishPolimorfStemmerComparisonBenchmark`, all quality methods, the separate CISTEM gold-standard experiment, and internal trie microbenchmarks are excluded |
|
||||
| Model scope | Exactly the 20 IDs declared by `Language.defaultModelId()`; Polish uses `pl-pl-unimorph`, and `pl-pl-polimorf` is not measured |
|
||||
| Core base commit | `1f1b03c6a8d36a0918b92ebde698e5379a2a5946` |
|
||||
| Measured source state | `release@4.0.0-dirty`; exact tracked changes and untracked-source checksums are retained as `measured-source-2026-07-23.patch` and `measured-untracked-2026-07-23.sha256` |
|
||||
| JMH version | 1.37 |
|
||||
| Speed benchmark mode | Average time, `time/op` |
|
||||
| Score unit | `ns/op` |
|
||||
| Speed warmup | 3 iterations, 1 s each |
|
||||
| Speed measurement | 5 iterations, 1 s each |
|
||||
| Accuracy warmup | 3 JMH warmup iterations were applied by the Gradle invocation; timing scores from quality methods are not interpreted |
|
||||
| Accuracy measurement | 5 JMH measurement samples; documentation uses deterministic auxiliary counter ratios from the same report |
|
||||
| Fork count in generated report files | 1 |
|
||||
| Default fork policy for accuracy-only benchmark classes | `@Fork(0)` for future default runs because accuracy counters are deterministic and not interpreted as speed |
|
||||
| Thread count | 1 |
|
||||
| Score unit | `ns/op`; language pages additionally derive `ms/op` and `ns/token` |
|
||||
| Speed warmup | 5 iterations, 1 s each, independently in every fork |
|
||||
| Speed measurement | 10 iterations, 1 s each, independently in every fork |
|
||||
| Speed forks | 3 independent JVM forks |
|
||||
| Speed threads | 1 |
|
||||
| Speed fork heap | Fixed `-Xms6g -Xmx6g` |
|
||||
| Reported uncertainty | JMH `Score Error (99.9%)` over 30 measured samples |
|
||||
| Observed relative uncertainty | Main speed matrix: maximum 11.945%, with 6 of 102 rows above 10%; coverage-speed curve: maximum 16.775%, with 3 of 10 rows above 10%; no published row exceeded 20% |
|
||||
| Deterministic measurements | Corpus, patch-command distribution, exact-root counters, coverage accuracy, and pairwise stemming quality are evaluated without interpreting runtime scores; no warmup is required |
|
||||
| JVM reported by JMH | JDK 25.0.3, OpenJDK 64-Bit Server VM, 25.0.3+9 |
|
||||
| Java runtime | OpenJDK Runtime Environment, Red Hat build 25.0.3+9 |
|
||||
| JVM invoker | `/usr/lib/jvm/java-25-openjdk/bin/java` |
|
||||
| Operating system | Fedora Linux 44 (MATE-Compiz) |
|
||||
| Kernel | Linux 7.0.12-201.fc44.x86_64 |
|
||||
| Kernel | Linux 7.1.4-200.fc44.x86_64 |
|
||||
| Architecture | x86_64 |
|
||||
| CPU | AMD Ryzen 5 8600G w/ Radeon 760M Graphics |
|
||||
| Physical cores | 6 |
|
||||
| Logical CPUs | 12 |
|
||||
| Physical / logical CPUs | 6 / 12 |
|
||||
| CPU frequency policy | `amd-pstate-epp`; governor `performance` on every logical CPU; EPP `performance`; boost enabled |
|
||||
| CPU affinity | Scheduler default; no explicit pinning |
|
||||
| Installed memory | 60 GiB reported by the operating system |
|
||||
| Pre-run idle state | Load average 0.25 / 0.36 / 0.71 after the 120 s idle interval; CPU Tctl 40.2 degrees Celsius; swap unused |
|
||||
| End-of-run state | Load average 1.16 / 1.28 / 1.32; CPU Tctl 60.5 degrees Celsius |
|
||||
| Power and idle policy | Developer workstation on stable power; screensaver, suspend, and hibernation disabled |
|
||||
| Concurrent project work | None during the published speed and coverage-speed run |
|
||||
|
||||
The workstation is not a hard real-time system. Normal kernel and desktop background activity was not removed, so the three independent forks and the published 99.9% error interval remain essential parts of result interpretation. Initial/final load and temperature sensor readings are stored in the environment report.
|
||||
|
||||
## Contracted Trie Baseline
|
||||
|
||||
All Radixor rows in the refreshed benchmark tables use contracted compiled patch tries. During compilation, a subtree whose reachable entries all resolve to the same preferred patch command is represented as an accepting leaf. Runtime lookup can therefore stop as soon as that leaf is reached, which reduces depth in uniform regions while preserving the preferred result used by `get()`.
|
||||
All Radixor rows use contracted compiled patch tries. During compilation, a subtree whose reachable entries all resolve to the same preferred patch command is represented as an accepting leaf. Runtime lookup can therefore stop as soon as that leaf is reached while preserving the preferred result used by `get()`.
|
||||
|
||||
## Model And Source Identity
|
||||
|
||||
`benchmark-corpora.csv` records the model ID, independent artifact version, and descriptor SHA-256 for every language. Every stemming-quality CSV row repeats the same three fields. The performance environment report additionally records checksums of the executable JMH JAR, runtime classpath manifest, corpus report, quality report, measured source patch, and untracked-source manifest.
|
||||
|
||||
The JMH runtime classpath contains the optional model artifact because it is a separately testable project dependency. It is not selected by any published benchmark. The selected-method manifest rejects `PolishPolimorf`, and the corpus/quality publication validators reject any non-default Polish model.
|
||||
|
||||
## Report Files
|
||||
|
||||
Generated local report files for this benchmark update:
|
||||
|
||||
- `build/reports/jmh/stemmer-comparison-2026-07-06.csv`
|
||||
- `build/reports/jmh/stemmer-comparison-2026-07-06.txt`
|
||||
- `build/reports/jmh/english-coverage-2026-07-06.csv`
|
||||
- `build/reports/jmh/english-coverage-2026-07-06.txt`
|
||||
- `build/reports/jmh/benchmark-corpora.csv`
|
||||
- `build/reports/jmh/stemmer-accuracy-2026-07-23.csv`
|
||||
- `build/reports/jmh/stemmer-accuracy-2026-07-23.txt`
|
||||
- `build/reports/jmh/stemmer-speed-2026-07-23.csv`
|
||||
- `build/reports/jmh/stemmer-speed-2026-07-23.txt`
|
||||
- `build/reports/jmh/english-coverage-accuracy-2026-07-23.csv`
|
||||
- `build/reports/jmh/english-coverage-accuracy-2026-07-23.txt`
|
||||
- `build/reports/jmh/english-coverage-speed-2026-07-23.csv`
|
||||
- `build/reports/jmh/english-coverage-speed-2026-07-23.txt`
|
||||
- `build/reports/jmh/performance-environment-2026-07-23.txt`
|
||||
- `build/reports/stemming-quality/stemming-quality.csv`
|
||||
- `build/reports/stemming-quality/stemming-quality.md`
|
||||
- `build/reports/stemming-quality/metric-correlations-pearson.csv`
|
||||
- `build/reports/stemming-quality/metric-correlations-spearman.csv`
|
||||
|
||||
JMH TXT and CSV reports are still published as benchmark artifacts. They are not converted into a Porter speed badge.
|
||||
The versioned documentation snapshot under `docs/benchmarks/data/` preserves the complete stemming-quality matrix. Machine-specific JMH reports remain build artifacts.
|
||||
|
||||
## Published Metrics
|
||||
|
||||
The historical English Radixor versus Porter performance badge is no longer generated. `tools/generate-pages-badges.py` now produces only coverage and mutation badge endpoint JSON files:
|
||||
|
||||
- `coverage-badge.json`
|
||||
- `pitest-badge.json`
|
||||
|
||||
The README therefore no longer presents a single Porter speed ratio. Benchmark interpretation now uses both speed and quality, because a narrow or aggressive stemmer can be fast while disagreeing with the dictionary root much more often than Radixor.
|
||||
The historical English Radixor versus Porter performance badge is retired. `tools/generate-pages-badges.py` produces only coverage and mutation badge endpoint JSON files. Benchmark interpretation uses both speed and quality because a narrow or aggressive stemmer can be fast while disagreeing with the dictionary root much more often than Radixor.
|
||||
|
||||
@@ -8,18 +8,24 @@ The authoritative Radixor language universe is the reconciliation of registered
|
||||
|
||||
Model identity is part of the candidate identity. Default Polish means `pl-pl-unimorph`; optional PoliMorf means `pl-pl-polimorf`. Results for those inputs must not be combined or relabeled, and historical snapshots cannot acquire a newer model identity retroactively.
|
||||
|
||||
Within one language and dictionary mode, every adapter receives the same original included forms. Exact duplicates are removed only within one dictionary row. Identical surface forms in different rows remain distinct entries. Candidate strings use exact `String.equals`, with no evaluation-only lowercasing, normalization, accent removal, or gold-label-aware selection. Adapter preprocessing and lifecycle match the JMH comparison path.
|
||||
Within one language and dictionary mode, every adapter receives the same original included forms. A distinct surface string is one evaluated item even when it occurs in several rows; those occurrences become multiple gold-group memberships. Candidate strings use exact `String.equals`, with no evaluation-only lowercasing, normalization, accent removal, or gold-label-aware selection. Adapter preprocessing and lifecycle match the JMH comparison path.
|
||||
|
||||
## Gold-standard pairs
|
||||
|
||||
Every usable dictionary row is a gold-standard equivalence group. An unordered pair from the same row is positive; a pair from different rows is negative. For group size `n`, `C2(n) = n * (n - 1) / 2`.
|
||||
Every usable dictionary row contributes one gold-standard group. The groups form an overlapping cover rather than an exclusive partition: one surface form may belong to several groups. For two distinct forms `u` and `v` with membership sets `G(u)` and `G(v)`:
|
||||
|
||||
- `TP = underPossiblePairs - underErrorPairs`: same-group pairs correctly related.
|
||||
- `FN = underErrorPairs`: same-group pairs incorrectly separated.
|
||||
- `FP = overErrorPairs`: different-group pairs incorrectly related.
|
||||
- `TN = overPossiblePairs - overErrorPairs`: different-group pairs correctly separated.
|
||||
```text
|
||||
goldRelated(u, v) = (G(u) intersection G(v) is not empty)
|
||||
```
|
||||
|
||||
Under-stemming is the false-negative relation among same-group pairs. Over-stemming is the false-positive relation among different-group pairs. Their percentages use different denominators and must not be added or averaged without an explicitly defined composite.
|
||||
A pair is counted once even if it shares several groups. Gold-negative pairs have disjoint membership sets. Thus:
|
||||
|
||||
- `TP = underPossiblePairs - underErrorPairs`: gold-related pairs correctly related.
|
||||
- `FN = underErrorPairs`: gold-related pairs incorrectly separated.
|
||||
- `FP = overErrorPairs`: gold-negative pairs incorrectly related.
|
||||
- `TN = overPossiblePairs - overErrorPairs`: gold-negative pairs correctly separated.
|
||||
|
||||
Under-stemming is Paice's Understemming Index (UI), the false-negative rate among gold-related pairs. Over-stemming is Paice's Overstemming Index (OI), the false-positive rate among gold-negative pairs. The original Paice formulation assumes disjoint lemma groups; this evaluator explicitly generalizes the pair relation to overlapping membership. Their percentages use different denominators and must not be added or averaged without an explicitly defined composite.
|
||||
|
||||
## Dictionary-processing modes
|
||||
|
||||
@@ -30,9 +36,9 @@ Under-stemming is the false-negative relation among same-group pairs. Over-stemm
|
||||
|
||||
`PRIMARY_OUTPUT` uses the adapter's deterministic primary stem. It defines a strict predicted partition and is the principal direct comparison between implementations.
|
||||
|
||||
`ANY_CANDIDATE` is an optimistic oracle-assisted pairwise upper bound. Same-group pairs succeed when candidate sets intersect. Different-group pairs avoid an error whenever a non-colliding candidate selection exists. Selection may differ between pairs, so this policy is not deterministic runtime behaviour and may not correspond to one globally realizable assignment.
|
||||
`ANY_CANDIDATE` is an optimistic oracle-assisted pairwise upper bound. Gold-related pairs succeed when candidate sets intersect. Gold-negative pairs avoid an error whenever a non-colliding candidate selection exists. Selection may differ between pairs, so this policy is not deterministic runtime behaviour and may not correspond to one globally realizable assignment. Because its positive and negative decisions use different oracle conditions, it does not define one confusion matrix; TP/FP/FN/TN and all confusion-derived scores are therefore `n/a`. Its separate under/over error counts and denominators remain defined.
|
||||
|
||||
`ALL_CANDIDATES` treats every returned candidate as active. Two forms are related when their candidate sets intersect. Alternatives can recover same-group relationships while introducing cross-group collisions. This overlapping relation need not be transitive or form a partition.
|
||||
`ALL_CANDIDATES` treats every returned candidate as active. Two forms are related when their candidate sets intersect. Alternatives can recover gold-positive relationships while introducing gold-negative collisions. This overlapping relation need not be transitive or form a partition.
|
||||
|
||||
Candidate-aware policies are reported as capability analyses. They are not mixed into the principal `PRIMARY_OUTPUT` ranking.
|
||||
|
||||
@@ -42,11 +48,11 @@ Undefined denominators produce `n/a`, never zero, `NaN`, or infinity. Metrics ar
|
||||
|
||||
| Metric | Formula | Range and interpretation | Sensitivity and applicability |
|
||||
| --- | --- | --- | --- |
|
||||
| Under-stemming rate | `FN / (TP + FN)` | `[0, 1]`; lower is better. False-negative rate over same-group pairs. | Sensitive to splitting large gold groups. All policies. |
|
||||
| Over-stemming rate | `FP / (TN + FP)` | `[0, 1]`; lower is better. False-positive rate over different-group pairs. | The denominator is usually very large. All policies. |
|
||||
| Precision | `TP / (TP + FP)` | `[0, 1]`; higher is better. Fraction of predicted relations that are gold-positive. | Penalizes over-stemming. All policies, with oracle-assisted interpretation for `ANY_CANDIDATE`. |
|
||||
| Recall | `TP / (TP + FN)` | `[0, 1]`; higher is better. Fraction of gold-positive pairs recovered. | Equivalent to one minus the under-stemming rate. All policies. |
|
||||
| Specificity | `TN / (TN + FP)` | `[0, 1]`; higher is better. Fraction of negative pairs separated. | Sensitive to cross-group collisions. All policies. |
|
||||
| Under-stemming rate | `FN / (TP + FN)` | `[0, 1]`; lower is better. False-negative rate over gold-related pairs. | Sensitive to splitting large gold groups. All policies. |
|
||||
| Over-stemming rate | `FP / (TN + FP)` | `[0, 1]`; lower is better. False-positive rate over gold-negative pairs. | The denominator is usually very large. All policies. |
|
||||
| Precision | `TP / (TP + FP)` | `[0, 1]`; higher is better. Fraction of predicted relations that are gold-positive. | Penalizes over-stemming. `PRIMARY_OUTPUT` and `ALL_CANDIDATES`. |
|
||||
| Recall | `TP / (TP + FN)` | `[0, 1]`; higher is better. Fraction of gold-positive pairs recovered. | Equivalent to one minus the under-stemming rate. `PRIMARY_OUTPUT` and `ALL_CANDIDATES`. |
|
||||
| Specificity | `TN / (TN + FP)` | `[0, 1]`; higher is better. Fraction of negative pairs separated. | Sensitive to false conflations. `PRIMARY_OUTPUT` and `ALL_CANDIDATES`. |
|
||||
| Balanced accuracy | `(recall + specificity) / 2` | `[0, 1]`; higher is better. Equal weight for positive and negative classes. | Primary navigation metric; less dominated by TN than ordinary accuracy, but not uniquely authoritative. |
|
||||
| Pairwise accuracy | `(TP + TN) / (TP + TN + FP + FN)` | `[0, 1]`; higher is better. | Can be dominated by the very large TN class and is not the default ranking metric. |
|
||||
| Pairwise error rate | `(FP + FN) / (TP + TN + FP + FN)` | `[0, 1]`; lower is better. | Also sensitive to the number of negative pairs. |
|
||||
@@ -59,17 +65,9 @@ Undefined denominators produce `n/a`, never zero, `NaN`, or infinity. Metrics ar
|
||||
|
||||
The general F-beta formula is `((1 + betaSquared) * TP) / (((1 + betaSquared) * TP) + (betaSquared * FN) + FP)`.
|
||||
|
||||
## Partition-only metrics
|
||||
## Inapplicable partition metrics
|
||||
|
||||
These metrics apply only to `PRIMARY_OUTPUT`. Candidate relations are not forced into artificial partitions.
|
||||
|
||||
- Adjusted Rand Index is the Rand agreement corrected for agreement expected from the gold/predicted contingency-table marginals. Its usual range is `[-1, 1]`, with `1` indicating identical partitions.
|
||||
- Homogeneity is `1 - H(gold | predicted) / H(gold)`, in `[0, 1]`; each predicted cluster ideally contains one gold group.
|
||||
- Completeness is `1 - H(predicted | gold) / H(predicted)`, in `[0, 1]`; each gold group ideally maps to one predicted cluster.
|
||||
- V-measure is the harmonic mean of homogeneity and completeness, in `[0, 1]`.
|
||||
- Normalized mutual information uses arithmetic-mean entropy normalization: `MI / ((H(gold) + H(predicted)) / 2)`, in `[0, 1]` under this implementation.
|
||||
|
||||
Entropy zero cases follow the evaluator's explicit perfect/undefined conventions. Language tables render inapplicable candidate-policy values as `n/a`.
|
||||
Standard Adjusted Rand Index, homogeneity, completeness, V-measure, and normalized mutual information are not calculated. Their ordinary contingency-table definitions require every item to have one exclusive gold label. Assigning an arbitrary single label or duplicating a multi-membership form would change the scientific question and reintroduce the counting defect this methodology avoids. A future overlapping-clustering index would require a separately specified random model and interpretation; it must not be labelled as ordinary ARI or NMI.
|
||||
|
||||
## Aggregation and ranking
|
||||
|
||||
@@ -81,4 +79,4 @@ Multiple metrics and Pearson/Spearman correlation datasets are published because
|
||||
|
||||
## Limitations
|
||||
|
||||
Dictionary groups encode the available annotation, not every linguistic distinction. Homographs may occur in different groups, singleton rows contribute no under-stemming pair, and group size affects pair counts. `ANY_CANDIDATE` is optimistic; `ALL_CANDIDATES` measures an overlapping graph; neither is a deterministic global assignment. Results characterize the tested versions, adapters, dictionaries, and preprocessing, not every deployment or domain.
|
||||
Dictionary groups encode the available annotation, not every linguistic distinction. Homographs and polyfunctional forms may have several memberships, singleton rows contribute no relation by themselves, and group size affects pair counts. `ANY_CANDIDATE` is optimistic; `ALL_CANDIDATES` measures an overlapping graph; neither is a deterministic global assignment. Results characterize the tested versions, adapters, dictionaries, and preprocessing, not every deployment or domain.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The stemmer comparison suite measures Radixor and Java stemmers on the same language and deterministic Radixor model dictionary-derived data. Published Radixor rows in this refresh use contracted compiled patch tries, where uniform preferred-command subtrees are collapsed into accepting leaves before the trie is frozen for lookup. For each language, the registered default model resource stores the expected root as the first tab-separated field on a line and its surface forms on the same line. Every single-token field on that line can therefore be paired with the same expected root.
|
||||
|
||||
Published stemmer comparison results must come only from benchmark classes matching `.*StemmerComparisonBenchmark.*`. Internal `FrequencyTrie*` microbenchmarks are not part of those results.
|
||||
Published speed results come only from the exact method selection retained in `published-speed-benchmarks-2026-07-23.txt`. Internal `FrequencyTrie*` microbenchmarks, quality methods, the CISTEM gold-standard experiment, and the optional `PolishPolimorfStemmerComparisonBenchmark` are not part of those results.
|
||||
|
||||
## Benchmark Passes
|
||||
|
||||
@@ -13,6 +13,8 @@ There are two distinct benchmark passes:
|
||||
|
||||
Timing corpora are generated once per JMH JVM and kept in memory as shared `{token, expectedRoot}` arrays. Corpus construction, dictionary loading, trie loading, table loading, and analyzer construction are setup work and are not included in measured benchmark methods.
|
||||
|
||||
The deterministic and timed workloads are executed separately. Corpus statistics, patch-command counts, exact-root counters, coverage accuracy, and pairwise quality do not use or interpret warmup or runtime scores. Published speed and coverage-speed methods use three independent forks, five one-second warmup iterations and ten one-second measurement iterations per fork, one benchmark thread, and a fixed 6 GiB heap.
|
||||
|
||||
Performance is interpreted as average time per input token:
|
||||
|
||||
```text
|
||||
@@ -37,10 +39,10 @@ For right-to-left Radixor languages, patch application uses the traversal direct
|
||||
|
||||
## Quality Metric
|
||||
|
||||
The quality pass reports exact-root agreement against the expected root from the Radixor dictionary line. It writes to the normal JMH report files:
|
||||
The quality pass reports exact-root agreement against the expected root from the default-model dictionary line. External-stemmer counters are written to:
|
||||
|
||||
- `build/reports/jmh/jmh-results.csv`
|
||||
- `build/reports/jmh/jmh-results.txt`
|
||||
- `build/reports/jmh/stemmer-accuracy-2026-07-23.csv`
|
||||
- `build/reports/jmh/stemmer-accuracy-2026-07-23.txt`
|
||||
|
||||
Accuracy is computed from standard JMH secondary rows:
|
||||
|
||||
@@ -54,7 +56,7 @@ rootPreservedPercent = rootPreservedMatches / rootEvaluatedTokens * 100
|
||||
|
||||
Morfologik can emit multiple terms for one input token. The quality benchmark uses the first emitted term for exact-root accounting when no ranking weight is exposed. Throughput benchmarks for Morfologik TokenFilter paths consume all emitted terms.
|
||||
|
||||
Quality reports use JMH auxiliary counter rows. Exact-root accounting is deterministic for a fixed corpus and stemmer, so repeated measurement samples duplicate the same counters; documentation uses the counter ratios and does not interpret quality benchmark timing scores.
|
||||
External-stemmer quality reports use JMH auxiliary counter rows from one deterministic evaluation. Radixor exact-root counts are computed directly while the default-model corpus and preferred patch commands are audited, so all 20 default models have the same coverage even where no older JMH quality adapter existed. Documentation uses counter ratios and does not interpret quality benchmark timing scores.
|
||||
|
||||
Pairwise over-stemming, under-stemming, candidate-aware policies, balanced accuracy, and partition comparison are a separate analytical evaluation. See [Linguistic Quality Methodology](linguistic-quality.md); exact-root accuracy must not be interpreted as the complement of pairwise under-stemming.
|
||||
Pairwise over-stemming, under-stemming, candidate-aware policies, and relation metrics are a separate analytical evaluation. See [Linguistic Quality Methodology](linguistic-quality.md); exact-root accuracy must not be interpreted as the complement of pairwise under-stemming.
|
||||
Default rows use `Language.defaultModelId()`. Optional variants require a separate model field; `pl-pl-unimorph` and `pl-pl-polimorf` must never share an ambiguous Polish label. The benchmark runtime receives each resource exactly once from its individual model JAR through direct JMH runtime dependencies. See [Model Selection and Loading](../../model-selection-and-loading.md).
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
|
||||
- Machine-readable CSV: [stemming-quality.csv](../data/stemming-quality.csv)
|
||||
- SHA-256 record: [stemming-quality.sha256](../data/stemming-quality.sha256)
|
||||
- SHA-256: `5a93a6ab60e46489737cd649eb1ac48182114b9038f7f20195ab9d1c1fc0dd28`
|
||||
- SHA-256: `edf16b07be8a535943ddf37caeb8807755c95e9e1fb13244145f28be74b491d8`
|
||||
- Complete scenarios: 308
|
||||
- Authoritative language universe: 20 languages
|
||||
- Language-page scenarios: 302 across 19 existing benchmark pages
|
||||
- Language-page scenarios: 308 across 20 benchmark pages
|
||||
|
||||
The six remaining scenarios are the three Radixor policies in two modes for `HE_IL`. Hebrew is present in the complete result snapshot but has no existing language benchmark page.
|
||||
|
||||
The CSV contains raw TP, FP, FN, and TN counts; raw over/under numerators and denominators; candidate statistics; relation metrics; and partition-only metrics. Documentation is regenerated from this file rather than manually transcribed.
|
||||
The CSV contains the model ID, independent model version, descriptor SHA-256, raw pair counts, raw over/under numerators and denominators, candidate statistics, and relation metrics. Reserved partition-metric columns remain empty because the gold standard is an overlapping cover. Documentation is regenerated from this file rather than manually transcribed. Publication fails when any row uses a model other than the language's registered default.
|
||||
|
||||
## Commands
|
||||
|
||||
@@ -24,6 +22,10 @@ The CSV contains raw TP, FP, FN, and TN counts; raw over/under numerators and de
|
||||
mkdocs build --strict --config-file build/mkdocs/mkdocs.yml
|
||||
```
|
||||
|
||||
For an immediate local preview, `mkdocs serve` works directly from the repository root. The checked-in
|
||||
model catalog makes that source tree complete. After changing model metadata or model bytes, refresh it
|
||||
with `./gradlew publishModelCatalogDocumentation`; verification rejects a stale checked-in catalog.
|
||||
|
||||
`stemmingQuality` performs the expensive complete evaluation and is intentionally not attached to `test` or `check`. It prepares JMH third-party dependencies automatically and writes:
|
||||
|
||||
- `build/reports/stemming-quality/stemming-quality.csv`
|
||||
@@ -57,19 +59,20 @@ The Pages workflow publishes that staged documentation together with Javadoc, JU
|
||||
|
||||
## Performance benchmark reproduction
|
||||
|
||||
The JMH comparison command family is:
|
||||
The current speed and coverage-speed command is:
|
||||
|
||||
```bash
|
||||
./gradlew jmh -Pjmh.includes='.*StemmerComparisonBenchmark.*' --no-daemon
|
||||
./gradlew writeJmhRuntimeClasspath --no-daemon
|
||||
tools/run-published-speed-benchmarks.sh 2026-07-23
|
||||
```
|
||||
|
||||
The exact JMH configuration, hardware, operating system, and JDK captured for the published performance tables are listed in [Environment and reports](environment.md). Quality and performance reports are separate datasets and are not combined into an undocumented scalar.
|
||||
The runner refuses to start unless every CPU uses the `performance` governor, materializes the exact selected benchmark list, rejects quality/Polimorf/gold-standard methods, and requires the Hebrew speed path. It records hardware, JVM, source-state, JAR, classpath, corpus, quality, load, temperature, and governor provenance before running. The exact JMH configuration is listed in [Environment and reports](environment.md). Quality and performance reports are separate datasets and are not combined into an undocumented scalar.
|
||||
|
||||
## Recorded and unavailable provenance
|
||||
|
||||
The performance documentation records its 2026-07-06 environment, JDK 25.0.3, operating system, and hardware. The quality CSV records the evaluated identifiers and counts but does not embed the Radixor Git revision, generation date, JDK, operating system, model ID, dictionary content hash, or immutable upstream revisions for every downloaded source. These fields are explicitly unavailable for this historical snapshot and are not reconstructed from filesystem timestamps. In particular, the snapshot predates the optional PoliMorf integration and must not be relabeled as `pl-pl-polimorf`.
|
||||
The performance documentation records its 2026-07-23 environment, JDK 25.0.3, operating system, hardware, base revision, exact dirty patch, untracked-source checksums, executable JMH JAR checksum, and model descriptor checksums. The quality CSV embeds model identity and checksum in every row; run date, core source state, JVM, OS, and hardware are shared provenance on the environment page.
|
||||
|
||||
Dependency versions that are reproducible from repository configuration include Apache Lucene 10.5.0, Morfologik 2.1.9, the Ukrainian dictionary artifact 4.9.1, and JMH 1.37. Other upstream branches or downloaded dictionary revisions should be pinned and embedded in a future result schema.
|
||||
Exact immutable upstream revisions were not recorded for every legacy UniMorph import. That limitation remains explicit in model descriptors and cannot be repaired from filesystem timestamps. Dependency versions reproducible from repository configuration include Apache Lucene 10.5.0, Morfologik 2.1.9, the Ukrainian dictionary artifact 4.9.1, and JMH 1.37.
|
||||
|
||||
## Correlation and audit data
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ The JMH adapter registry is authoritative for evaluated implementations and lang
|
||||
|
||||
| Family or implementation | Upstream / attribution | Tested version or revision | Evaluated scope | Output capability and adapter behaviour | Interpretation notes |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Radixor | Egothor / Radixor project | Current repository revision; exact revision was not embedded in the quality CSV | All 20 reconciled default model languages; 19 have benchmark pages | Deterministic preferred patch via `get`; ranked distinct alternatives via `getAll`; primary is always included | Model-dictionary-derived compiled patch trie. Default rows use each language's stable default model ID. |
|
||||
| Radixor | Egothor / Radixor project | Base commit and measured working-tree state recorded on the environment page | All 20 reconciled default model languages; all 20 have benchmark pages | Deterministic preferred patch via `get`; ranked distinct alternatives via `getAll`; primary is always included | Model-dictionary-derived compiled patch trie. Default rows use each language's stable default model ID. |
|
||||
| Apache Lucene language stem filters | Apache Lucene project | 10.5.0 | Adapter-declared language-specific subsets | TokenFilter lifecycle and language normalization match JMH; normally single-output | Light, minimal, possessive, and language stem filters deliberately implement different scopes. Narrow scope is not a defect. |
|
||||
| Apache Lucene SnowballFilter | Apache Lucene project using Snowball algorithms | Lucene 10.5.0 | Snowball-supported subset of Radixor languages | Single primary token emitted through the Lucene TokenFilter path | Includes TokenStream overhead and required normalization. |
|
||||
| Official Snowball Java | Snowball project | Repository preparation downloads the configured upstream Java distribution; an immutable revision was not recorded in the quality CSV | Same-language adapter subset | Direct generated Java API; single output | Rule-based suffix algorithms provide broad baselines rather than dictionary-root guarantees. |
|
||||
|
||||
Reference in New Issue
Block a user