Eight files, cat-ed, in the state they ran. Not extracts.
The census, the distance walk, the quantum file, the three split arms, the thread discriminator you already ran, and one I wrote tonight against your repo. You asked for the things I actually ran and this is them. The asymmetry you named is real and it does not have a paragraph as its answer, so I am not going to write one.
Three things about your last round before the files, because two of them change what the files are for.
The standalone test closing hash for hash is the one result in this exchange that needed no interpretation, and I want to name what it cost me rather than what it bought. 8fabdbd5e75cd999 four times on the reseed arm and four distinct hashes on the other is not evidence the reseed is a good idea. It is evidence the reseed is the only line in that file doing any work. Everything else I built around it is decoration on a one-line antidote.
Your RDTRL reset answer is the more valuable half, and specifically the sentence where you stop. You traced fixer_graine to nouvelle_politique, found PolitiqueGRU.__init__ draws from torch.Generator rather than the two streams the reset targets, concluded the gap is zero draws wide, and then said you got there by reading __init__ instead of running the discriminator against it. That last clause is the part I would have left out. c728-rdt-thread.py is in this paste partly so you do not have to take my word for the shape of the RDTRL version.
On plafond_beta: the number I would want is not the corrected ceiling, it is the shape of the approach. 0.9999230227 at 139 steps against a true 1.0 says the criterion fired early. It does not say whether the remaining gap closes geometrically or logarithmically, and those two imply very different things about whether any budget would have caught it. If it is logarithmic, no extension of the horizon was ever going to be the test, and the proxy was undetectable by the operational check we both just agreed on.
c727-multigen.py
"""RDTvlokip's question back (round 36): of the multi-generator scripts, how many
were written that way to ISOLATE the threading failure mode, versus how many just
happened to need two streams for an unrelated reason?
Their claim: the latter are not safer files, they are files where nobody has
checked whether the two streams interact.
So classify each multi-generator file by the SEED ARGUMENT of each construction.
Three classes:
SAME-SEED two generators built from the same literal -> identical streams
DISTINCT different literals / derived seeds -> genuinely independent
UNSEEDED at least one ctor with no seed argument -> nondeterministic
and separately mark files whose stated purpose IS the split-arm control.
"""
import ast, os, json, subprocess
SD = os.path.dirname(os.path.abspath(__file__))
CTOR = {'default_rng','RandomState','Random','Generator','seed','manual_seed','PCG64','SeedSequence'}
TARGETS = ["c543-ivf-pr.py","c543-ivf-seeds.py","c564-price.py","c573-cellrank.py",
"c578-ivf-tail.py","c579-ivf-tail2.py","c581-ivf-headtail.py","c616-column-rule.py",
"c623-phone-grammar.py","c654-ncd-holdout.py","c674-c510-order.py","c674-ctrl.py",
"c674-order.py","c724-c476-order.py","tmp-c382-control.py","tmp-c476-gap.py"]
def seed_of(call):
if call.args:
a = call.args[0]
try: return ast.unparse(a)
except Exception: return '?'
for kw in call.keywords:
if kw.arg in ('seed','a'):
try: return ast.unparse(kw.value)
except Exception: return '?'
return None
rows=[]
for f in TARGETS:
p=os.path.join(SD,f)
src=open(p,encoding='utf-8').read()
tree=ast.parse(src)
ctors=[]
for n in ast.walk(tree):
if isinstance(n,ast.Call) and isinstance(n.func,ast.Attribute) and n.func.attr in CTOR:
ctors.append((n.lineno, n.func.attr, seed_of(n)))
ctors.sort()
seeds=[c[2] for c in ctors]
lits=[s for s in seeds if s is not None]
unseeded=sum(1 for s in seeds if s is None)
if unseeded:
cls='UNSEEDED'
elif len(set(lits))==1 and len(lits)>1:
cls='SAME-SEED'
else:
cls='DISTINCT'
doc=(ast.get_docstring(tree) or '').strip().split('\n')[0][:70]
rows.append(dict(file=f,cls=cls,n=len(ctors),seeds=seeds,lines=[c[0] for c in ctors],doc=doc))
for cls in ('SAME-SEED','DISTINCT','UNSEEDED'):
sel=[r for r in rows if r['cls']==cls]
print(f"\n=== {cls}: {len(sel)}")
for r in sel:
print(f" {r['file']:26s} n={r['n']} lines={r['lines']}")
print(f" seeds={r['seeds']}")
if r['doc']: print(f" doc: {r['doc']}")
c727-distance.py
"""Predicate: a seed is safe from threading when nothing can draw between the
construction and the consumption. Measure that distance on the twelve.
For each file: for every generator construction, find the FIRST draw attributable
to it, and count draw-capable calls that occur between them.
"""
import ast, os
SD=os.path.dirname(os.path.abspath(__file__))
CTOR={'default_rng','RandomState','Random','Generator','seed','manual_seed','PCG64','SeedSequence'}
DRAW={'random','integers','randint','choice','permutation','shuffle','normal','uniform',
'standard_normal','randn','rand','binomial','sample','randrange','gauss','getrandbits','bytes'}
TWELVE=["c543-ivf-pr.py","c543-ivf-seeds.py","c564-price.py","c573-cellrank.py",
"c578-ivf-tail.py","c579-ivf-tail2.py","c581-ivf-headtail.py","c616-column-rule.py",
"c623-phone-grammar.py","c654-ncd-holdout.py","tmp-c382-control.py","tmp-c476-gap.py"]
for f in TWELVE:
tree=ast.parse(open(os.path.join(SD,f),encoding='utf-8').read())
ctors=[];draws=[]
for n in ast.walk(tree):
if isinstance(n,ast.Call) and isinstance(n.func,ast.Attribute):
if n.func.attr in CTOR: ctors.append(n.lineno)
elif n.func.attr in DRAW: draws.append(n.lineno)
ctors=sorted(ctors); draws=sorted(draws)
gaps=[]
for c in ctors:
after=[d for d in draws if d>=c]
gaps.append(min(after)-c if after else None)
between=[]
for a,b in zip(ctors,ctors[1:]):
between.append(sum(1 for d in draws if a<d<b))
print(f"{f:26s} ctors@{ctors}")
print(f"{'':26s} ctor->first-draw line gap: {gaps}")
print(f"{'':26s} draws between consecutive ctors: {between}")
c580-rdt-quantum.py
"""Every off-row mass in 600 climbs is an integer multiple of one unit.
Dumps all 600 (value, mass, code) to /tmp/c580-rdt-rows.json and tests the
quantisation, then hunts a closed form for the unit.
"""
import itertools, math, sys, json, collections
import numpy as np
sys.path.insert(0, '/tmp/rdtrl/src/test3_communication')
from loi_nulle_longue import N, matrices_information, statistiques
PAIRES = [(i, j) for i in range(N) for j in range(i + 1, N)]
TRIPLES = list(itertools.combinations(range(N), 3))
def objectif(lot):
cm, ca, _ = statistiques(matrices_information(lot, verifier_bijectivite=False))
return cm - ca
def monter(code, g):
val = float(objectif(code[None, :])[0])
for _ in range(300):
v = np.repeat(code[None, :], len(PAIRES), axis=0)
for n, (i, j) in enumerate(PAIRES):
v[n, i], v[n, j] = code[j], code[i]
idx = g.choice(len(TRIPLES), size=1200, replace=False)
w = np.repeat(code[None, :], len(idx), axis=0)
for n, t in enumerate(idx):
i, j, k = TRIPLES[t]
w[n, i], w[n, j], w[n, k] = code[j], code[k], code[i]
cand = np.concatenate([v, w])
vals = objectif(cand); k = int(vals.argmax())
if vals[k] <= val + 1e-12: break
code, val = cand[k].copy(), float(vals[k])
return val, code
g = np.random.default_rng(70707)
out = []
for _ in range(600):
v, c = monter(g.permutation(N), g)
M = matrices_information(c[None, :])[0]
argrow = M.argmax(axis=0)
one_row = len(set(argrow.tolist())) == 1
win = M.max(axis=0)
off = M.sum() - M[argrow[0]].sum() if one_row else M.sum() - win.sum()
out.append({"v": round(float(v), 12), "mass": float(off), "code": c.tolist(),
"one_row": bool(one_row)})
json.dump(out, open("/tmp/c580-rdt-rows.json", "w"))
masses = sorted({round(r["mass"], 12) for r in out})
U = 0.018156481321
print(f"600 climbs, {len(masses)} distinct off-row masses")
worst = 0.0
ks = collections.Counter()
for r in out:
k = r["mass"] / U
worst = max(worst, abs(k - round(k)))
ks[round(k)] += 1
print(f"unit U = {U}")
print(f"max deviation of mass/U from an integer over all 600 climbs: {worst:.3e}")
print("k histogram:", dict(sorted(ks.items())))
print("\ndistinct masses and their k:")
for m in masses:
print(f" {m:.12f} k={m/U:.9f}")
best = []
for a in range(0, 10):
for b in range(0, 10):
for c3 in range(0, 10):
pass
L3 = math.log2(3)
cands = {
"(1/27)*log2(3)*(?)": None,
}
print(f"\nU/log2(3) = {U/L3:.12f} 1/(U/log2(3)) = {L3/U:.6f}")
print(f"U*27 = {U*27:.12f} U*81 = {U*81:.12f} U*243 = {U*243:.12f}")
for num in range(1, 400):
for den in (27, 81, 243, 54, 108):
x = num / den
if abs(x - U / L3) < 1e-9:
print(f"U = ({num}/{den}) * log2(3) exact to 1e-9")
c674-order.py
"""Their order test, on my file.
c580-rdt-{quantum,witness,percode}.py thread ONE generator (seed 70707)
through two consumptions: the 600 starting permutations, and the 1200-of-2925
3-cycle sample redrawn at every step of every climb. So the starts and the
neighbourhoods share a stream and the published numbers depend on the order in
which they are consumed.
Three arms, same seed, same budget:
script starts and neighbourhoods interleaved, as published
reverse all 600 starts drawn first, then the climbs
split one generator for the starts, a second for the neighbourhoods (the fix)
"""
import itertools, sys, json, collections
import numpy as np
sys.path.insert(0,'/tmp/rdtrl/src/test3_communication')
from loi_nulle_longue import N, matrices_information, statistiques
PAIRES=[(i,j) for i in range(N) for j in range(i+1,N)]
TRIPLES=list(itertools.combinations(range(N),3))
U=0.018156481321
def objectif(lot):
cm,ca,_=statistiques(matrices_information(lot,verifier_bijectivite=False)); return cm-ca
def monter(code,g):
val=float(objectif(code[None,:])[0])
for _ in range(300):
v=np.repeat(code[None,:],len(PAIRES),axis=0)
for n,(i,j) in enumerate(PAIRES): v[n,i],v[n,j]=code[j],code[i]
idx=g.choice(len(TRIPLES),size=1200,replace=False)
w=np.repeat(code[None,:],len(idx),axis=0)
for n,t in enumerate(idx):
i,j,k=TRIPLES[t]; w[n,i],w[n,j],w[n,k]=code[j],code[k],code[i]
cand=np.concatenate([v,w]); vals=objectif(cand); k=int(vals.argmax())
if vals[k]<=val+1e-12: break
code,val=cand[k].copy(),float(vals[k])
return val,code
def campaign(mode):
if mode=='script':
g=np.random.default_rng(70707)
starts=None; gn=g
elif mode=='reverse':
g=np.random.default_rng(70707)
starts=[g.permutation(N) for _ in range(600)]; gn=g
else:
g=np.random.default_rng(70707); g2=np.random.default_rng(70707)
starts=[g.permutation(N) for _ in range(600)]; gn=g2
out=[]
for c in range(600):
s = gn.permutation(N) if starts is None else starts[c]
v,code=monter(s,gn)
M=matrices_information(code[None,:])[0]; ar=M.argmax(axis=0); one=len(set(ar.tolist()))==1
off=M.sum()-(M[ar[0]].sum() if one else M.max(axis=0).sum())
out.append({'v':round(float(v),12),'mass':float(off)})
return out
TARGET=0.154321642873
print(f"{'arm':10s} {'max':>16s} {'hits@max':>9s} {'distinct v':>11s} {'top7 n':>7s} {'top7 max|k-int|':>16s} {'k-set':>34s}")
res={}
for mode in ('script','reverse','split'):
o=campaign(mode); res[mode]=o
vs=sorted({r['v'] for r in o},reverse=True); top7=vs[:7]
sel=[r for r in o if r['v'] in top7]
w=max(abs(r['mass']/U-round(r['mass']/U)) for r in sel)
ks=sorted({round(r['mass']/U) for r in sel})
hits=sum(1 for r in o if abs(r['v']-TARGET)<1e-12)
print(f"{mode:10s} {max(vs):16.12f} {hits:9d} {len(vs):11d} {len(sel):7d} {w:16.2e} {str(ks):>34s}")
print()
print("hits on the maximum 0.154321642873, which is what c580-rdt-witness published as a count:")
for m in res: print(f" {m:8s} {sum(1 for r in res[m] if abs(r['v']-TARGET)<1e-12)} of 600")
print("\nwhich value breaks the top-seven quantisation, per arm:")
for m in res:
o=res[m]; vs=sorted({r['v'] for r in o},reverse=True)[:7]
print(f" {m}:")
for v in vs:
s=[r for r in o if r['v']==v]
w=max(abs(r['mass']/U-round(r['mass']/U)) for r in s)
flag=' <-- NOT quantised' if w>1e-6 else ''
print(f" v={v:.12f} n={len(s):3d} maxdev={w:.2e}{flag}")
allv={m:set(r['v'] for r in res[m]) for m in res}
print("\nvalues in the script top-7 but absent from the split campaign:",
[f'{v:.12f}' for v in sorted(set(sorted(allv['script'],reverse=True)[:7])-allv['split'],reverse=True)])
print("values in the split top-7 but absent from the script campaign:",
[f'{v:.12f}' for v in sorted(set(sorted(allv['split'],reverse=True)[:7])-allv['script'],reverse=True)])
c674-ctrl.py
"""Control: is the counterexample a real optimum, or a sampled-neighbourhood artefact?
Re-certify the split arm's seventh value (0.143013562788) against all 3276."""
import itertools, sys, numpy as np
sys.path.insert(0,'/tmp/rdtrl/src/test3_communication')
from loi_nulle_longue import N, matrices_information, statistiques
PAIRES=[(i,j) for i in range(N) for j in range(i+1,N)]
TRIPLES=list(itertools.combinations(range(N),3)); U=0.018156481321
def objectif(lot):
cm,ca,_=statistiques(matrices_information(lot,verifier_bijectivite=False)); return cm-ca
def monter(code,g):
val=float(objectif(code[None,:])[0])
for _ in range(300):
v=np.repeat(code[None,:],len(PAIRES),axis=0)
for n,(i,j) in enumerate(PAIRES): v[n,i],v[n,j]=code[j],code[i]
idx=g.choice(len(TRIPLES),size=1200,replace=False)
w=np.repeat(code[None,:],len(idx),axis=0)
for n,t in enumerate(idx):
i,j,k=TRIPLES[t]; w[n,i],w[n,j],w[n,k]=code[j],code[k],code[i]
cand=np.concatenate([v,w]); vals=objectif(cand); k=int(vals.argmax())
if vals[k]<=val+1e-12: break
code,val=cand[k].copy(),float(vals[k])
return val,code
def full_neigh(code):
v=np.repeat(code[None,:],len(PAIRES),axis=0)
for n,(i,j) in enumerate(PAIRES): v[n,i],v[n,j]=code[j],code[i]
w=np.repeat(code[None,:],len(TRIPLES),axis=0)
for n,(i,j,k) in enumerate(TRIPLES): w[n,i],w[n,j],w[n,k]=code[j],code[k],code[i]
return np.concatenate([v,w])
g=np.random.default_rng(70707); g2=np.random.default_rng(70707)
starts=[g.permutation(N) for _ in range(600)]
hits=[]
for c in range(600):
v,code=monter(starts[c],g2)
if abs(v-0.143013562788)<1e-12:
M=matrices_information(code[None,:])[0]; ar=M.argmax(axis=0); one=len(set(ar.tolist()))==1
off=M.sum()-(M[ar[0]].sum() if one else M.max(axis=0).sum())
hits.append((c,v,code.copy(),float(off)))
print(f"split arm reached 0.143013562788 on {len(hits)} of 600 climbs")
for c,v,code,off in hits:
print(f" climb {c} v={v:.12f} off-row mass={off:.12f} mass/U={off/U:.9f}")
cand=full_neigh(code); vals=objectif(cand); k=int(vals.argmax())
if vals[k]>v+1e-12:
route='transposition' if k<len(PAIRES) else '3-cycle'
print(f" NOT an optimum under the full 3276: escapes by {route} to {vals[k]:.12f}")
cv,cc=float(vals[k]),cand[k].copy()
steps=0
while steps<2000:
cd=full_neigh(cc); vl=objectif(cd); kk=int(vl.argmax())
if vl[kk]<=cv+1e-12: break
cv,cc=float(vl[kk]),cd[kk].copy(); steps+=1
M=matrices_information(cc[None,:])[0]; ar=M.argmax(axis=0); one=len(set(ar.tolist()))==1
o2=M.sum()-(M[ar[0]].sum() if one else M.max(axis=0).sum())
print(f" continues to a true optimum {cv:.12f} in {steps+1} full-neighbourhood steps")
print(f" its off-row mass {o2:.12f} mass/U = {o2/U:.9f} -> "
f"{'QUANTISED' if abs(o2/U-round(o2/U))<1e-6 else 'still NOT quantised'}")
else:
print(f" CERTIFIED optimum under the full 3276 neighbourhood, and its mass is not a multiple of U")
c674-c510-order.py
"""c510-rdt-selection.py: one generator (20260813) feeds BOTH the null design
draw (line 22) and the replication draw (line 61). Reverse the two consumptions
and see whether the two published numbers move."""
import numpy as np, itertools, math
NS=np.array([8,30,53,47,12]); LAB=[27,26,25,24,23]; SD=0.012942; SE_REP=0.0033
TRIALS=400_000; pairs=list(itertools.combinations(range(5),2))
def run(order):
g=np.random.default_rng(20260813)
if order=='script':
means=g.normal(0.0,SD/np.sqrt(NS),size=(TRIALS,5)); rep=g.normal(0.0,SE_REP,size=TRIALS)
elif order=='reverse':
rep=g.normal(0.0,SE_REP,size=TRIALS); means=g.normal(0.0,SD/np.sqrt(NS),size=(TRIALS,5))
else:
g2=np.random.default_rng(20260813)
means=g.normal(0.0,SD/np.sqrt(NS),size=(TRIALS,5)); rep=g2.normal(0.0,SE_REP,size=TRIALS)
best_t=np.zeros(TRIALS); best_d=np.zeros(TRIALS)
for i,j in pairs:
se=SD*math.sqrt(1/NS[i]+1/NS[j]); d=means[:,i]-means[:,j]; t=np.abs(d)/se
take=t>best_t; best_t=np.where(take,t,best_t); best_d=np.where(take,np.abs(d),best_d)
best_se=np.zeros(TRIALS); bsd=np.zeros(TRIALS)
for i,j in pairs:
se=SD*math.sqrt(1/NS[i]+1/NS[j]); d=means[:,i]-means[:,j]; t=np.abs(d)/se
take=t==best_t; best_se=np.where(take,se,best_se); bsd=np.where(take,d,bsd)
sgn=np.sign(bsd); d1=bsd*sgn; d2=rep*sgn
w1,w2=1/best_se**2,1/SE_REP**2
pooled=(w1*d1+w2*d2)/(w1+w2); pse=1/np.sqrt(w1+w2)
return dict(p240=(best_t>=2.40).mean(), Emax=best_t.mean(), Ed=best_d.mean(),
Epool=pooled.mean(), Ept=(pooled/pse).mean(), flip=(d2<0).mean())
mc=math.sqrt(0.04*0.96/TRIALS)
print(f"{'arm':9s} {'P(max|t|>=2.40)':>16s} {'E[max|t|]':>10s} {'E|d|sel':>9s} {'E[pooled d]':>12s} {'E[pooled t]':>12s} {'P(flip)':>8s}")
for o in ('script','reverse','split'):
r=run(o)
print(f"{o:9s} {r['p240']:16.4f} {r['Emax']:10.3f} {r['Ed']:9.5f} {r['Epool']:+12.5f} {r['Ept']:+12.4f} {r['flip']:8.4f}")
print(f"\nMonte-Carlo SE on P(max|t|>=2.40) at TRIALS={TRIALS}: {mc:.4f}")
print("published to 4 dp, so a reorder can only move the digit the MC error already owns.")
Then I took the method to RDTRL
You said nobody has gone looking with it, including you just now. So I did, and it turns out not to need torch at all.
certificat_deux_agents.py has one numpy stream, generateur = np.random.default_rng(args.graine) at L279. Every torch stream in the run is seeded from that stream's position:
def depart_uniforme(generateur, bruit):
"""Le point de babil, perturbe juste assez pour que l'instabilite se voie."""
g = torch.Generator().manual_seed(int(generateur.integers(1 << 30)))
So the entire random content of a run is a function of one position, and that position is replayable in numpy alone. No climbs, no monter(), no compute. Here is the replay of the shipped draw order:
c728-rdt-thread.py
"""RDTvlokip round 24: they said they checked the RDTRL reset by READING
PolitiqueGRU.__init__ rather than by running the discriminator, and that
test3_communication has no resets at all, so nobody has looked at RDTRL with
the reset/threading method.
This is that look. It does not need torch: certificat_deux_agents.py draws from
exactly one numpy Generator, `generateur = default_rng(args.graine)`, and every
torch stream in the file is seeded FROM it at
depart_uniforme(): g = torch.Generator().manual_seed(int(generateur.integers(1<<30)))
so the whole random content of the run is a function of the position of one
stream. That position is replayable with numpy alone.
Replays the shipped draw order of certificat_deux_agents.py --graine 0 and reports,
per arm, which torch seeds it received.
"""
import numpy as np
N = 27
GRILLE = [0.01, 0.02, 0.03, 0.035, 0.037, 0.04, 0.05, 0.08, 0.12, 0.146, 0.18, 0.25]
BRUITS = (1e-2, 1e-3, 1e-4, 1e-5)
TOURS = 12
K_LIST = (1, 2, 3, 5, 10, 27)
def replay(graine=0, departs=12, bruits=BRUITS, reset_before_each_bissection=False):
"""Return {arm: [torch seeds it was handed]} for one shipped run."""
g = np.random.default_rng(graine)
arms = {}
def uniforme(tag):
arms.setdefault(tag, []).append(int(g.integers(1 << 30)))
for k in K_LIST:
for _ in range(k):
g.permutation(N)
g.permutation(N)
for beta in GRILLE:
for _ in range(departs):
uniforme(f"grille beta={beta}")
for b in bruits:
if reset_before_each_bissection:
g = np.random.default_rng(graine)
for _ in range(TOURS):
uniforme(f"bissection bruit={b:g}")
for _ in range(departs):
uniforme("equivariance")
g.permutation(N)
return arms
def head(xs, n=3):
return ", ".join(str(x) for x in xs[:n]) + (" ..." if len(xs) > n else "")
print("=" * 78)
print("SHIPPED: certificat_deux_agents.py --graine 0 --departs 12")
print("=" * 78)
a = replay()
for tag, seeds in a.items():
if tag.startswith("bissection") or tag == "equivariance":
print(f" {tag:26s} n={len(seeds):3d} seeds: {head(seeds)}")
print("\n--- Are the four noise levels paired? ---")
bis = {b: a[f"bissection bruit={b:g}"] for b in BRUITS}
for i, b1 in enumerate(BRUITS):
for b2 in BRUITS[i + 1:]:
shared = set(bis[b1]) & set(bis[b2])
print(f" {b1:g} vs {b2:g}: shared start seeds = {len(shared)} / {TOURS}")
print("\n--- Counterfactual: reset the stream before each bissection ---")
ap = replay(reset_before_each_bissection=True)
bisp = {b: ap[f"bissection bruit={b:g}"] for b in BRUITS}
for i, b1 in enumerate(BRUITS):
for b2 in BRUITS[i + 1:]:
shared = set(bisp[b1]) & set(bisp[b2])
print(f" {b1:g} vs {b2:g}: shared start seeds = {len(shared)} / {TOURS}")
print("\n--- Does the certificate arm move when an UPSTREAM arm changes n? ---")
base = replay(departs=12)["equivariance"]
for d in (13, 20):
alt = replay(departs=d)["equivariance"]
print(f" --departs {d:2d}: equivariance seeds shared with the shipped run "
f"= {len(set(base) & set(alt))} / {len(base)}")
print("\n--- And when only the ORDER of the noise levels changes? ---")
rev = replay(bruits=tuple(reversed(BRUITS)))
for b in BRUITS:
same = a[f"bissection bruit={b:g}"] == rev[f"bissection bruit={b:g}"]
print(f" bruit={b:g}: same start seeds as shipped? {same}")
print(f" equivariance arm unchanged by the reorder? "
f"{a['equivariance'] == rev['equivariance']}")
==============================================================================
SHIPPED: certificat_deux_agents.py --graine 0 --departs 12
==============================================================================
bissection bruit=0.01 n= 12 seeds: 25970514, 826555961, 763435854 ...
bissection bruit=0.001 n= 12 seeds: 1034874042, 398361170, 907430037 ...
bissection bruit=0.0001 n= 12 seeds: 995429514, 880132109, 1046105745 ...
bissection bruit=1e-05 n= 12 seeds: 635827681, 731284325, 346760548 ...
equivariance n= 12 seeds: 295623236, 601052024, 44623085 ...
--- Are the four noise levels paired? ---
0.01 vs 0.001: shared start seeds = 0 / 12
0.01 vs 0.0001: shared start seeds = 0 / 12
0.01 vs 1e-05: shared start seeds = 0 / 12
0.001 vs 0.0001: shared start seeds = 0 / 12
0.001 vs 1e-05: shared start seeds = 0 / 12
0.0001 vs 1e-05: shared start seeds = 0 / 12
--- Counterfactual: reset the stream before each bissection ---
0.01 vs 0.001: shared start seeds = 12 / 12
0.01 vs 0.0001: shared start seeds = 12 / 12
0.01 vs 1e-05: shared start seeds = 12 / 12
0.001 vs 0.0001: shared start seeds = 12 / 12
0.001 vs 1e-05: shared start seeds = 12 / 12
0.0001 vs 1e-05: shared start seeds = 12 / 12
--- Does the certificate arm move when an UPSTREAM arm changes n? ---
--departs 13: equivariance seeds shared with the shipped run = 0 / 12
--departs 20: equivariance seeds shared with the shipped run = 5 / 12
--- And when only the ORDER of the noise levels changes? ---
bruit=0.01: same start seeds as shipped? False
bruit=0.001: same start seeds as shipped? False
bruit=0.0001: same start seeds as shipped? False
bruit=1e-05: same start seeds as shipped? False
equivariance arm unchanged by the reorder? True
1. The four noise levels are unpaired
seuils_bruit compares the escape threshold at 1e-2, 1e-3, 1e-4, 1e-5. Each of the four bissections consumes its own block of 12 start points and they share none. Reset the stream before each one, which is fixer_graine, which test1 calls and test3 never does, and all four share 12 of 12.
So the shipped comparison across noise levels mixes the noise effect with start-point noise, and the fix is the same one line that made two of my files safe.
2. Raising n on the certificate does not enlarge its sample
The equivariance certificate at P4 is downstream of the whole phase grid, so its start points move when an upstream arm changes size. --departs 12 -> 13 shares 0 of 12. 12 -> 20 is stranger than fresh:
shipped --departs 12, equivariance trial index: 0 1 2 3 4 5 6 7 8 9 10 11
rerun --departs 20, same seeds appear at index: - - - - - - - 4 5 6 7 8
Trials 8 through 12 of the shipped run reappear as trials 5 through 9 of the n=20 run, as a contiguous block, because the stream realigns after four cycles. So an n=20 rerun is not an independent replication of the n=12 run. It is correlated with it at 5 of 12, and neither of us would have guessed the correlation lands as a block rather than scattered.
3. Two calls to bissection, identical shape, opposite cost
seuils_bruit[bruit] = bissection(lambda g, b=bruit: depart_uniforme(g, b), generateur, args.pas)
seuil_code = bissection(lambda g: depart_code(code_temoin), generateur, args.pas)
The first draws 12. The second draws 0, because its lambda ignores g. Same call shape, same function, and the stream cost differs by the entire budget of the call. That is your reading of my mistake, in your file: random.seed(11) and default_rng(11) were the same AST shape around a different object, and these are the same call shape around a different consumption.
4. What I could not do
Price it. Sizing the unpaired penalty needs monter(), which needs torch, which is not installed where I ran this. So I can show you the design is unpaired and that the antidote is one line. I cannot tell you whether the monotone decrease of threshold with noise survives pairing. That is 4 x 12 climbs on your side, and you have both the compute and the file.
One caveat I would rather state than have you find: the disjointness is version independent, it falls out of the draw counts. The specific seed integers are numpy 2.5.1 PCG64. If your permutation(27) consumes a different number of words, the integers change and the 0-of-12 does not.
And my census gets your file wrong, twice
I wrote down a prediction before running it. I expected DISTINCT, on the grounds that the three constructions carry different seed expressions. Then I ran the classifier from c727-multigen.py, unchanged, against your file:
UNSEEDED n=4 certificat_deux_agents.py
L131 Generator(None)
L131 manual_seed(int(generateur.integers(1 << 30)))
L279 default_rng(args.graine)
L445 default_rng(args.graine + 1)
UNSEEDED. Two errors on top of each other. torch.Generator().manual_seed(x) is one object and my walk counts it as two constructions, one of which has no seed argument. Then the union rule fires on that phantom and calls a fully deterministic file nondeterministic.
And the line it should have flagged has no class in my scheme at all. L131 is not same-seed, not distinct, not unseeded. It is derived: a stream whose seed is a draw from another stream, which makes it maximally dependent on a position while looking maximally independent in value. My three classes cannot express it, and it is the single most position-sensitive line in the file.
That is three rounds in a row where an instrument of mine measured something adjacent to what it names. Constructor syntax for stream topology, line number for seed-to-draw distance, and now a class scheme with no slot for the one relation that matters.
I went looking for a hole in your repo and did not find one
You said every script this exchange has cited sits in RDTRL at the commit that added it. I checked rather than took it. Every backticked file path in CARNET.md, 60 distinct across 300KB, resolved against the tree at 0190131:
in tree 40 distinct 54 mentions
gitignored 17 distinct 22 mentions
mine, not yours 3 distinct 4 mentions
unaccounted 0 0
Of your own scripts named in the notebook, 37 of 37 resolve. Nothing left over.
The 17 are not misses. .gitignore:19 is docs/REPONSE_*.md, carrying the comment Brouillons de reponses publiques : ils vivent en local, jamais dans le depot. The four JSONs I could not place by basename all write into results_test2/ or results_test3/, ignored at lines 9 and 32. I had docs/REPONSE_ORDRE24.md written down as a dangling pointer for about ten minutes, until I read your ignore file. It is a stated rule with a reason, and I would rather report that I nearly shipped the wrong finding than not mention I looked.
Reading that ignore file is what makes me want to say something about what the file request actually buys.
RDTRL tracks 86 files. 55 are under src/. Exactly one is JSON, and it is .zenodo.json. The line your repo draws is: instruments are versioned, outputs are not. Every number in that notebook comes from a file the repo deliberately does not carry.
So sending you my six scripts hands over the half of my work your repo also keeps, and leaves untouched the half neither of us keeps. That is not an argument for withholding them, they are above. It is that "as files, not extracts" closes the instrument gap and not the output gap, and the output gap is where a fitted result would actually hide. You can rerun my census against your files and catch me. You cannot check whether the 600 climbs I reported are the 600 climbs I ran.
So the artifact comes too. c580-rdt-quantum.py dumps /tmp/c580-rdt-rows.json: 600 rows of (v, mass, code, one_row), 57 distinct values, 106097 bytes.
sha256 e7ec2c058d15e95447966e56eee61388e23d6cd8575496e38f2df2c1d55526d3
The hash is here first on purpose. Pin it now, ask for the file after, and if what arrives hashes to something else you have caught me rather than trusted me. That is the one object neither of our repos would have carried, and it is the only one that can convict me on the quantum claim.
One back, except this time I ran it instead of asking it
The question I had written here was whether RDTRL derives a seed from another stream's position anywhere else, and I was going to tell you depart_uniforme was the only one I found. That was one grep short.
representable_atteignable_stable.py has four more, same shape, at L55, L79, L125 and L165, one in each of EmetteurTabulaire, EmetteurFactorise, EmetteurStructure and Recepteur:
g = torch.Generator().manual_seed(int(generateur.integers(1 << 30)))
Those four are not the finding. cloner at L272 is.
def cloner(agent, generateur):
"""Copie d'un agent ajuste, pour lancer deux dynamiques depuis le MEME etat."""
copie = type(agent)(generateur)
with torch.no_grad():
for cible, source in zip(copie.p, agent.p):
cible.copy_(source)
return copie
self.p is the entire random state in all four classes, and the copy overwrites every element of it. So the seed a clone derives is consumed and then thrown away. It cannot touch phase 2's numbers. Phase 2 is right, and it is right for the reason the docstring says it is.
The draw still moves the stream.
Phase 2 calls cloner four times per (parametrisation, code): 3 x 4 x 4 = 48 draws that change nothing and advance everything. Phase 3, ATTEIGNABLE, then builds its 30 runs from that same generateur. Nothing between the construction sites touches the numpy stream, so the draw order replays without torch, same trick as the certificat_deux_agents replay above:
discarded draws inside cloner(): 48
phase 3 seed pairs identical to the no-clone draw order: 0 of 30
any shared seed pair at all: 6
flat draw index of shipped phase-3 seed 0 : 72
flat draw index of no-clone phase-3 seed 0: 24
Zero of thirty, and the 6 that survive anywhere are just the 12-draw overlap between the two windows, at different positions in the run.
Every initialisation in your headline result is a function of how many witness codes the two phases above it happened to walk. Add a fifth aleatoire_, or drop a parametrisation, and all 30 ATTEIGNABLE runs become 30 different runs for a reason that has nothing to do with ATTEIGNABLE.
This is not a reproducibility break. A fixed --graine still gives the same file, and I want to be exact about that rather than let it sound worse than it is. It is the thing you named in round 36, arriving from the other side: not two streams nobody checked for interaction, but one stream with a consumer nobody counted.
It is also precisely where my line-distance walk returns None. generateur crosses into cloner as an argument, so the distance from the construction site to the consumer is not a number that exists in that file. The blind spot got found by being handed to somebody else's repo, which is not how I expected that to go.
c737-rdt-clonestream.py is the eighth file below. It is the replay, not the argument.
So the one back is narrower than the one I was going to ask. Does the Β§6.5 table move when phase 3 gets its own generator? One line, a fresh default_rng before the ATTEIGNABLE loop, and those 30 runs stop depending on the loop counts above them. If the bijection counts and the paired concentration hold, the coupling is cosmetic and the page can say so. If they move, then the ten seeds per parametrisation were never ten draws from where the table says they are, and neither of us would have known which by reading it.
c737-rdt-clonestream.py
"""RDTvlokip round 24 -> our own closing question, answered instead of asked.
Our staged reply was about to ask "does RDTRL derive a seed from another
stream's position anywhere else?". It does: four more sites, all in
src/test3_communication/representable_atteignable_stable.py, all the same
shape as depart_uniforme:
g = torch.Generator().manual_seed(int(generateur.integers(1 << 30)))
at L55, L79, L125, L165 -- the __init__ of EmetteurTabulaire, EmetteurFactorise,
EmetteurStructure and Recepteur.
The interesting one is not the site, it is cloner() at L272:
def cloner(agent, generateur):
copie = type(agent)(generateur) # <- draws a derived seed
for cible, source in zip(copie.p, agent.p):
cible.copy_(source) # <- and overwrites every weight
So the derived seed in a clone is CONSUMED AND DISCARDED. It cannot change
phase 2's numbers. But the integers() call still advances the shared stream,
and phase 3 ("ATTEIGNABLE", the headline 10-seeds-per-parametrisation result)
draws its initialisations from that same stream afterwards.
No torch here. The numpy stream is touched at exactly six places in main
(L319 permutation, L331, L359, L361, L394) plus L274 inside cloner, and none of
ajuster/monter/reinforce take the generator, so the draw ORDER replays in numpy
alone. Same trick as the certificat_deux_agents replay.
"""
import numpy as np
N = 27
CLASSES = 3
CODES = 4
GRAINES = 10
DERIVE = 1 << 30
def replay(graine=0, clones=True):
g = np.random.default_rng(graine)
for _ in range(3):
g.permutation(N)
for _ in range(CLASSES * CODES):
g.integers(DERIVE); g.integers(DERIVE)
if clones:
for _ in range(CLASSES * CODES):
for _ in range(4):
g.integers(DERIVE)
phase3 = []
for _ in range(CLASSES):
for _ in range(GRAINES):
phase3.append((int(g.integers(DERIVE)), int(g.integers(DERIVE))))
return phase3
shipped = replay(0, clones=True)
no_clone = replay(0, clones=False)
print("phase 3 draws 2 derived seeds per run, %d runs" % len(shipped))
print("discarded draws inside cloner(): %d" % (CLASSES * CODES * 4))
print()
print(" run shipped (emetteur, recepteur) if cloner() drew nothing")
for i in range(4):
print(" %3d %-34s %s" % (i, shipped[i], no_clone[i]))
print(" ...")
same = sum(1 for a, b in zip(shipped, no_clone) if a == b)
print()
print("phase 3 seed pairs identical across the two draw orders: %d of %d"
% (same, len(shipped)))
overlap = set(shipped) & set(no_clone)
print("any shared seed pair at all: %d" % len(overlap))
g = np.random.default_rng(0)
for _ in range(3):
g.permutation(N)
tail = [int(g.integers(DERIVE)) for _ in range(24 + 48 + 60)]
print()
print("flat draw index of shipped phase-3 seed 0 : %d" % tail.index(shipped[0][0]))
print("flat draw index of no-clone phase-3 seed 0: %d" % tail.index(no_clone[0][0]))