Skip to content

Commit b9eb653

Browse files
committed
trace ace: add V102 applicability resolution audit
1 parent 55e8955 commit b9eb653

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env python3
2+
"""V102: locate the resolution at which RELATED applicability actually lives.
3+
4+
Diagnostic only. Uses untouched outer objective-cold folds to generate V75/RELATED
5+
predictions, then measures label-informed oracle blend ceilings at progressively finer
6+
groupings. This does NOT define a deployable router; it decides what unit V103 should model.
7+
"""
8+
from __future__ import annotations
9+
import argparse, json
10+
from pathlib import Path
11+
import numpy as np
12+
from sklearn.linear_model import LogisticRegression
13+
from sklearn.metrics import log_loss
14+
from v71_mastery_events import load_transcript, tokens
15+
from v75_canonical_trajectory import load_training, SEED
16+
from v85_evidence_state import build_v75
17+
from v93_shift_robust_validation import folds_from_groups
18+
from v94_related_control import segmented_control, build_control
19+
20+
EPS=1e-5
21+
GRID=np.array([0.,.15,.25,.35,.45,.60,1.0])
22+
23+
def fit(X,y,tr,va):
24+
m=LogisticRegression(C=.25,max_iter=300,solver='liblinear',random_state=SEED).fit(X[tr],y[tr])
25+
return np.clip(m.predict_proba(X[va])[:,1],EPS,1-EPS)
26+
27+
def ll(y,p): return float(log_loss(y,np.clip(p,EPS,1-EPS),labels=[0,1]))
28+
29+
def oracle_group(y,p0,pr,groups):
30+
q=np.empty(len(y)); ws=[]
31+
for g in np.unique(groups):
32+
ix=np.where(groups==g)[0]; best=(1e99,0.,None)
33+
for w in GRID:
34+
z=(1-w)*p0[ix]+w*pr[ix]; v=ll(y[ix],z)
35+
if v<best[0]: best=(v,float(w),z)
36+
q[ix]=best[2]; ws.append(best[1])
37+
return ll(y,q), float(np.mean(ws)), len(ws)
38+
39+
def family(s):
40+
t=tokens(str(s))
41+
return ' '.join(t[:3]) if t else ''
42+
43+
def run(a):
44+
f=load_training(a.features,a.labels).reset_index(drop=True)
45+
cache={sid:load_transcript(a.transcripts/f'{sid}.csv') for sid in f.session_id.astype(str).unique()}
46+
rt=[]; rz=[]
47+
for i,r in f.iterrows():
48+
t,z=segmented_control(cache[str(r.session_id)],str(r.learning_objective),'related'); rt.append(t); rz.append(z)
49+
if (i+1)%2500==0: print('rows',i+1)
50+
X0=build_v75(f,cache); Xr=build_control(rt,rz); y=f.target.to_numpy(int)
51+
obj=(f.learning_objective_id if 'learning_objective_id' in f else f.learning_objective).astype(str).to_numpy()
52+
sess=f.session_id.astype(str).to_numpy(); fam=np.asarray([family(x) for x in f.learning_objective.astype(str)])
53+
p0=np.zeros(len(y)); pr=np.zeros(len(y))
54+
for k,(tr,va) in enumerate(folds_from_groups(obj),1):
55+
p0[va]=fit(X0,y,tr,va); pr[va]=fit(Xr,y,tr,va); print('fold',k)
56+
v75=ll(y,p0); v97=ll(y,.65*p0+.35*pr)
57+
row_loss=np.minimum(-(y*np.log(p0)+(1-y)*np.log(1-p0)),-(y*np.log(pr)+(1-y)*np.log(1-pr)))
58+
row_oracle=float(np.mean(row_loss))
59+
levels={}
60+
for name,g in [('family',fam),('objective',obj),('session',sess),('session_objective',np.char.add(np.char.add(sess,'|'),obj))]:
61+
val,mw,n=oracle_group(y,p0,pr,g); levels[name]={'oracle_ll':val,'gain_vs_v97':v97-val,'mean_best_weight':mw,'groups':n}
62+
# global best fixed weight ceiling
63+
best=min((ll(y,(1-w)*p0+w*pr),float(w)) for w in GRID)
64+
out={'v75':v75,'v97':v97,'global_grid_oracle':{'ll':best[0],'weight':best[1],'gain_vs_v97':v97-best[0]},
65+
'levels':levels,'row_endpoint_oracle':{'ll':row_oracle,'gain_vs_v97':v97-row_oracle},
66+
'decision_rule':'Choose the coarsest grouping whose oracle recovers a material fraction of the row endpoint oracle; V103 must model that unit using runtime-visible unlabeled features only.'}
67+
Path(a.out).write_text(json.dumps(out,indent=2)); print(json.dumps(out,indent=2))
68+
if __name__=='__main__':
69+
p=argparse.ArgumentParser(); p.add_argument('--features',type=Path,required=True); p.add_argument('--labels',type=Path,required=True); p.add_argument('--transcripts',type=Path,required=True); p.add_argument('--out',default='v102_applicability_resolution.json'); run(p.parse_args())

0 commit comments

Comments
 (0)