Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions bench_e4b_multilayer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""
E4b — Does an INTERIOR fixed-width edit corrupt suffix K/V at layers >0? (the claude48/codex R6 RED)
Claim under test: "E4 proved equality only at layer 0; at L>0 the suffix attends to the edited
slot, so suffix hidden states (hence suffix K/V) change -> pointer-stable suffix reuse is NOT
exact for an INTERIOR edit. It is exact only for a TERMINAL edit (slot at end, no suffix)."

Method: REAL Qwen2.5-7B first N layers (QwenLayerN). Build a sequence of S tokens with a
fixed-width W-token slot. Full multi-layer prefill caches per-layer K/V for the ORIGINAL.
Then change the slot's token values (SAME width -> RoPE positions unchanged) and full-prefill
the EDITED sequence. Compare per-layer suffix K/V (tokens after the slot) between original and
edited. We do this for two slot positions:
OFF = S//2 (INTERIOR edit: there IS a suffix after the slot)
OFF = S - W (TERMINAL edit: NO suffix after the slot)
If interior suffix K/V diverge at L>=1 but terminal does not, the RED is CONFIRMED and the repair
must be scoped to terminal/append edits.

Reports, per layer, max|dK|,|dV| over SUFFIX tokens only, for interior vs terminal.
"""
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src"))
import torch
from decode_layer import QwenLayerN

DEV="cuda"
N = int(os.environ.get("E4B_LAYERS","6"))
S = int(os.environ.get("E4B_S","2048"))
W = int(os.environ.get("E4B_W","64"))
torch.manual_seed(0)

m = QwenLayerN(num_layers=N, device=DEV)
V = m.embed.shape[0]

@torch.no_grad()
def prefill(ids):
"""Full multi-layer prefill. Returns per-layer K,V tensors [n_kv, S, hd] (post-RoPE)."""
T=len(ids)
ids_t=torch.tensor(ids,device=DEV)
h=m.embed[ids_t] # [T, hidden]
pos=torch.arange(T,device=DEV).float().view(T,1)
Ks=[]; Vs=[]
for li in range(m.N):
Lw=m.L[li]
x=m._rmsnorm(h, Lw["ln1"])
q=(x@Lw["wq"].T+Lw["bq"]).view(T,m.n_q,m.hd)
k=(x@Lw["wk"].T+Lw["bk"]).view(T,m.n_kv,m.hd)
v=(x@Lw["wv"].T+Lw["bv"]).view(T,m.n_kv,m.hd)
# rope per-position
ang=pos*m.inv_freq # [T, hd/2]
cos=torch.cat([ang.cos(),ang.cos()],dim=-1)[:,None,:]
sin=torch.cat([ang.sin(),ang.sin()],dim=-1)[:,None,:]
def rope(t):
t1=t[...,:m.hd//2]; t2=t[...,m.hd//2:]
return (t.float()*cos+torch.cat([-t2,t1],dim=-1).float()*sin).to(t.dtype)
q=rope(q); k=rope(k)
# causal attention over full seq, GQA
rep=m.n_q//m.n_kv
Kf=k.permute(1,0,2).repeat_interleave(rep,dim=0) # [n_q,T,hd]
Vf=v.permute(1,0,2).repeat_interleave(rep,dim=0)
Qf=q.permute(1,0,2) # [n_q,T,hd]
o=torch.nn.functional.scaled_dot_product_attention(Qf,Kf,Vf,is_causal=True)
o=o.permute(1,0,2).reshape(T,m.n_q*m.hd)
h=h+(o@Lw["wo"].T)
xn=m._rmsnorm(h,Lw["ln2"])
gate=torch.nn.functional.silu((xn@Lw["gate"].T).float()).to(m.dtype)
up=xn@Lw["up"].T
h=h+((gate*up)@Lw["down"].T)
Ks.append(k.permute(1,0,2).contiguous()) # [n_kv,T,hd]
Vs.append(v.permute(1,0,2).contiguous())
return Ks,Vs

def run(off, label):
base=torch.randint(0,V,(S,)).tolist()
# carve a fixed-width slot
for i in range(W): base[off+i]= (1000+i)%V
edited=list(base)
for i in range(W): edited[off+i]= (2000+i)%V # new same-width values
K0,V0=prefill(base); K1,V1=prefill(edited)
print(f"\n=== {label}: OFF={off} (slot {off}..{off+W}), suffix=[{off+W}..{S}] ===")
print("| layer | suffix max|dK| | suffix max|dV| | slot max|dK| |")
print("|---|---|---|---|")
suf=slice(off+W,S)
for li in range(m.N):
dK_suf=(K0[li][:,suf,:]-K1[li][:,suf,:]).abs().max().item() if off+W<S else 0.0
dV_suf=(V0[li][:,suf,:]-V1[li][:,suf,:]).abs().max().item() if off+W<S else 0.0
dK_slot=(K0[li][:,off:off+W,:]-K1[li][:,off:off+W,:]).abs().max().item()
print(f"| {li} | {dK_suf:.4e} | {dV_suf:.4e} | {dK_slot:.4e} |")

print(f"E4b multi-layer suffix-divergence test: N={N} layers, S={S}, W={W}, Qwen2.5-7B")
run(S//2, "INTERIOR EDIT")
run(S-W, "TERMINAL EDIT")
116 changes: 116 additions & 0 deletions bench_e4c_terminal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
E4c — Rescoped repair: TERMINAL/APPEND tool-result injection, FULL multi-layer, exact + timed.
After E4b proved interior edits corrupt suffix K/V at L>0, T2's repair is rescoped to the TERMINAL
case (tool result appended at the live decode boundary — the standard agent pattern: model emits a
tool call, result is appended, generation continues). Here suffix=empty, so pointer-stable reuse of
the cached prefix K/V is EXACT at full depth. We measure the intervention:
B RECOMPUTE (stock): full N-layer prefill of prefix(S) + appended result(W) [hash chain broke]
C REPAIR (rescoped): reuse cached prefix(S) K/V at ALL layers, prefill ONLY the W appended tokens
Correctness: repaired final-token logits (argmax) vs full recompute. Sweep S; W=64.
"""
import sys, os, time
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src"))
import torch
from decode_layer import QwenLayerN
DEV="cuda"
N=int(os.environ.get("E4C_LAYERS","6")); W=int(os.environ.get("E4C_W","64"))
SLIST=[int(x) for x in os.environ.get("E4C_SLIST","2048,4096,8192,16384,32768").split(",")]
TR=int(os.environ.get("E4C_TRIALS","20"))
torch.manual_seed(0)
m=QwenLayerN(num_layers=N, device=DEV); Vsz=m.embed.shape[0]

@torch.no_grad()
def layers_prefill(ids, cacheKV=None, only_new_from=None):
"""If only_new_from is None: full prefill of all tokens, return per-layer (K,V) [n_kv,T,hd] + final logits.
Else: reuse cacheKV for tokens [0:only_new_from], compute only [only_new_from:T], appending
to cached K/V (exact iff those new tokens are TERMINAL — no earlier token attends to them)."""
T=len(ids); ids_t=torch.tensor(ids,device=DEV)
h=m.embed[ids_t]
start = 0 if only_new_from is None else only_new_from
pos=torch.arange(T,device=DEV).float().view(T,1)
Ks=[];Vs=[]
hcur=h
for li in range(m.N):
Lw=m.L[li]
x=m._rmsnorm(hcur, Lw["ln1"])
q=(x@Lw["wq"].T+Lw["bq"]).view(T,m.n_q,m.hd)
k=(x@Lw["wk"].T+Lw["bk"]).view(T,m.n_kv,m.hd)
v=(x@Lw["wv"].T+Lw["bv"]).view(T,m.n_kv,m.hd)
ang=pos*m.inv_freq
cos=torch.cat([ang.cos(),ang.cos()],dim=-1)[:,None,:]; sin=torch.cat([ang.sin(),ang.sin()],dim=-1)[:,None,:]
def rope(t):
t1=t[...,:m.hd//2]; t2=t[...,m.hd//2:]
return (t.float()*cos+torch.cat([-t2,t1],dim=-1).float()*sin).to(t.dtype)
q=rope(q); k=rope(k)
if only_new_from is not None and cacheKV is not None:
# overwrite cached prefix K/V for [0:start], keep new for [start:]
kc,vc=cacheKV[li]
k=torch.cat([kc, k.permute(1,0,2)[:,start:,:]],dim=1).permute(1,0,2) if False else k
rep=m.n_q//m.n_kv
Kf=k.permute(1,0,2).repeat_interleave(rep,dim=0); Vf=v.permute(1,0,2).repeat_interleave(rep,dim=0)
Qf=q.permute(1,0,2)
o=torch.nn.functional.scaled_dot_product_attention(Qf,Kf,Vf,is_causal=True).permute(1,0,2).reshape(T,m.n_q*m.hd)
hcur=hcur+(o@Lw["wo"].T)
xn=m._rmsnorm(hcur,Lw["ln2"]); gate=torch.nn.functional.silu((xn@Lw["gate"].T).float()).to(m.dtype); up=xn@Lw["up"].T
hcur=hcur+((gate*up)@Lw["down"].T)
Ks.append(k.permute(1,0,2).contiguous()); Vs.append(v.permute(1,0,2).contiguous())
logits=m.logits_of(hcur[-1])
return list(zip(Ks,Vs)), logits

# For the TERMINAL case, the exact repair = compute only the W new tokens' contribution. Because no
# earlier token attends to them, the prefix K/V AND prefix hidden states are unchanged at all layers.
# So repair = a W-token incremental prefill that attends to cached prefix K/V. We implement the
# stock recompute (B) and a true incremental (C) and check final-logit argmax equality + timing.

@torch.no_grad()
def incremental(prefix_ids, new_ids, cache):
"""Exact terminal append: for each layer, compute K/V for the W new tokens, attend over
[cached prefix K/V ++ new K/V], propagate hidden state for the new tokens only."""
S=len(prefix_ids); T=S+len(new_ids)
ids_t=torch.tensor(prefix_ids+new_ids,device=DEV)
pos=torch.arange(T,device=DEV).float().view(T,1)
hnew=m.embed[ids_t[S:]] # [W,hidden] hidden state for new tokens entering layer 0
posnew=pos[S:]
for li in range(m.N):
Lw=m.L[li]; kc,vc=cache[li] # [n_kv,S,hd]
x=m._rmsnorm(hnew,Lw["ln1"])
q=(x@Lw["wq"].T+Lw["bq"]).view(-1,m.n_q,m.hd)
k=(x@Lw["wk"].T+Lw["bk"]).view(-1,m.n_kv,m.hd)
v=(x@Lw["wv"].T+Lw["bv"]).view(-1,m.n_kv,m.hd)
angn=posnew*m.inv_freq; cosn=torch.cat([angn.cos(),angn.cos()],dim=-1)[:,None,:]; sinn=torch.cat([angn.sin(),angn.sin()],dim=-1)[:,None,:]
def rope(t,c,s):
t1=t[...,:m.hd//2];t2=t[...,m.hd//2:]; return (t.float()*c+torch.cat([-t2,t1],dim=-1).float()*s).to(t.dtype)
q=rope(q,cosn,sinn); k=rope(k,cosn,sinn)
rep=m.n_q//m.n_kv
Kfull=torch.cat([kc, k.permute(1,0,2)],dim=1).repeat_interleave(rep,dim=0) # [n_q, S+W, hd]
Vfull=torch.cat([vc, v.permute(1,0,2)],dim=1).repeat_interleave(rep,dim=0)
Qn=q.permute(1,0,2) # [n_q, W, hd]
# causal mask for the W new queries over S+W keys
Wn=q.shape[0]
mask=torch.full((Wn,S+Wn),float('-inf'),device=DEV);
for i in range(Wn): mask[i,:S+i+1]=0.0
o=torch.nn.functional.scaled_dot_product_attention(Qn,Kfull,Vfull,attn_mask=mask).permute(1,0,2).reshape(Wn,m.n_q*m.hd)
hnew=hnew+(o@Lw["wo"].T)
xn=m._rmsnorm(hnew,Lw["ln2"]); gate=torch.nn.functional.silu((xn@Lw["gate"].T).float()).to(m.dtype); up=xn@Lw["up"].T
hnew=hnew+((gate*up)@Lw["down"].T)
return m.logits_of(hnew[-1])

print(f"E4c TERMINAL repair (rescoped, exact): N={N} layers, W={W}, Qwen2.5-7B")
print("| S | B recompute (ms) | C repair (ms) | C/B | speedup | argmax match |")
print("|---|---|---|---|---|---|")
for S in SLIST:
prefix=torch.randint(0,Vsz,(S,)).tolist(); new=torch.randint(0,Vsz,(W,)).tolist()
cache,_=layers_prefill(prefix) # cache prefix K/V (the "still-mapped pages")
# B: full recompute of prefix+new
full=prefix+new
for _ in range(3): _,lb=layers_prefill(full)
torch.cuda.synchronize(); t0=time.perf_counter()
for _ in range(TR): _,lb=layers_prefill(full)
torch.cuda.synchronize(); tb=(time.perf_counter()-t0)/TR*1000
# C: incremental terminal repair
for _ in range(3): lc=incremental(prefix,new,cache)
torch.cuda.synchronize(); t0=time.perf_counter()
for _ in range(TR): lc=incremental(prefix,new,cache)
torch.cuda.synchronize(); tc=(time.perf_counter()-t0)/TR*1000
match = (lb.argmax().item()==lc.argmax().item())
print(f"| {S} | {tb:.3f} | {tc:.3f} | {tc/tb:.3f} | {tb/tc:.1f}x | {match} |")
118 changes: 118 additions & 0 deletions bench_e6_kv_divergence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
E6 — T3 core measurement: does TOKEN-prefix sharing (what RadixAttention exploits) OVERSTATE the
true KV-level sharing opportunity in REAL agent branch families, and is the gap NON-TRIVIAL +
LAYER-DEPENDENT (not just 1-(tail/total))?

Tests both T3 YELLOW objections:
- Muse Park "trivial 1-(tail/total)": if token-share != KV-share and the gap grows with layer
depth, the model is NOT a trivial token ratio.
- claude48 "RadixAttention already does dynamic sharing / no delta": RadixAttention shares on
token-prefix match. If true KV diverges EARLIER than tokens do, RadixAttention OVER-shares
(reuses KV that is actually stale at depth) OR the real reusable fraction is smaller -> a
measurable a-priori-divergence model beats reactive token matching.

Method: REAL Qwen2.5-7B (N layers). For each real agent branch pair (two sibling sessions sharing
an opening prefix), tokenize both, find the token-level common-prefix length Ltok. Then run both
through the model and, per layer, find the KV-level common-prefix length Lkv(layer) = #leading
positions whose K AND V match within fp16 tol. Report Lkv(layer)/Ltok: if <1 and decreasing with
layer, token-sharing overstates KV-sharing by a layer-dependent amount = the non-trivial structure.
"""
import sys, os, json, glob
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src"))
import torch
from decode_layer import QwenLayerN
from transformers import AutoTokenizer

DEV="cuda"; N=int(os.environ.get("E6_LAYERS","6")); MAXTOK=int(os.environ.get("E6_MAXTOK","1024"))
TOL=float(os.environ.get("E6_TOL","5e-3")); MAXPAIRS=int(os.environ.get("E6_MAXPAIRS","12"))
SNAP=os.path.expanduser("~/.cache/huggingface/hub/models--Qwen--Qwen2.5-7B-Instruct/snapshots")
SNAP=os.path.join(SNAP, os.listdir(SNAP)[0])
tok=AutoTokenizer.from_pretrained(SNAP, trust_remote_code=True)
m=QwenLayerN(num_layers=N, device=DEV)

@torch.no_grad()
def perlayer_kv(ids):
T=len(ids); ids_t=torch.tensor(ids,device=DEV); h=m.embed[ids_t]
pos=torch.arange(T,device=DEV).float().view(T,1)
Ks=[];Vs=[]
for li in range(m.N):
Lw=m.L[li]; x=m._rmsnorm(h,Lw["ln1"])
q=(x@Lw["wq"].T+Lw["bq"]).view(T,m.n_q,m.hd); k=(x@Lw["wk"].T+Lw["bk"]).view(T,m.n_kv,m.hd); v=(x@Lw["wv"].T+Lw["bv"]).view(T,m.n_kv,m.hd)
ang=pos*m.inv_freq; cos=torch.cat([ang.cos(),ang.cos()],dim=-1)[:,None,:]; sin=torch.cat([ang.sin(),ang.sin()],dim=-1)[:,None,:]
def rope(t):
t1=t[...,:m.hd//2];t2=t[...,m.hd//2:]; return (t.float()*cos+torch.cat([-t2,t1],dim=-1).float()*sin).to(t.dtype)
q=rope(q);k=rope(k); rep=m.n_q//m.n_kv
Kf=k.permute(1,0,2).repeat_interleave(rep,dim=0);Vf=v.permute(1,0,2).repeat_interleave(rep,dim=0);Qf=q.permute(1,0,2)
o=torch.nn.functional.scaled_dot_product_attention(Qf,Kf,Vf,is_causal=True).permute(1,0,2).reshape(T,m.n_q*m.hd)
h=h+(o@Lw["wo"].T); xn=m._rmsnorm(h,Lw["ln2"]); gate=torch.nn.functional.silu((xn@Lw["gate"].T).float()).to(m.dtype); up=xn@Lw["up"].T; h=h+((gate*up)@Lw["down"].T)
Ks.append(k.permute(1,0,2).contiguous());Vs.append(v.permute(1,0,2).contiguous())
return Ks,Vs

def kv_common_prefix(Ka,Va,Kb,Vb,Ltok):
"""#leading positions (up to Ltok) where K&V match within TOL, at this layer."""
n=min(Ka.shape[1],Kb.shape[1],Ltok)
dK=(Ka[:,:n,:]-Kb[:,:n,:]).abs().amax(dim=(0,2)) # [n]
dV=(Va[:,:n,:]-Vb[:,:n,:]).abs().amax(dim=(0,2))
bad=((dK>TOL)|(dV>TOL)).nonzero()
return (bad[0,0].item() if bad.numel()>0 else n)

def first_user_text(path):
with open(path) as f:
for line in f:
try:d=json.loads(line)
except:continue
if d.get("type")=="user":
c=d.get("message",{}).get("content")
if isinstance(c,str) and len(c)>50: return c
if isinstance(c,list):
for b in c:
if isinstance(b,dict) and b.get("type")=="text" and len(b.get("text",""))>50: return b["text"]
return None
def full_text(path,limit=8000):
out=[]
with open(path) as f:
for line in f:
try:d=json.loads(line)
except:continue
if d.get("type") in ("user","assistant"):
c=d.get("message",{}).get("content")
if isinstance(c,str): out.append(c)
elif isinstance(c,list):
for b in c:
if isinstance(b,dict): out.append(b.get("text") or b.get("thinking") or json.dumps(b.get("input",b.get("content","")))[:500])
if sum(len(x) for x in out)>limit: break
return "\n".join(out)

# build branch pairs
files=sorted(glob.glob(os.path.expanduser("~/.claude/projects/**/*.jsonl"),recursive=True))
from collections import defaultdict
fam=defaultdict(list)
for f in files[:325]:
t=first_user_text(f)
if t: fam[t[:200]].append(f)
pairs=[]
for k,v in fam.items():
if len(v)>=2:
for i in range(min(len(v)-1,3)):
pairs.append((v[i],v[i+1]))
pairs=pairs[:MAXPAIRS]
print(f"E6: {len(pairs)} real agent branch pairs, Qwen2.5-7B {N} layers, max {MAXTOK} tok, tol {TOL}")
print("| pair | Ltok (token-shared) | Lkv@L0 | Lkv@Lmid | Lkv@Llast | KVshare/tokshare@last |")
print("|---|---|---|---|---|---|")
import statistics
ratios=[]
for idx,(fa,fb) in enumerate(pairs):
ta=tok.encode(full_text(fa))[:MAXTOK]; tb=tok.encode(full_text(fb))[:MAXTOK]
# token common prefix
Ltok=0
for x,y in zip(ta,tb):
if x==y: Ltok+=1
else: break
if Ltok<8: continue
Ka,Va=perlayer_kv(ta); Kb,Vb=perlayer_kv(tb)
lkv=[kv_common_prefix(Ka[li],Va[li],Kb[li],Vb[li],Ltok) for li in range(m.N)]
last_ratio=lkv[-1]/Ltok if Ltok else 0
ratios.append(last_ratio)
print(f"| {idx} | {Ltok} | {lkv[0]} | {lkv[m.N//2]} | {lkv[-1]} | {last_ratio:.3f} |")
if ratios:
print(f"\nmedian KVshare/tokshare at last layer: {statistics.median(ratios):.3f} (1.0 = token-share fully predicts KV-share; <1 = token-share OVERSTATES reusable KV)")
Loading