/** * @copyright Sister Software * @license AGPL-3.0 * @author Teffen Ellis, et al. * * `no-street-led` — the Norwegian street-led slice, third orthography of the #901 * leading-name-boundary family. The post-#920 NO row-read measured the residual as * ORDER-SENSITIVE decode: street-led forms ("Tangavegen 40, 5620 Tørvikbygd") carry a 30% * residual vs pc-first's 7% — the diacritic street head (…vegen/…veien with ø/å/æ) is the same * leading-name-before-comma boundary the FR bare-street and SI village forms exercise, in a * third orthography. All three real orders cycle per tuple (balanced polarity — the v1.9.9 * lesson: a one-order gradient loses at convergence): * * 1. canonical "«st» «n», «pc» «city»" (the 30% residual class — the lesson) * 2. city-first "«city», «pc», «st» «n»" * 3. pc-first "«pc» «city», «st» «n»" (the 7% floor — the anchor the others converge to) */ import { mulberry32 as makeMulberry32 } from "@mailwoman/core/utils" import { TextSpliterator } from "spliterator" import { alignAndWrite, foldNOSurface, readTuples, type CorpusRecipe, sliceSourceID } from "#recipes/scaffold" /** * Slice recipe registered with the corpus builder — see the file header for the parse behaviour it exists to exercise, * and `description` below for the surface form it generates. */ export const noStreetLedRecipe: CorpusRecipe = { name: "no-street-led", description: "NO street-led boundary form (#901 family): '«st» «n», «pc» «city»' — diacritic street heads", mode: "tuples", options: [ { flag: "--exclude-surfaces ", description: "REQUIRED. The NO digit board's reserved surface list " + "(mailwoman/eval-harness/fixtures/no-digits.surfaces.txt); every listed street is skipped.", }, ], async run(opts, write) { makeMulberry32(opts.seed) // THE SPLIT (ported from fr-fragment, #727 T2). Without it this slice trains on all 10,697 // NO surfaces, 1,952 of which the digit board reserves — so a Norway retrain would grade // memorization of `Hallingrudveien` while claiming to measure the boundary form. There is no // safe default: source-disjoint by street SURFACE is the discipline, so the flag throws. const excludePath = opts.excludeSurfaces if (!excludePath) { throw new Error( "no-street-led: --exclude-surfaces is REQUIRED. Pass the NO digit board's reserved list " + "(mailwoman/eval-harness/fixtures/no-digits.surfaces.txt) or this slice trains on its own eval set. " + "Source-disjoint by street SURFACE is the split discipline; there is no safe default." ) } const excluded = new Set() for await (const line of TextSpliterator.fromAsync(excludePath)) { const trimmed = line.trim() if (trimmed && !trimmed.startsWith("#")) { excluded.add(trimmed) } } if (!excluded.size) throw new Error(`no-street-led: --exclude-surfaces "${excludePath}" listed no surfaces`) let read = 0 let emitted = 0 let skipped = 0 let contaminated = 0 for await (const t of readTuples(opts.input!)) { read++ const street = String(t.street ?? "").trim() const city = String(t.locality ?? "").trim() const number = String(t.number ?? "").trim() const postcode = String(t.postcode ?? "").trim() if (!street || !city || !number || !postcode) { skipped++ continue } // A surface on the digit board never enters training. if (excluded.has(foldNOSurface(street))) { contaminated++ continue } const order = read % 3 let raw: string const components: Record = { street, house_number: number, postcode, locality: city, } if (order === 0) { raw = `${street} ${number}, ${postcode} ${city}` } else if (order === 1) { raw = `${city}, ${postcode}, ${street} ${number}` } else { raw = `${postcode} ${city}, ${street} ${number}` } const source_id = sliceSourceID("synth-no-street-led", { ...components, o: String(order), v: String(read) }) const canonical = { raw, components, country: "NO", locale: "nb-NO", source: "synth-no-street-led", source_id, corpus_version: "0.10.0", license: "Synthetic — no-street-led; (street, number, postcode, city) from OpenAddresses NO (per-source attribution in the model card)", } if (alignAndWrite(write, canonical, "no-street-led")) { emitted++ } else { skipped++ } } return { read, emitted, skipped, contaminated } }, }