LittlePlyResearch notebook

build_curriculum.py

python · 99 lines · Exact bytes

Line 1 ↗
Version & provenance · data-cm7

Recorded SHA-256 f4f6a8bcbd4f3b39090fe74fb2b85da229aaac6fa879e1266c5daec077c42c2bRendered SHA-256 f4f6a8bcbd4f3b39090fe74fb2b85da229aaac6fa879e1266c5daec077c42c2b

Apache-2.0 original project code; dependencies retain their own licenses. License notes. Line links are stable within this snapshot. CM-7 completed implementation source; original and public rendered hashes recorded. Host-specific paths use /work placeholders; inspect reproduction instructions before running. Separate Rust helper retains its GPL license.

1"""Bounded curriculum from sealed records; no training, no raw-data copies."""
2import collections,json,hashlib,shutil,time
3from pathlib import Path
4from inventory_inputs import ROOT,STATE,c,protection,sha
5from records import DTYPE,canonical,digest,atomic_json
6import numpy as np
7
8OUT=ROOT/'curriculum-v2'
9def phase(rows):
10    pieces=np.count_nonzero(rows['x'][:,51:115]!=128,axis=1)
11    return np.where(pieces<=10,2,np.where(rows['ply']<16,0,1)).astype(np.uint8)
12def guard(extra=0):
13    for p in (ROOT,Path(__file__).parent):
14        if shutil.disk_usage(p).free<50_000_000_000+extra:raise RuntimeError('free-space reserve')
15def inventory():
16    guard(6_000_000_000);db,_=protection();db.close()
17    m=json.loads((STATE/'manifest.json').read_text());stats=collections.Counter();nonpad=0
18    for f in m['files']:
19        p=STATE/f['name']
20        if sha(p)!=f['sha256']:raise ValueError('source hash')
21        rows=np.memmap(p,dtype=DTYPE,mode='r')
22        if len(rows)!=f['rows']:raise ValueError('source length')
23        if f['split']!=0:continue
24        for k,v in collections.Counter(phase(rows).tolist()).items():stats[f"{f['source']}:{k}"]+=v
25        nonpad+=int(rows['attention'].sum())
26    result=dict(source_manifest_sha256=sha(STATE/'manifest.json'),phase_counts=dict(stats),phase_ids={'0':'opening','1':'middlegame','2':'endgame <=10 pieces'},nonpadding_input_slots=nonpad)
27    Path(__file__).with_name('sealed-phase-inventory.json').write_text(json.dumps(result,indent=2));print(json.dumps(result),flush=True)
28def build():
29    from curriculum_loader import SCHEDULE_DTYPE
30    theory=ROOT/'theory-v3'
31    tm=json.loads((theory/'manifest.json').read_text())
32    if tm['label_readiness']!='independently_reviewed_ready':raise ValueError('theory review required')
33    if OUT.exists():raise ValueError('refuse overwrite')
34    guard(300_000_000);OUT.mkdir()
35    metadata=[json.loads(x) for x in (theory/'metadata.jsonl').read_text().splitlines()]
36    keys=np.array([bytes.fromhex(x['position_key']) for x in metadata],dtype='V32')
37    m=json.loads((STATE/'manifest.json').read_text());rng=np.random.default_rng(20261029)
38    ids={0:[],1:[]};offset={0:0,1:0};phases={0:[],1:[]};nonpad={0:[],1:[]}
39    for f in m['files']:
40        if f['split']!=0:continue
41        src=f['source'];rows=np.memmap(STATE/f['name'],dtype=DTYPE,mode='r')
42        keep=~np.isin(np.ascontiguousarray(rows['key']).view('V32').ravel(),keys)
43        ids[src].append(np.arange(offset[src],offset[src]+len(rows),dtype=np.uint32)[keep]);offset[src]+=len(rows)
44        phases[src].append(phase(rows)[keep]);nonpad[src].append(rows['attention'].sum(axis=1,dtype=np.uint16)[keep])
45    ids={s:np.concatenate(ids[s]) for s in (0,1)};n=min(map(len,ids.values()));stats={};input_nonpad=0;selected_phases={}
46    for src in (0,1):
47        p=np.concatenate(phases[src]);npad=np.concatenate(nonpad[src]);perm=rng.permutation(len(ids[src]))[:n]
48        ids[src]=ids[src][perm];stats[str(src)]={str(k):int(v) for k,v in collections.Counter(p[perm].tolist()).items()};input_nonpad+=int(npad[perm].sum())
49        selected_phases[src]=p[perm]
50        ids[src].tofile(OUT/f'ordinary-source{src}-indices.bin')
51    theory_n=tm['rows'];total=2*n+3*theory_n
52    schedule=np.memmap(OUT/'schedule.bin',dtype=SCHEDULE_DTYPE,mode='w+',shape=(total,));is_theory=np.zeros(total,dtype=bool)
53    theory_ids=np.concatenate([np.arange(f['rows'],dtype=np.uint32) for f in tm['files']]);theory_src=np.concatenate([np.full(f['rows'],f['source'],dtype=np.uint8) for f in tm['files']])
54    for exp in range(3):
55        lo=exp*total//3;hi=(exp+1)*total//3
56        places=lo+((np.arange(theory_n,dtype=np.int64)+1)*(hi-lo)//(theory_n+1))
57        perm=rng.permutation(theory_n);is_theory[places]=True
58        schedule['kind'][places]=1;schedule['source'][places]=theory_src[perm];schedule['row'][places]=theory_ids[perm];schedule['exposure'][places]=exp
59    ordinary_places=np.flatnonzero(~is_theory)
60    assert len(ordinary_places)==2*n
61    for src in (0,1):
62        places=ordinary_places[src::2];schedule['kind'][places]=0;schedule['source'][places]=src;schedule['row'][places]=ids[src];schedule['exposure'][places]=0
63    schedule.flush();del schedule
64    unique=2*n+theory_n;exposed=total
65    tags=np.memmap(OUT/'state-tags.bin',dtype='u1',mode='w+',shape=(unique,3))
66    for src in (0,1):tags[src*n:(src+1)*n,0]=src;tags[src*n:(src+1)*n,1]=selected_phases[src];tags[src*n:(src+1)*n,2]=0
67    tags[2*n:,0]=theory_src;tags[2*n:,1]=2;tags[2*n:,2]=2;tags.flush();del tags
68    stateids=np.memmap(OUT/'schedule-state-ids.bin',dtype='<u4',mode='w+',shape=(total,))
69    schedule=np.memmap(OUT/'schedule.bin',dtype=SCHEDULE_DTYPE,mode='r')
70    for src in (0,1):
71        mask=(schedule['kind']==0)&(schedule['source']==src);lookup=np.full(offset[src],2**32-1,dtype=np.uint32);lookup[ids[src]]=np.arange(src*n,(src+1)*n,dtype=np.uint32);stateids[mask]=lookup[schedule['row'][mask]]
72        mask=(schedule['kind']==1)&(schedule['source']==src);base=2*n+sum(f['rows'] for f in tm['files'] if f['source']<src);stateids[mask]=base+schedule['row'][mask]
73    stateids.flush();del stateids;del schedule
74    manifest=dict(format='littleply-indexed-curriculum-v1',readiness='pending_independent_review',roots=dict(ordinary=str(STATE),theory=str(theory)),dependencies=[dict(path=str(p),sha256=sha(p)) for p in [STATE/'manifest.json',theory/'manifest.json',theory/'metadata.jsonl',Path(__file__).with_name('curriculum_loader.py')]],schedule_dtype=SCHEDULE_DTYPE.descr,schedule_sha256=sha(OUT/'schedule.bin'),schedule_entries=total,ordinary_per_source=n,theory_unique=theory_n,theory_exposures=3,unique_decisions=unique,unique_serialized_slots=123*unique,unique_input_slots=122*unique,unique_supervised_targets=3*unique,unique_nonpadding_input_slots=input_nonpad+74*theory_n,unique_nonpadding_serialized_slots=input_nonpad+74*theory_n+unique,exposure_serialized_slots=123*exposed,exposure_input_slots=122*exposed,exposure_supervised_targets=3*exposed,exposure_nonpadding_input_slots=input_nonpad+74*theory_n*3,phase_counts=stats,phase_ids={'0':'opening >10pieces ply<16','1':'middlegame >10pieces ply>=16','2':'endgame <=10pieces'},phase_adjustment='retain available unique ordinary states; no duplication to fill15/60/25; equal sources after theory ownership exclusions',theory_spacing='one exposure in each global third, evenly spaced; distinct shuffled order; ordinarysource0/1alternate in remaining slots',tie_policy='exposure index modulo complete tied triples; update x[-2:] and y together',seed=20261029,validation_root=str(STATE),validation_split=1,heldout_journal=str(STATE/'journal.sqlite'),opening_verified_rows=0,ordinary_dropped_for_source_equality=abs(len(np.concatenate([np.array([],dtype=np.uint32)]))) if False else offset[0]+offset[1]-2*n-theory_n,estimated_hours_at_992=exposed/992/3600,training_launched=False)
75    assert unique*123<=2_000_000_000 and theory_n*123<=200_000_000
76    manifest['dependencies'].append(dict(path=str(Path(__file__)),sha256=sha(Path(__file__))))
77    from inventory_inputs import PROJECT
78    manifest['dependencies'].extend(dict(path=str(p),sha256=sha(p)) for p in [PROJECT/'state-v2'/'records.py',PROJECT/'state-v2'/'runtime_records.py',PROJECT/'state-v2'/'codec.py'])
79    manifest['sidecars']={name:dict(sha256=sha(OUT/name),bytes=(OUT/name).stat().st_size) for name in ('state-tags.bin','schedule-state-ids.bin','ordinary-source0-indices.bin','ordinary-source1-indices.bin')}
80    manifest['dense_state_id']='ordinary source0 selected-index order, then source1, then theory source0/source1 local row order'
81    manifest['state_tags_columns']=['source_u8','phase_u8','label_kind_u8'];manifest['label_kinds']={'0':'ordinary','1':'verified_opening','2':'exact_tablebase'}
82    validation=[]
83    for f in m['files']:
84        if f['split']!=1:continue
85        rows=np.memmap(STATE/f['name'],dtype=DTYPE,mode='r');v=np.zeros((len(rows),3),dtype='u1');v[:,0]=f['source'];v[:,1]=phase(rows);validation.append(v)
86    validation=np.concatenate(validation);validation.tofile(OUT/'validation-tags.bin')
87    manifest['validation_rows']=len(validation);manifest['validation_tag_order']='source manifest file order then row order, split1 only'
88    manifest['validation_phase_counts']={str(src):dict((str(k),int(v)) for k,v in collections.Counter(validation[validation[:,0]==src,1].tolist()).items()) for src in (0,1)}
89    manifest['sidecars']['validation-tags.bin']=dict(sha256=sha(OUT/'validation-tags.bin'),bytes=(OUT/'validation-tags.bin').stat().st_size)
90    manifest['ordinary_shuffle']='uniform deterministic permutation within each source, preserving measured available phase mix'
91    manifest['final_partial_global_batch']='drop without replacement or padding; report actual consumed schedule entries'
92    manifest['global_batch_512_usable_entries']=total//512*512
93    manifest['global_batch_512_dropped_tail']=total%512
94    atomic_json(OUT/'manifest.json',manifest);print(json.dumps(manifest),flush=True)
95
96if __name__=='__main__':
97    import sys
98    build() if '--build' in sys.argv else inventory()
99