/* DSA top-keep partial-select: the quickselect rewrite (#356) must produce a * BIT-IDENTICAL kept-position set to the old full-vocab qsort, for every score * shape the attention indexer can see. * * Why this test exists (#356): attention_rows() selects the top-`keep` context * keys (index_topk=2048 on GLM-5.2) to attend to. It previously did this by * full-qsorting all `nk` scores (O(nk log nk)) and reading tmp[keep-1] as the * threshold. It now does a partial_select_desc (quickselect, O(nk) average) and * takes the threshold as the min of the selected top-keep block. The contract is * subtle but STRONGER than test_topp's: * * The two position-order scans that build dst[] -- * for t: if isc[t] > thr -> keep (strictly above threshold) * for t: if isc[t] == thr -> keep (ties, in position order) * -- are UNCHANGED by the rewrite. So if the threshold value is identical, * the kept-position set is identical element-by-element (not just as a * multiset, which is all the unstable sampling heap in #335 could promise). * * Strategy: drive the REAL partial_select_desc (via the include-glm.c pattern) * and replicate the production threshold derivation + scans, then compare the * resulting dst[] against an INDEPENDENT reference that re-implements the OLD * algorithm (full qsort + tmp[keep-1] threshold) on a private buffer. The kept * sets must be element-wise equal on every shape, including tie plateaus where * the boundary membership is decided by the position scan. * * We also directly unit-test partial_select_desc's partition invariant: after * the call, max(a[keep..n)) <= min(a[0..keep)) -- i.e. the keep largest really * did land in the prefix. This catches a broken quickselect even before the * end-to-end comparison. * * In-memory only (no scratch files), so it builds clean on the Windows MinGW CI * job without the unmerged compat shim. */ #define main coli_glm_main_unused #include "../glm.c" #undef main #include static int g_nfails = 0; #define FAIL(fmt, ...) do { \ fprintf(stderr, " FAIL [%s nk=%d keep=%d shape=%s]: " fmt "\n", \ label, nk, keep, shape_name, ##__VA_ARGS__); \ g_nfails++; \ return; \ } while (0) /* ---- independent reference: the OLD algorithm (full qsort + tmp[keep-1]) ---- */ /* qsort comparator matching the production cmp_fdesc exactly (desc, unstable). */ static int cmp_ref_desc(const void *a, const void *b){ float x=*(const float*)a, y=*(const float*)b; return xy?-1:0; } /* Reproduce the OLD glm.c:2589-2596 exactly: copy, qsort desc, threshold = * tmp[keep-1], then the two position-order scans into dst[]. Returns nd. */ static int keep_old(const float *isc, int nk, int keep, int *dst){ float *tmp=malloc((size_t)nk*sizeof(float)); memcpy(tmp,isc,(size_t)nk*sizeof(float)); qsort(tmp,(size_t)nk,sizeof(float),cmp_ref_desc); float thr=tmp[keep-1]; int nd=0; for(int t=0;tthr) dst[nd++]=t; for(int t=0;tthr) dst[nd++]=t; for(int t=0;t= every element of tmp[keep..n). * (>=, not >: equal values may sit on either side of the partition boundary, * which is fine -- the threshold is the MIN of the prefix, and the position * scan handles ties.) */ float top_min=INFINITY, tail_max=-INFINITY; for(int i=0;itail_max) tail_max=tmp[i]; if(!(top_min >= tail_max)) FAIL("partition invariant violated: top_min=%.9g < tail_max=%.9g", top_min, tail_max); free(tmp); } /* ---- end-to-end: old vs new kept-set must be element-wise identical ---- */ static void check_case(const char *label, int nk, int keep, const char *shape_name, const float *isc){ int *da=malloc((size_t)nk*sizeof(int)); int *db=malloc((size_t)nk*sizeof(int)); int na=keep_old(isc,nk,keep,da); int nb=keep_new(isc,nk,keep,db); /* 1. both keep exactly `keep` positions (the contract: keep the top-keep by * count). A count mismatch is a real bug, not a tie artifact. */ if(na!=keep) FAIL("old kept %d, expected %d (old path is the reference)", na, keep); if(nb!=keep) FAIL("new kept %d, expected %d", nb, keep); if(na!=nb) FAIL("keep-count mismatch: old=%d new=%d", na, nb); /* 2. element-wise identical dst[]. This is the strong contract: because the * threshold is derived identically and the position-order scans are byte- * for-byte the same, the kept SET and its ORDER must match exactly. (This * is what makes #356 cleaner than #335, which was multiset-only.) */ int first_diff=-1; for(int i=0;i=0) FAIL("kept-set differs at index %d: old dst[%d]=%d new dst[%d]=%d", first_diff, first_diff, da[first_diff], first_diff, db[first_diff]); /* 3. also check the partition invariant directly (catches a subtly broken * quickselect even if the threshold happened to come out right). */ check_partition(label,isc,nk,keep,shape_name); free(da); free(db); printf(" ok [nk=%d keep=%d shape=%s]\n", nk, keep, shape_name); } #undef FAIL /* deterministic xorshift32 RNG (matches the test_i4_grouped.c / test_topp.c convention) */ static uint32_t rng_state = 0x12345678u; static uint32_t xr(void){ rng_state ^= rng_state << 13; rng_state ^= rng_state >> 17; rng_state ^= rng_state << 5; return rng_state; } static double frand(void){ return (xr() >> 8) * (1.0 / 16777216.0); } /* [0,1) */ /* fill scores for a given shape. Shapes stress the threshold boundary and the * quickselect's median-of-three pivot differently. */ static void fill_shape(float *isc, int nk, int shape){ switch(shape){ case 0: /* uniform random distinct (no ties): the clean contract case */ for(int i=0;i3) isc[nk/3]=1.f; if(nk>2) isc[nk/2]=0.5f; break; case 2: /* strictly decreasing geometric (no ties): sorted input -- worst case * for a naive quickselect; median-of-three must handle it */ for(int i=0;i boundary membership decided * entirely by the position scan (exercises the ==thr path) */ for(int i=0;i degenerate threshold, all kept * via the ==thr scan; quickselect must not infinite-loop or corrupt */ for(int i=0;i=n / k==1 / k==n edges. */ int nks[] = {1, 2, 8, 64, 2049, 4097, 8193}; int keeps[] = {1, 8, 256, 1024, 2048}; int n_shapes = 6; int cases = 0; for(size_t ni=0; nin, but skip the plateau/geometric edge if nk<7) */ fill_shape(isc,nk,shape); for(size_t ki=0; kink) continue; /* keep<=nk invariant of the production code */ if(keep<=0) continue; char label[40]; snprintf(label,sizeof(label),"nk[%zu]/keep[%zu]/shape[%d]",ni,ki,shape); const char *sn=(const char*[]){"random","peaked","decreasing","increasing","plateau","all-equal"}[shape]; check_case(label,nk,keep,sn,isc); cases++; } } free(isc); } /* edge: keep == nk (nothing to partition; both paths keep everything) */ { int nk=100, keep=100; float isc[100]; for(int i=0;i every kept slot is a tie; * the position scan must pick positions 0..keep-1 deterministically */ { int nk=1000, keep=500; float isc[1000]; for(int i=0;i positions 0..%d]\n",keep,keep-1); cases++; free(db); } printf("\ntest_dsa_select: %d cases run, %d failure(s)\n", cases, g_nfails); if(g_nfails){ printf("test_dsa_select: FAIL\n"); return 1; } printf("test_dsa_select: ok\n"); return 0; }