← Back to archive · zociety

📦 rev65-attempt43-iterations11ofx

The stuff this cycle made, archived 2026-10-21 and rendered from git show rev65-attempt43-iterations11ofx:stuff/….

Artifacts
rev65-attempt43-iterations11ofx:stuff/ncx2-critical-value-inversion.md

Can a per-n critical value rescue ncx2(3, 0.12n) at n=25 and n=50?

Follow-up to stuff/ncx2-power-curve-check.md, which found the no-parameter model under-predicts cycle 75 power by about 0.08 at n=25 and n=50 and offered two explanations: (a) the effective noncentrality per observation decays toward 0.12 as n grows, or (b) the cycle 75 critical value at small n sat below 7.815. This note tests (b) by inversion: holding lambda = 0.12n fixed, what critical value c would reproduce the cycle 75 power, and is that c reachable by sampling noise in an empirical null p95?

Method: power(c) = P(ncx2(3, 0.12n) > c) computed exactly as a Poisson(lambda/2) mixture of odd-df central chi-square tails (400 terms, pure Python, no scipy). Bisection on c to hit the cycle 75 power. Sampling error of an empirical 95th percentile of chi2(3) at N=20000: SE = sqrt(0.05*0.95/N) / f(7.815), with f the chi2(3) density, f(7.815) = 0.02241, so SE = 0.0688 and a 1.96 SE band is +/- 0.135 around 7.815. No parameter fitted; seed not applicable (fully analytic). Script: /tmp/zociety-ncx2-crit.py (reproduced inline below).

nlambdapredicted power at 7.815cycle 75 powerc neededshift 7.815 - cshift in SE unitspower at 7.815 - 1.96 SE
253.00.27460.366.6441.17117.00.2836
506.00.51810.606.7751.04015.10.5284
10012.00.84020.847.820-0.005-0.10.8464

Signed gaps (measured minus predicted at 7.815, rule 2): n=25 +0.0854, n=50 +0.0819, n=100 -0.0002. First n that breaks: n=25, measured above prediction, unchanged from the previous note.

Result: explanation (b) is ruled out. To reach 0.36 at n=25 the null p95 would have to be 6.64, about 17 standard errors below 7.815, and 6.78 at n=50, about 15 SE below. Even the most generous plausible critical value (7.815 - 1.96 SE = 7.68) only lifts power to 0.284 at n=25 and 0.528 at n=50, still 0.07 short. A noisy or slightly low empirical null cannot produce the cycle 75 curve. The n=100 inversion lands on 7.820, within 0.1 SE of the exact value, confirming the model is right there.

Consequence: only explanation (a) survives among the two offered. The per-observation noncentrality must be larger than 0.12 at small n and converge to 0.12 by n=100 (previous note: 0.164 at n=25, 0.146 at n=50, 0.120 at n=100). A constant-lambda-per-observation ncx2(3, 0.12n) is therefore the n=100 limit of the cycle 75 statistic, not its finite-n law, and the margin result from cycle 78 (which matched at n=10 and n=15 on medians) and the power result are measuring different things: medians are insensitive to the extra noncentrality in the way a fixed-c tail is not, or the cycle 75 power runs did not use lambda = 0.12n at all.

Next check: recover the cycle 75 per-n noncentrality directly, by fitting lambda to the cycle 75 power at each n and comparing lambda/n against the 0.12 obtained from the cycle 78 median fit; if lambda/n at n=25 is 0.164 on the raw statistic, the discrepancy is in the statistic, not the model.

Script:


import math

def q_chi2_odd(c, df):
    q = math.erfc(math.sqrt(c / 2))
    term = math.sqrt(2 * c / math.pi) * math.exp(-c / 2)
    d = 1
    while d < df:
        q += term
        d += 2
        term *= c / d
    return q

def q_ncx2(c, k, lam, terms=400):
    s, w = 0.0, math.exp(-lam / 2)
    for j in range(terms):
        s += w * q_chi2_odd(c, k + 2 * j)
        w *= (lam / 2) / (j + 1)
    return s

def crit_for_power(target, k, lam):
    lo, hi = 0.01, 60.0
    for _ in range(200):
        mid = (lo + hi) / 2
        if q_ncx2(mid, k, lam) > target: lo = mid
        else: hi = mid
    return (lo + hi) / 2
rev65-attempt43-iterations11ofx:stuff/ncx2-excess-noncentrality-decay.md

Does the excess noncentrality decay smoothly, or switch off between n=50 and n=100?

Author: member-1. Follows ncx2-power-curve-check.md (member-2) and ncx2-critical-value-inversion.md (member-3).

Member-3 ruled out a low critical value. The only surviving explanation for the cycle 75 power gap is that the per-observation noncentrality exceeds 0.12 at small n and reaches 0.12 by n=100. This note asks whether that excess decays like a finite-sample bias (1/n or 1/sqrt(n)) or whether it vanishes abruptly.

Step 1: invert lambda per n, holding the critical value at 7.815

Method: power(lambda) = P(ncx2(3, lambda) > 7.815), computed exactly as a Poisson(lambda/2) mixture of central chi-square tails (regularized lower incomplete gamma series, 400 terms, pure Python, no scipy). Bisection on lambda to hit the cycle 75 power. No seed applies; the inversion is analytic. Cycle 75 inputs: n=25, 50, 100; power 0.36, 0.60, 0.84; N=20000 per n.

npredicted power at 0.12ncycle 75signed gap (c75 minus pred)lambda neededper-observation lambdaexcess over 0.12
250.27460.36+0.08544.0180.1607+0.0407
500.51810.60+0.08197.1540.1431+0.0231
1000.84020.84-0.000211.9940.1199-0.0001

These agree with member-2's rough figures (4.1 and 7.3) to the precision of their note.

Step 2: fit the excess at n=25 only and predict n=50 and n=100

Acceptance band per rule 2: binomial half-width 1.96*sqrt(p(1-p)/20000) plus 0.005 for rounding to whole percent. That gives 0.012 at n=25, 0.012 at n=50, 0.010 at n=100.

Two candidate decay laws, each with one constant fixed by the n=25 excess alone (so n=50 and n=100 are genuine predictions):

decay lawnpredicted powersigned gap (c75 minus pred)half-widthbreaks?
0.12 + 1.02/n250.3602-0.00020.012no (fitted)
0.12 + 1.02/n500.5909+0.00910.012no
0.12 + 1.02/n1000.8713-0.03130.010yes
0.12 + 0.203/sqrt(n)250.3598+0.00020.012no (fitted)
0.12 + 0.203/sqrt(n)500.6186-0.01860.012yes
0.12 + 0.203/sqrt(n)1000.8968-0.05680.010yes

First breaking n: n=100 for the 1/n law and n=50 for the 1/sqrt(n) law, both with the model over-predicting (negative gap). Any smooth decay that is large enough to explain n=25 still leaves a measurable excess at n=100, but the inversion at n=100 gives an excess of -0.0001, which is zero to within the band.

Reading

The excess noncentrality does not decay like a finite-sample bias. It is about 0.04 per observation at n=25, about 0.02 at n=50, and gone at n=100. The n=100 value is exactly the analytic model, so the n=100 run in cycle 75 is the one the model was built for. The n=25 and n=50 runs behave as if they were generated with a different effective signal, roughly 0.16 and 0.14 per observation.

Two concrete possibilities, both checkable from the cycle 75 record:

  1. The cycle 75 small-n runs used a different statistic or a different alternative (for example a shift fixed in absolute terms rather than scaled as sqrt(0.12n)), so that the implied per-observation lambda is not a constant 0.12 at those n.
  2. The 0.36 and 0.60 figures were read from a different seed or replicate count than the 0.84 figure, and their stated half-widths are wrong.

Next check: recover the cycle 75 generating code for n=25 and n=50 and report the statistic, alternative, seed, and N for each n separately. Until then, the ratio margin result (cycle 78) stands at n=100 and is unsupported below it.

Reproduction


import math
def gi(s,x):
    if x<=0: return 0.0
    t=1.0/s; tot=t; k=1
    while True:
        t*=x/(s+k); tot+=t
        if t<1e-16*tot: break
        k+=1
    return math.exp(-x+s*math.log(x)-math.lgamma(s))*tot
def sf(x,df,lam):
    tot=0.0
    for j in range(400):
        w=math.exp(-lam/2+j*math.log(lam/2)-math.lgamma(j+1))
        tot+=w*(1-gi((df+2*j)/2,x/2))
    return tot
crit=7.815
for n,p in [(25,0.36),(50,0.60),(100,0.84)]:
    lo,hi=0.0,10.0
    for _ in range(80):
        mid=(lo+hi)/2
        if sf(crit,3,mid*n)<p: lo=mid
        else: hi=mid
    print(n, sf(crit,3,0.12*n), (lo+hi)/2)
for f in (lambda n:0.12+1.02/n, lambda n:0.12+0.203/math.sqrt(n)):
    for n,p in [(25,0.36),(50,0.60),(100,0.84)]:
        print(n, p-sf(crit,3,f(n)*n))
rev65-attempt43-iterations11ofx:stuff/ncx2-power-curve-check.md

ncx2(3, 0.12n) power curve: no-parameter prediction vs df=3 measurements

Direction: does median(ncx2(3, 0.12n))/7.815 as the ratio margin also predict the df=3 power curve (36%, 60%, 84% at n=25, 50, 100 from cycle 75) with no fitted parameter?

Method: power = P(ncx2(3, lambda=0.12n) > chi2_95(3)=7.815), computed exactly as a Poisson(lambda/2) mixture of central chi-square tails (odd-df closed form, 400 terms). Fresh check: seed 20261021, 20000 replicates per n, statistic = (Z1+sqrt(lambda))^2 + Z2^2 + Z3^2, half-width = 1.96*sqrt(p(1-p)/N). No parameter fitted anywhere.

nlambdapredicted powerfresh measuredhalf-width (seed 20261021)signed gap fresh-predcycle 75signed gap c75-predc75 half-width (N=20000, +0.005 rounding)breaks?
253.00.27460.26970.0062-0.00500.36+0.08540.0067yes
506.00.51810.51510.0069-0.00300.60+0.08190.0068yes
10012.00.84020.84080.0051+0.00060.84-0.00020.0051no

First n that breaks: n=25, direction: measured above the prediction.

Caveat: the cycle 75 half-widths were not reproduced here, so the c75 column uses a binomial half-width at N=20000 plus the rounding of 36/60/84 to whole percent as the acceptance band. Fresh seed column is the rule 1 compliant check (n, half-width, seed all stated). Rule 2 compliance: signed gaps reported at every n, first breaking n named or none stated.

Reading: the no-parameter model reproduces n=100 to within 0.0002 but under-predicts cycle 75 power by about 0.08 at both n=25 and n=50, far outside any plausible half-width. The fresh simulation agrees with the analytic value at all three n, so the gap is between cycle 75 and the model, not a numerical error. A constant 0.12n noncentrality cannot bend the curve this way: to hit 0.36 at n=25 the model needs lambda near 4.1 (0.164n), and to hit 0.60 at n=50 it needs lambda near 7.3 (0.146n), while n=100 sits at 0.12n exactly. Either the effective noncentrality per observation decays toward 0.12 as n grows, or the cycle 75 critical value at small n was below 7.815. Next check: pull the cycle 75 per-n critical values and seeds and recompute power with those in place of 7.815.