尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Sound-correspondence rule discovery from cognate data
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Pairwise and multi-lect phonological alignment for the new historical linguistics framework.
The implementation is a C99 core library with a stable C ABI,
built on the merkmal feature library. Python
bindings are provided under python/.
Given user-supplied cognate sets — pairs of forms in the
two-lect case, N-way tuples of forms in the multi-lect case —
regulae produces a structured model of the systematic
phonological correspondences between the lects involved.
Nothing more. It does not infer cognates, propose proto-forms,
build phylogenies, or assign dates to changes: those are jobs
for a downstream historical-inference package that consumes
this one's output.
What you get back from a training run is a multi-lect model carrying:
phoible,
pbase-*) both work. A fixed list fits the language its
author had in mind and silently loses the rest.--permutations — whether that clears what the same search
reaches on the corpus shuffled. --tune-search turns the
comparison into a gate. The default categorical charge prices
all K−1 parameters added when a K-outcome distribution is
split. On unrelated wordlists from one inventory
(testdata/restraint/chance.tsv) that correction leaves no
selected conditioned correspondence; the shuffled runs still
measure how high an unpriced search can reach.The field's tool for cross-lect correspondence is lingrex's CoPaR (correspondence-pattern recognition): it clusters the columns of aligned cognate sets into correspondence patterns and imputes a missing reflex from them. regulae overlaps with it — both take expert cognate sets, both align across lects, and both can predict a held-out reflex — and stating the overlap is the point of this note. What regulae adds is the part a historical linguist reads a correspondence for: the environment each one is conditioned on, the decision list that orders the conditioned rules, and a standing verdict for each against a per-pivot null. A pattern table says which sounds correspond; regulae says where, in what order the conditions were found, and whether the conditioning survives its own shuffle.
The multi-lect half of that scores a conditioned split per sister lect, so a
conditioned correspondence is recovered at every arity
(testdata/restraint/arity_2 … arity_5) rather than decaying as lects are
added and the outcome charge grows with the number of languages. The multi-lect
class table is a first-class output rather than a lossy lift of the pairs.
scripts/benchmark_cloze.py runs a leave-one-reflex-out cloze — hold out one
lect's reflex in a cognate set, predict it from the rest — for both tools on
shared Lexibank families. It is a reproducible harness, not a leaderboard: the
two predict different units (regulae scores a held-out reflex position by
position, CoPaR imputes an alignment site) and an exact whole-reflex match is a
harsh metric, so the numbers indicate rather than rank. On a Tukanoan-area
Arawakan sample regulae recovers about two-thirds of held-out reflex positions;
run the script for the CoPaR side and other families. The comparison is there
for the boundary above — regulae is not a better pattern recogniser, it is a
correspondence model that also states where each correspondence holds and
whether it stands.
regulae is built on merkmal for phonological
feature lookups and segment distances, used through its public
C API only. CMake finds an installed merkmal::merkmal, or
falls back to a sibling checkout at ../merkmal
(override with -DREGULAE_MERKMAL_SOURCE_DIR=...).
cmake -S . -B build/c && cmake --build build/c
ctest --test-dir build/c --output-on-failure
Sanitizer builds use -DREGULAE_ENABLE_SANITIZER=address (or
undefined).
Three corpus suites sit behind ctest, and they ask three different
questions. Each has a README explaining how to add to it.
| suite | question | test |
|---|---|---|
testdata/soundlaws/ | can regulae find a change the field settled long ago? | sound_laws |
testdata/restraint/ | can it decline to find one that is not there? | restraint |
testdata/diagnostics/ | can it tell you your corpus is wrong? | diagnostics |
A tool that passes the first and fails the second is worse than useless,
because a spurious conditioned rule is formatted exactly like a real one.
restraint/ carries two lects with no history between them, a change spread
over the lexicon rather than over an environment, a borrowed stratum, and a
ladder that measures the evidence floor — about eight examples of a change and
eight counterexamples, below which the search stays quiet rather than guessing.
That figure is for a trigger in the immediate neighbour; a trigger a syllable
away needs five examples a side, not three, and the distant_* ladder pins it.
regulae train <corpus.tsv> # machine-readable model summary
regulae train --human <corpus.tsv> # readable report
regulae train --permutations 20 <corpus.tsv> # calibrate against shuffled data
regulae train --predictive-folds 5 <corpus.tsv> # group-held-out reflex evidence
regulae train --pairwise <corpus.tsv> # per-pair learned tables
regulae align <corpus.tsv> # alignments, prior-only
regulae align --model <corpus.tsv> # alignments under the trained model
regulae outliers --top-k 10 <corpus.tsv>
--format tsv|wide|gled|arcaverborum selects the input format
(default tsv). The generic TSV reader expects cognate_id,
lect_id and segments columns, with optional confidence,
breaks (morpheme boundaries), syllables, etymon_group and
source_group; a cognate set
takes the lowest confidence any of its rows reports.
etymon_group names paradigm cells or records that repeat one lexical
history; source_group names a publication or transcription batch. regulae
never infers either from identifiers or forms. The bootstrap uses etymon groups
automatically when they are supplied and otherwise reports that cognate sets
are being treated as independent. Source-level resampling is explicit because
collapsing a whole publication to one draw is usually too coarse.
Tone, stress and length can each be given per segment — tone,
stress and length columns in the long format, <lect>_tone,
<lect>_stress and <lect>_length in the wide. Length written
into the grapheme as aː is a different claim: merkmal reads
that as its own segment with the long feature, which is right
where length is contrastive. The column says the vowel has a
length, which is what a lengthening changes.
A lect may appear twice in one cognate set. That is a doublet — two reflexes of one etymon — and it is 3.2% of cognate-set members across the Lexibank datasets with expert judgements. The corpus reads it as one set per combination of reflexes, each carrying its share of the confidence, so both reflexes are counted and neither is a second vote.
The wide reader takes the shape linguistic data is usually
written in — one row per cognate, one column per lect, holding
whole unsegmented words:
gloss latin spanish latin_breaks spanish_breaks
father pater padre - -
woman femina ember - 3
Words are segmented through merkmal, so multi-codepoint
graphemes survive: pʰ, t͡ʃ, kʷ and a base plus combining
diacritic each stay one segment, where splitting on characters
would break them. The tie bar is what marks an affricate as one
segment: untied tʃ is read as two, which is what the
transcription says. A <lect>_breaks column supplies morpheme
boundary indices.
Tone written on the word is carried through as the segment's own
dimension rather than as part of the grapheme: ma³³ gives m
and a bearing ³³, and a tone spelled as a separate token —
how CLDF wordlists publish it — attaches to the segment before
it. This reads Chao's superscript digits, which is what the
field writes; an ASCII 1 is not tone notation and is still
reported as an unknown grapheme. A <lect>_tone column can
annotate tone per segment instead, and overrides what the word
carries.
Stress is carried the same way. The IPA marks in a word are lifted
onto the syllable nucleus — ˈpater accents the first syllable,
paˈter the second — and a <lect>_stress column annotates it per
segment for corpora that record it separately. Leaving the mark in
the grapheme would make a stressed segment a different segment from
its unstressed self, which splits every correspondence it takes part
in and puts stress out of reach of the conditioning it drives.
Each experiment under experiments/ is a curated corpus and a
findings.md interpreting what training it produced. Run one
through the CLI:
regulae train --human --format wide experiments/latin_spanish/cognates.tsv
regulae train --human --format wide experiments/oe_english/cognates.tsv
regulae train --human --format wide experiments/ppn_hawaiian/cognates.tsv
The gled_* and arcaverborum_polynesian experiments read
external corpora and need the data file supplied.
#include "regulae.h"
rg_context *ctx = NULL;
rg_corpus *corpus = NULL;
rg_multi_model *model = NULL;
rg_train_options options;
rg_context_new_builtin(&ctx); /* owns the merkmal bridge */
/* The last argument is where a failed load says what went wrong and on which
* line. Pass NULL to decline it. */
rg_load_diagnosis why;
if (rg_corpus_load_tsv("cognates.tsv", NULL, &corpus, &why) != RG_OK) {
fprintf(stderr, "line %lu: %s\n", (unsigned long)why.line, why.message);
return 1;
}
rg_train_options_init_defaults(&options);
/* Multi-lect training: the canonical entry point. */
rg_train_model(ctx, rg_corpus_cognates(corpus),
rg_corpus_cognate_count(corpus), &options, &model);
/* Read the reconciled classes. A table is handed out whole, and the rows are
* borrowed from the model. */
size_t count = 0;
const rg_multi_class_row *classes = rg_multi_model_unconditioned_classes(model, &count);
for (size_t i = 0; i < count; i++) {
for (size_t j = 0; j < classes[i].segment_count; j++) {
printf("%s%s:%s", j ? " ~ " : "",
classes[i].lect_ids[j], classes[i].graphemes[j]);
}
printf(" count=%.0f\n", classes[i].count);
}
char *text = rg_format_multi_model(model, NULL); /* caller-owned */
puts(text);
rg_string_free(text);
rg_multi_model_free(model);
rg_corpus_free(corpus);
rg_context_free(ctx);
Every call that can fail returns rg_status; output pointers
become caller-owned only on RG_OK. Accessors return borrowed
pointers valid while the owning handle lives. Loaders are
provided for generic TSV, GLED and arcaverborum data
(rg_corpus_load_tsv, rg_corpus_load_gled,
rg_corpus_load_arcaverborum), and rg_corpus_from_pairs
lifts a directed pairwise corpus into cognate sets.
include/regulae.h is the API contract; RG_ABI_VERSION
moves on any layout, signature or ownership change. It is at
46. docs/consumer_guide.md §7 has the full list of rules;
a few of the design choices it reflects:
evidence member on the row
rather than four separate fields.rg_corpus_fit reports the per-pair verdict separately from the
multi-lect one, because the two are counted differently.predictive_folds or pass --predictive-folds <n> to select environments
inside dependency-group training folds and score unseen reflexes. It never
changes an in-sample split score into a predictive claim.CONTEXT.md — the terms regulae uses
and the claims it is careful to keep apart, so a surface relationship model
is never read as a historical one.docs/GUIDE.md — what the output means and
how to read a conditioned environment. Also the source for the in-page
walkthrough.docs/NOTATION.md — every published
environment is printed twice, once exactly and once as A ~ B / lect: X _ Y.
The core of that notation is the field's; the rest regulae defines, because
past _ / # ∅ {} [±F] σ … nothing is agreed, and a correspondence whose
environment belongs to a named lect has no notation at all.docs/capabilities.md —
generated: which capabilities are implemented, and which corpora the loaders
can currently express. A corpus it cannot read says something about the input
path, not the engine.docs/family_survey.md — a recorded run over forty-odd
published wordlists, one or two per family, from Indo-European to Pama-Nyungan.
Regenerate with scripts/families.py against your own Lexibank clone; nothing
is vendored, so it is not checked by CI.docs/training_pipeline.md,
docs/correspondence_discovery.md, and
docs/alignment_details.md — rationale for the staged
training pipeline, discovery mechanisms, and data types.docs/consumer_guide.md —
the external contract: what each published field means, what it does not
mean, how to read a table, and the ABI stability rules. Start here if you
are building on regulae rather than changing it.MIT, matching merkmal, which regulae is built on. See
LICENSE.
The vendored copy of cJSON in third_party/cjson/ is also MIT and keeps its
own licence file; it is regulae's only dependency other than merkmal.
rg_find_cognate_outliers.rg_multi_model_fit: the mean alignment cost per segment,
and — with --permutations <n> — the same statistic over trainings
on a corpus whose pairings have been shuffled. This measures
whether the supplied pairings contain more cross-lect structure
than their shuffled alternative; it is not a genealogical
verdict. Measured with twelve shuffles on two wordlists drawn
from one inventory with no history between them: 79
unconditioned and zero conditioned classes, against a shuffled
80 and 5.8, and z = -0.1. A corpus with a regular surface
relationship is tens of standard deviations from its shuffles.docs/static_analysis.md — the clang-tidy baseline
and why each suppression exists.python/ — a thin wrapper that trains
through the C core and parses the JSON model it renders; see that directory's
README to build and use them.