OverviewFilesCommitsIssuesDocsReleasesPeers

atomspace-layer-discovery commits / fc40b6b

experiments/level2: re-parse loop (l2 --reparse): exchange over parse links, MST over classes, 5 rounds
Smoke seed 1, 3K: Test A + A-U output unchanged (R0 PASS). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fc40b6b806f8fd18c0c924c2cb3254894f44a359
by plankatron <fleeky@gmail.com> · 2026-10-18T23:28:12.000Z

Changed files (3) +165 -2

experiments/level2/l2.c +111 -2
……@@ -216,6 +216,75 @@ static void link_prf (const corpus_t *C, const parse_t *P, double *pr, double *r
216216 *f1 = *pr + *rc > 0 ? 2 * *pr * *rc / (*pr + *rc) : 0;
217217}
218218
219// ---------------------------------------------------------------- re-parse loop (PREREG-reparse.md)
220
221#define KR 30
222#define ROUNDS 5
223
224// Exchange algorithm over weighted ordered word pairs Wp (left a, right b); copied from
225// ../jigsaw-transformer/jt.c exchange_weighted (K = KR).
226static int *exchange_pairs (const double *Wp, const corpus_t *tr) {
227 double *freq = calloc(V, sizeof(double));
228 for (int t = 0; t < tr->ntok; t++) freq[tr->w[t]] += 1;
229 int *order = malloc(sizeof(int) * V);
230 for (int x = 0; x < V; x++) order[x] = x;
231 for (int i = 1; i < V; i++) { int x = order[i], j = i; while (j > 0 && freq[order[j - 1]] < freq[x]) { order[j] = order[j - 1]; j--; } order[j] = x; }
232 int *cls = malloc(sizeof(int) * V);
233 for (int i = 0; i < V; i++) cls[order[i]] = i % KR;
234 double *Nc = calloc((size_t) KR * KR, sizeof(double));
235 for (int a = 0; a < V; a++) for (int b = 0; b < V; b++) { double v = Wp[(size_t) a * V + b]; if (v > 0) Nc[cls[a] * KR + cls[b]] += v; }
236 double sv[64], pv[64];
237 for (int pass = 0; pass < 30; pass++) {
238 int moves = 0;
239 for (int i = 0; i < V; i++) {
240 int x = order[i];
241 if (freq[x] == 0) continue;
242 int a = cls[x];
243 double self = Wp[(size_t) x * V + x];
244 memset(sv, 0, sizeof sv); memset(pv, 0, sizeof pv);
245 for (int y = 0; y < V; y++) {
246 if (y == x) continue;
247 double r = Wp[(size_t) x * V + y], l = Wp[(size_t) y * V + x];
248 if (r > 0) sv[cls[y]] += r;
249 if (l > 0) pv[cls[y]] += l;
250 }
251 for (int c = 0; c < KR; c++) { Nc[a * KR + c] -= sv[c]; Nc[c * KR + a] -= pv[c]; }
252 Nc[a * KR + a] -= self;
253 int best = a; double bestF = -1e300;
254 for (int b = 0; b < KR; b++) {
255 for (int c = 0; c < KR; c++) { Nc[b * KR + c] += sv[c]; Nc[c * KR + b] += pv[c]; }
256 Nc[b * KR + b] += self;
257 double f = 0;
258 for (int r = 0; r < KR; r++) {
259 double row = 0, col = 0;
260 for (int c = 0; c < KR; c++) { double v = Nc[r * KR + c]; if (v > 0) f += v * log(v); row += Nc[r * KR + c]; col += Nc[c * KR + r]; }
261 if (row > 0) f -= row * log(row);
262 if (col > 0) f -= col * log(col);
263 }
264 if (f > bestF + 1e-9 || (b == a && f >= bestF - 1e-9)) { bestF = f; best = b; }
265 for (int c = 0; c < KR; c++) { Nc[b * KR + c] -= sv[c]; Nc[c * KR + b] -= pv[c]; }
266 Nc[b * KR + b] -= self;
267 }
268 for (int c = 0; c < KR; c++) { Nc[best * KR + c] += sv[c]; Nc[c * KR + best] += pv[c]; }
269 Nc[best * KR + best] += self;
270 if (best != a) { cls[x] = best; moves++; }
271 }
272 if (!moves) break;
273 }
274 free(freq); free(order); free(Nc);
275 return cls;
276}
277
278// Many-to-one accuracy of labels lab[] (< nlab) against gold token classes.
279static double m1_acc (const corpus_t *C, const int *lab, int nlab) {
280 int *ct = calloc((size_t) nlab * NCLS, sizeof(int));
281 for (int t = 0; t < C->ntok; t++) ct[(size_t) lab[t] * NCLS + C->c[t]]++;
282 long ok = 0;
283 for (int l = 0; l < nlab; l++) { int b = 0; for (int g = 1; g < NCLS; g++) if (ct[(size_t) l * NCLS + g] > b) b = ct[(size_t) l * NCLS + g]; ok += b; }
284 free(ct);
285 return (double) ok / C->ntok;
286}
287
219288// ---------------------------------------------------------------- span-type table
220289
221290typedef struct { uint16_t s[LMAX]; uint8_t len, used; uint32_t cnum, hard, first; float soft; } span_t; // hard/first/soft: Test A-soft
……@@ -461,17 +530,18 @@ static void run_soft (int seed, int tier, const char *src, const corpus_t *tr, c
461530// ---------------------------------------------------------------- main
462531
463532int main (int argc, char **argv) {
464 if (argc < 2) { fprintf(stderr, "usage: l2 <seed> [--tiers a,b] [--heldout N] [--mut-drop] [--jt prefix] [--soft [--soft-scale s]] [--undirected]\n"); return 1; }
533 if (argc < 2) { fprintf(stderr, "usage: l2 <seed> [--tiers a,b] [--heldout N] [--mut-drop] [--jt prefix] [--soft [--soft-scale s]] [--undirected] [--reparse]\n"); return 1; }
465534 int seed = atoi(argv[1]);
466535 int tiers[16] = { 3000, 10000, 30000 }, ntier = 3, nho = 2000;
467536 const char *jt_prefix = NULL;
468 int soft = 0, undirected = 0; double soft_scale = 1.0;
537 int soft = 0, undirected = 0, reparse = 0; double soft_scale = 1.0;
469538 for (int i = 2; i < argc; i++) {
470539 if (!strcmp(argv[i], "--mut-drop")) mut_drop = 1;
471540 else if (!strcmp(argv[i], "--heldout") && i + 1 < argc) nho = atoi(argv[++i]);
472541 else if (!strcmp(argv[i], "--jt") && i + 1 < argc) jt_prefix = argv[++i];
473542 else if (!strcmp(argv[i], "--soft")) soft = 1;
474543 else if (!strcmp(argv[i], "--undirected")) undirected = 1;
544 else if (!strcmp(argv[i], "--reparse")) reparse = 1;
475545 else if (!strcmp(argv[i], "--soft-scale") && i + 1 < argc) soft_scale = atof(argv[++i]);
476546 else if (!strcmp(argv[i], "--tiers") && i + 1 < argc) {
477547 ntier = 0;
……@@ -599,6 +669,45 @@ int main (int argc, char **argv) {
599669 free(MI);
600670 }
601671 }
672 // PREREG-reparse.md: round 0 parses words; round r clusters words over the round r-1 parse links, re-parses.
673 if (reparse) {
674 if (!Ptr.lnk) { Ptr = parse_alloc(full.ntok); Pho = parse_alloc(ho.ntok); }
675 u_tr = &Ptr; u_ho = &Pho;
676 int *cls = NULL;
677 double *Wp = malloc(sizeof(double) * V * V);
678 for (int r = 0; r <= ROUNDS; r++) {
679 int A = V;
680 if (r == 0) { memcpy(str, tr.w, sizeof(int) * tr.ntok); memcpy(sho, ho.w, sizeof(int) * ho.ntok); }
681 else {
682 memset(Wp, 0, sizeof(double) * V * V);
683 for (int s = 0; s < tr.ns; s++) {
684 int o = tr.off[s], n = tr.off[s + 1] - o;
685 for (int t = 0; t < n; t++) for (int k = 0; k < Ptr.nl[o + t]; k++) {
686 int b = Ptr.lnk[(o + t) * MAXL + k];
687 if (b > t) Wp[(size_t) tr.w[o + t] * V + tr.w[o + b]] += 1;
688 }
689 }
690 free(cls);
691 cls = exchange_pairs(Wp, &tr);
692 for (int t = 0; t < tr.ntok; t++) str[t] = cls[tr.w[t]];
693 for (int t = 0; t < ho.ntok; t++) sho[t] = cls[ho.w[t]];
694 A = KR;
695 }
696 corpus_t trs = with_symbols(&tr, str), hos = with_symbols(&ho, sho);
697 arm_t a = { "l1", A, NULL, 1 };
698 pairs_t P = count_pairs(&a, &trs);
699 double *MI = pair_mi(&P), pr, rc, f1;
700 free_pairs(&P);
701 parse_u(&trs, MI, A, 0, &Ptr); parse_u(&hos, MI, A, 0, &Pho);
702 free(MI);
703 link_prf(&hos, &Pho, &pr, &rc, &f1);
704 printf("RP\t%d\t%d\t%d\t%.5f\t%.5f\t%.5f\t%.5f\n", seed, tier, r, pr, rc, f1, m1_acc(&ho, sho, A));
705 char nm[32];
706 snprintf(nm, sizeof nm, "M-RP%d-ucomp", r);
707 run_arm(seed, tier, nm, RULE_UCOMP, &trs, &hos, &ho, (const int *) u_tr, (const int *) u_ho);
708 }
709 free(cls); free(Wp);
710 }
602711 free(Lmap);
603712
604713 // PREREG-soft.md: rank span types by assembly-ness over KPARSE perturbed parses (sources G and L).
experiments/level2/run-reparse.sh +12 -0
……@@ -0,0 +1,12 @@
1#!/bin/sh
2# PREREG-reparse full run: Test A + Test A-U + re-parse loop, 10 seeds x tiers 3K,10K,30K, in parallel.
3set -e
4cd "$(dirname "$0")"
5gcc -O2 -Wall -o l2 l2.c -lm 2>/dev/null
6out=results-reparse/$(date +%s)
7mkdir -p "$out"
8git rev-parse HEAD > "$out/commit.txt"
9sha256sum l2.c ../across-cluster/ac.c > "$out/src.sha256"
10for s in 1 2 3 4 5 6 7 8 9 10; do ./l2 $s --undirected --reparse > "$out/seed-$s.tsv" & done
11wait
12echo "$out"
experiments/level2/summarize-reparse.py +42 -0
……@@ -0,0 +1,42 @@
1#!/usr/bin/env python3
2# Score results-reparse/<stamp> against PREREG-reparse.md. Usage: summarize-reparse.py results-reparse/<stamp> results-U/<stamp>
3import glob, math, os, re, sys
4T975 = {9: 2.262}
5def mean(x): return sum(x) / len(x)
6def ci(d):
7 n = len(d); m = mean(d); sd = math.sqrt(sum((v - m) ** 2 for v in d) / (n - 1))
8 h = T975.get(n - 1, 1.96) * sd / math.sqrt(n)
9 return m, m - h, m + h
10def fmt(c): return f'{c[0]:+.3f} [{c[1]:+.3f}, {c[2]:+.3f}]'
11RP, A, P = {}, {}, {}
12NEW = re.compile(r'^RP\t|RP[0-9]')
13for f in glob.glob(os.path.join(sys.argv[1], 'seed-*.tsv')):
14 for line in open(f):
15 p = line.rstrip('\n').split('\t')
16 if p[0] == 'RP': RP[(int(p[2]), int(p[3]), int(p[1]))] = tuple(map(float, p[4:8]))
17 elif p[0] == 'A': A[(int(p[2]), p[3], int(p[1]))] = (int(p[4]), float(p[5]), float(p[6]), float(p[7]))
18 elif p[0] == 'P': P[(int(p[2]), p[3], p[4], int(p[1]))] = tuple(map(float, p[5:8]))
19seeds = sorted({k[2] for k in RP}); tiers = sorted({k[0] for k in RP}); top = max(tiers); rounds = sorted({k[1] for k in RP})
20out = []
21def say(s=''): out.append(s); print(s)
22bad = sum(list(open(os.path.join(sys.argv[2], f'seed-{s}.tsv'))) != [l for l in open(os.path.join(sys.argv[1], f'seed-{s}.tsv')) if not NEW.search(l)] for s in seeds)
23say(f'R0: {"PASS" if bad == 0 else "FAIL"} — {len(seeds) - bad}/{len(seeds)} seeds reproduce Test A-U exactly')
24def rp(t, r, i): return [RP[(t, r, s)][i] for s in seeds]
25def at(t, m, i): return [A[(t, m, s)][i] for s in seeds]
26def pf(t, x): return [P[(t, x, 'MST', s)][2] for s in seeds]
27say('\n## Per round (mean): link F1 / many-to-one / ucomp type recall / ucomp precision')
28for t in tiers:
29 say(f'tier {t}: G-MST F1 {mean(pf(t, "G")):.3f} L-MST F1 {mean(pf(t, "L")):.3f}, L ucomp recall {mean(at(t, "M-L-ucomp-MST", 2)):.3f}')
30 for r in rounds:
31 m = f'M-RP{r}-ucomp'
32 say(f' round {r}: {mean(rp(t, r, 2)):.3f} / {mean(rp(t, r, 3)):.3f} / {mean(at(t, m, 2)):.3f} / {mean(at(t, m, 3)):.3f}')
33say('\n## Predictions')
34def verdict(n, ok, txt): say(f'{n}: {"PASS" if ok else "FAIL"} — {txt}')
35ok = True; cells = []
36for t in tiers: c = ci([a - b for a, b in zip(rp(t, 1, 2), rp(t, 0, 2))]); ok &= c[1] > 0; cells.append(f'{t}: {fmt(c)}')
37verdict('P1', ok, 'link F1 round 1 − round 0 ' + '; '.join(cells))
38last = max(rounds)
39c = ci([a - b for a, b in zip(rp(top, last, 2), rp(top, 1, 2))]); verdict('P2', c[1] > 0, f'link F1 round {last} − round 1 at {top} {fmt(c)}')
40c = ci([a - b for a, b in zip(rp(top, last, 2), pf(top, 'L'))]); verdict('P3', c[1] > 0, f'link F1 round {last} − L-MST at {top} {fmt(c)}')
41c = ci([a - b for a, b in zip(at(top, f'M-RP{last}-ucomp', 2), at(top, 'M-L-ucomp-MST', 2))]); verdict('P4', c[1] > 0, f'ucomp type recall round {last} − L-MST at {top} {fmt(c)}')
42open(os.path.join(sys.argv[1], 'summary.txt'), 'w').write('\n'.join(out) + '\n')