morphtree 2.1.0__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. morphtree/__init__.py +28 -0
  2. morphtree/__main__.py +3 -0
  3. morphtree/_experimental/__init__.py +1 -0
  4. morphtree/_experimental/admission.py +364 -0
  5. morphtree/_experimental/base.py +215 -0
  6. morphtree/_experimental/component_cache_v2.py +263 -0
  7. morphtree/_experimental/evolution_report.py +307 -0
  8. morphtree/_experimental/execution_v2.py +697 -0
  9. morphtree/_experimental/facade.py +289 -0
  10. morphtree/_experimental/integrated.py +748 -0
  11. morphtree/_experimental/learning.py +418 -0
  12. morphtree/_experimental/manifest.json +204 -0
  13. morphtree/_experimental/numeric_fixture.py +21 -0
  14. morphtree/_experimental/parameter_contract.py +38 -0
  15. morphtree/_experimental/postfit.py +51 -0
  16. morphtree/_experimental/preprocessing.py +133 -0
  17. morphtree/_experimental/processes.py +191 -0
  18. morphtree/_experimental/producer.py +542 -0
  19. morphtree/_experimental/product.py +156 -0
  20. morphtree/_experimental/product_cli.py +43 -0
  21. morphtree/_experimental/roundtrip_v3.py +311 -0
  22. morphtree/_experimental/runtime_io.py +65 -0
  23. morphtree/_experimental/sampling.py +26 -0
  24. morphtree/_experimental/support.py +281 -0
  25. morphtree/_experimental/target.py +59 -0
  26. morphtree/_experimental/training.py +53 -0
  27. morphtree/_experimental/worker.py +682 -0
  28. morphtree/_installed.py +17 -0
  29. morphtree/_native/__init__.py +1 -0
  30. morphtree/_native/bin/win_amd64/morphtree_kernels.dll +0 -0
  31. morphtree/_native/buffers.py +82 -0
  32. morphtree/_native/loader.py +39 -0
  33. morphtree/_native/manifest.json +29 -0
  34. morphtree/_native/runtime.py +48 -0
  35. morphtree/_native/src/histogram.c +61 -0
  36. morphtree/_native/src/traversal.c +51 -0
  37. morphtree/api.py +388 -0
  38. morphtree/api_compat.py +65 -0
  39. morphtree/backend.py +162 -0
  40. morphtree/booster.py +252 -0
  41. morphtree/cli.py +52 -0
  42. morphtree/config.py +262 -0
  43. morphtree/control.py +20 -0
  44. morphtree/data.py +300 -0
  45. morphtree/diagnostics.py +158 -0
  46. morphtree/estimators.py +156 -0
  47. morphtree/examples.py +25 -0
  48. morphtree/exporting.py +224 -0
  49. morphtree/legacy_artifacts.py +32 -0
  50. morphtree/metrics.py +156 -0
  51. morphtree/model.py +1010 -0
  52. morphtree/mutations.py +205 -0
  53. morphtree/native_lab_backend.py +79 -0
  54. morphtree/numpy_backend.py +180 -0
  55. morphtree/optuna_tuner.py +154 -0
  56. morphtree/persistence.py +414 -0
  57. morphtree/prediction_output.py +35 -0
  58. morphtree/prepared_bins.py +82 -0
  59. morphtree/public.py +106 -0
  60. morphtree/resource_plan.py +170 -0
  61. morphtree/scalable.py +781 -0
  62. morphtree/scalable_backend.py +74 -0
  63. morphtree/scalable_checkpoint.py +129 -0
  64. morphtree/scalable_data.py +255 -0
  65. morphtree/scalable_receipts.py +92 -0
  66. morphtree/scalable_target.py +68 -0
  67. morphtree/scalable_variants.py +133 -0
  68. morphtree/sklearn_view.py +79 -0
  69. morphtree/source_markers.py +25 -0
  70. morphtree/species.py +47 -0
  71. morphtree/temporary.py +65 -0
  72. morphtree/training_fragment.py +130 -0
  73. morphtree/tree.py +231 -0
  74. morphtree/tuning.py +150 -0
  75. morphtree-2.1.0.dist-info/METADATA +212 -0
  76. morphtree-2.1.0.dist-info/RECORD +79 -0
  77. morphtree-2.1.0.dist-info/WHEEL +4 -0
  78. morphtree-2.1.0.dist-info/entry_points.txt +2 -0
  79. morphtree-2.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,263 @@
1
+ """Version 2 research cache; preserves v1 source and fixes Pure backend spelling.
2
+
3
+ The caller must build identity from actual ordered inputs. A complete result is
4
+ immutable; uncertain jobs require reconciliation. OS leases prevent equal-key
5
+ concurrent computations. This is not a remote/untrusted model execution service.
6
+ """
7
+ from __future__ import annotations
8
+ from contextlib import contextmanager
9
+ from dataclasses import dataclass
10
+ import hashlib,json,math,os,re,time,uuid,sys
11
+ from pathlib import Path,PurePosixPath
12
+ from .runtime_io import artifact_path
13
+ from .processes import exclusive_lock,LockBusy
14
+ ROOT=None
15
+ CODE_ROOT=None
16
+
17
+ MAX_RECORD=2*1024**2
18
+ def canonical(v):
19
+ text=json.dumps(v,sort_keys=True,separators=(',',':'),allow_nan=False)
20
+ if len(text.encode('utf-8'))>MAX_RECORD:raise ValueError('Bounded identity/receipt required')
21
+ return text
22
+ def digest(v):return hashlib.sha256(canonical(v).encode('utf-8')).hexdigest()
23
+ def sha(path):return hashlib.sha256(Path(path).read_bytes()).hexdigest()
24
+ def _hash(h):
25
+ if not isinstance(h,str) or not re.fullmatch('[a-f0-9]{64}',h):raise ValueError('SHA256 required')
26
+ return h
27
+ def source_path(locator):
28
+ if not isinstance(locator,str) or ':' in locator or '\\' in locator:raise ValueError('Project-relative source required')
29
+ p=PurePosixPath(locator)
30
+ if p.is_absolute() or '..' in p.parts or not p.parts:raise ValueError('Unsafe source locator')
31
+ path=(CODE_ROOT.parent if p.parts[0]=='morphtree' else ROOT).joinpath(*p.parts)
32
+ for a in (path,*path.parents):
33
+ if a in (ROOT,CODE_ROOT.parent):break
34
+ if a.is_symlink() or a.exists() and getattr(a.lstat(),'st_file_attributes',0)&0x400:raise ValueError('No source reparse traversal')
35
+ if not path.is_file():raise ValueError('Missing source')
36
+ return path
37
+ def _numeric(v):
38
+ if v is None:return 'MISSING'
39
+ if isinstance(v,bool) or not isinstance(v,(int,float)):raise ValueError('Explicit numeric encoded TRAIN values required')
40
+ f=float(v)
41
+ if math.isinf(f):raise ValueError('Infinite TRAIN values forbidden')
42
+ return 'MISSING' if math.isnan(f) else f.hex()
43
+
44
+ @dataclass(frozen=True)
45
+ class FitRequest:
46
+ text:str
47
+ @property
48
+ def key(self):return hashlib.sha256(self.text.encode('utf-8')).hexdigest()
49
+ def value(self):return json.loads(self.text)
50
+
51
+ def learning_request(train,indices,*,protocol,split_sha256,preprocessing,task,family,structure,params,config,objective,seed,backend,dependencies,sources):
52
+ """Freeze actual rows and all declared learner dependencies by value.
53
+
54
+ Container weights/length, wall deadline and logical candidate name do not alter
55
+ an atomic completed fit. Learning tree/sample budget does; interrupted work is
56
+ never a cache hit. Protocol/split mismatches are intentionally conservative.
57
+ """
58
+ if type(train) is not dict or set(train)!={'role','ids','X','y'} or train['role']!='TRAIN':raise ValueError('TRAIN-only cache input')
59
+ X,y,ids=train['X'],train['y'],train['ids']
60
+ if not 1<=len(X)<=65536 or len(y)!=len(X) or len(ids)!=len(X) or len(set(ids))!=len(ids):raise ValueError('Aligned unique TRAIN rows required')
61
+ if any(not isinstance(x,(str,int)) or isinstance(x,bool) for x in ids):raise ValueError('Opaque row identifiers required')
62
+ width=len(X[0])
63
+ if not 1<=width<=128 or any(len(row)!=width for row in X):raise ValueError('Bounded rectangular numeric TRAIN required')
64
+ if task not in ('binary','regression','multiclass') or family not in ('boost','bag') or backend not in ('pure','numpy'):raise ValueError('Explicit native component context')
65
+ if not indices or any(type(i) is not int or not 0<=i<len(X) for i in indices):raise ValueError('Ordered actual sample indices required')
66
+ if type(seed) is not int or config.get('seed')!=seed or type(params.get('n_estimators')) is not int or params['n_estimators']<1:raise ValueError('Actual seed/tree budget required')
67
+ if family=='bag' and (params['n_estimators']!=1 or params.get('learning_rate')!=1.):raise ValueError('Bag identity describes one reset member, not its container')
68
+ if preprocessing.get('fit_role')!='TRAIN' or set(protocol)!={'path','sha256'} or not sources or not objective or not dependencies:raise ValueError('Full provenance required')
69
+ from dataclasses import asdict
70
+ from morphtree import HyperParams,AlgorithmGenome,MorphTreeConfig
71
+ if set(params)!=set(asdict(HyperParams())) or set(config)!=set(asdict(MorphTreeConfig())) or set(structure)!=set(AlgorithmGenome().to_dict()):raise ValueError('Complete native params/config/genome required')
72
+ HyperParams(**params);AlgorithmGenome(**structure);MorphTreeConfig(**config)
73
+ if config['tuner']!='none' or config['enable_tuning'] or config['enable_morphing'] or config['refit_full'] or config['memory_enabled']:raise ValueError('Fixed state-free component fitting only')
74
+ _hash(protocol['sha256']);_hash(split_sha256)
75
+ values=[[ _numeric(v) for v in row] for row in X];targets=[_numeric(v) for v in y]
76
+ # Hash the full actual input and effective ordered sample, not just array dimensions.
77
+ h=hashlib.sha256()
78
+ for row,label,ident in zip(values,targets,ids):h.update((canonical([row,label,ident])+'\n').encode('utf-8'))
79
+ sampled=hashlib.sha256()
80
+ for i in indices:sampled.update((canonical([values[i],targets[i],ids[i]])+'\n').encode('utf-8'))
81
+ value={'schema':'native-component-learning-v1','protocol':protocol,'split_sha256':split_sha256,'role':'TRAIN','TRAIN_sha256':h.hexdigest(),
82
+ 'effective_sample_sha256':sampled.hexdigest(),'indices':list(indices),'rows':len(X),'features':width,'preprocessing':preprocessing,
83
+ 'task':task,'family':family,'structure':structure,'params':params,'config':config,'objective':objective,'seed':seed,'backend':backend,
84
+ 'dependencies':dependencies,'sources':sources,'learning_budget':{'trees':params['n_estimators'],'sample_rows':len(indices)}}
85
+ required={'morphtree/'+p.name for p in CODE_ROOT.glob('*.py')}
86
+ if not required:raise ValueError('Missing actual installed native module set')
87
+ if not required<=set(sources):raise ValueError('Pin every consolidated learner module')
88
+ for n,h in sources.items():_hash(h);source_path(n)
89
+ return FitRequest(canonical(value))
90
+
91
+ def check_context(request):
92
+ if type(request) is not FitRequest:raise ValueError('Canonical FitRequest required')
93
+ v=request.value();p=v['protocol']
94
+ if sha(source_path(p['path']))!=p['sha256']:raise ValueError('Changed protocol')
95
+ for n,h in v['sources'].items():
96
+ if sha(source_path(n))!=h:raise ValueError('Changed learner dependency')
97
+ return v
98
+
99
+ def _write(path,value):
100
+ path=artifact_path(path);text=canonical(value)+'\n'
101
+ with path.open('x',encoding='utf-8') as f:f.write(text);f.flush();os.fsync(f.fileno())
102
+ def _read(path):
103
+ path=artifact_path(path)
104
+ if path.stat().st_size>MAX_RECORD:raise ValueError('Oversize cache record')
105
+ return json.loads(path.read_text('utf-8'))
106
+
107
+ def check_model(path,value):
108
+ from .execution_v2 import validate_model
109
+ validate_model(path,value)
110
+
111
+ class PendingComponent(RuntimeError):pass
112
+ class ComputeBudgetExhausted(RuntimeError):pass
113
+
114
+ class ComponentStore:
115
+ def __init__(self,folder,*,max_compute_jobs=0,seconds=180):
116
+ self.root=artifact_path(folder);self.root.mkdir(exist_ok=True)
117
+ if type(max_compute_jobs) is not int or max_compute_jobs<0 or not 0<seconds<=600:raise ValueError('Finite cache policy required')
118
+ with exclusive_lock(artifact_path(self.root/'metadata-lock')):
119
+ p=artifact_path(self.root/'POLICY.json')
120
+ if not p.exists():_write(p,{'schema':'native-component-store-v1','max_compute_jobs':max_compute_jobs,'seconds':seconds,'expires_unix':time.time()+seconds})
121
+ self.policy=_read(p)
122
+ if self.policy['max_compute_jobs']!=max_compute_jobs or self.policy['seconds']!=seconds:raise ValueError('Cache policy cannot reset budget/deadline')
123
+ self.keys=artifact_path(self.root/'components');self.keys.mkdir(exist_ok=True)
124
+ def _active(self,*,reuse_only=False):
125
+ if (self.root/'STOP').exists() or not reuse_only and time.time()>=self.policy['expires_unix']:raise ComputeBudgetExhausted('Cache computation expiry/STOP; no self-renewal')
126
+ def event(self,kind,**fields):
127
+ # Callers never acquire a component lock while holding this short journal lease.
128
+ for attempt in range(20):
129
+ try:
130
+ with exclusive_lock(artifact_path(self.root/'journal-lock')):
131
+ p=artifact_path(self.root/'EVENTS.jsonl')
132
+ with p.open('a',encoding='utf-8') as f:f.write(canonical({'event':kind,'UTC_unix':time.time(),**fields})+'\n');f.flush();os.fsync(f.fileno())
133
+ return
134
+ except LockBusy:
135
+ if attempt==19:raise
136
+ time.sleep(min(.01*(attempt+1),.1))
137
+ def _result(self,request,folder):
138
+ row=_read(folder/'COMPLETE.json')
139
+ if row['key']!=request.key or row['request']!=request.value():raise ValueError('Cache identity mismatch')
140
+ ref=row['artifact'];path=artifact_path(ROOT/ref['path'])
141
+ if path.stat().st_size!=ref['bytes'] or sha(path)!=ref['sha256']:raise ValueError('Changed completed component')
142
+ for proof in row['proof']:
143
+ if sha(source_path(proof['path']))!=proof['sha256']:raise ValueError('Changed completion provenance')
144
+ return row
145
+ def import_completed(self,request,model,*,expected_sha256,proof):
146
+ """Explicit verified historical admission; this performs no learning job."""
147
+ self._active();v=check_context(request);model=artifact_path(model);_hash(expected_sha256)
148
+ if not proof or sha(model)!=expected_sha256 or model.stat().st_size>16*1024**2:raise ValueError('Verified bounded artifact required')
149
+ for row in proof:
150
+ if sha(source_path(row['path']))!=_hash(row['sha256']):raise ValueError('Bad completion evidence')
151
+ check_model(model,v)
152
+ folder=artifact_path(self.keys/request.key)
153
+ with exclusive_lock(folder):
154
+ if (folder/'COMPLETE.json').exists():return self._result(request,folder)
155
+ if (folder/'INTENT.json').exists():raise PendingComponent('Reconcile interrupted learning before import')
156
+ record={'key':request.key,'request':v,'artifact':{'path':model.relative_to(ROOT).as_posix(),'sha256':expected_sha256,'bytes':model.stat().st_size},'proof':proof,'origin':'VERIFIED_HISTORICAL_IMPORT','compute_jobs':0}
157
+ _write(folder/'COMPLETE.json',record);self.event('IMPORTED_COMPLETED',key=request.key)
158
+ return record
159
+ @contextmanager
160
+ def claim(self,request,*,logical_id):
161
+ self._active(reuse_only=True);check_context(request)
162
+ if not isinstance(logical_id,str) or not 1<=len(logical_id)<=200:raise ValueError('Bounded logical request ID')
163
+ self.event('LOGICAL_REQUEST',key=request.key,logical_id=logical_id)
164
+ folder=artifact_path(self.keys/request.key)
165
+ try:
166
+ with exclusive_lock(folder):
167
+ if (folder/'COMPLETE.json').exists():
168
+ result=self._result(request,folder);self.event('CACHE_HIT',key=request.key,logical_id=logical_id);yield Lease(self,request,folder,result);return
169
+ self._active()
170
+ if (folder/'INTENT.json').exists():raise PendingComponent('Uncommitted attempt must not replay')
171
+ with exclusive_lock(artifact_path(self.root/'metadata-lock')):
172
+ used=sum(1 for _ in self.keys.glob('*/INTENT.json'))
173
+ if used>=self.policy['max_compute_jobs']:raise ComputeBudgetExhausted('No compute job budget')
174
+ _write(folder/'INTENT.json',{'key':request.key,'request':request.value(),'owner_pid':os.getpid(),'logical_id':logical_id})
175
+ self.event('COMPUTE_JOB_RESERVED',key=request.key,logical_id=logical_id)
176
+ lease=Lease(self,request,folder,None);yield lease
177
+ if lease.result is None:raise PendingComponent('Job incomplete; intent retained')
178
+ except LockBusy:
179
+ self.event('IN_FLIGHT_REFUSED',key=request.key,logical_id=logical_id);raise
180
+ def counts(self):
181
+ rows=[json.loads(x) for x in (self.root/'EVENTS.jsonl').read_text('utf-8').splitlines()] if (self.root/'EVENTS.jsonl').exists() else []
182
+ return {n:sum(x['event']==n for x in rows) for n in ('LOGICAL_REQUEST','CACHE_HIT','COMPUTE_JOB_RESERVED','IMPORTED_COMPLETED','IN_FLIGHT_REFUSED','NATIVE_FIT_STARTED','NATIVE_FIT_COMPLETED')}
183
+
184
+ class Lease:
185
+ def __init__(self,store,request,folder,result):self.store,self.request,self.folder,self.result=store,request,folder,result
186
+ @property
187
+ def hit(self):return self.result is not None
188
+ def commit(self,model,*,expected_sha256,proof):
189
+ if self.result is not None:raise ValueError('Completed lease is immutable')
190
+ self.store._active();check_context(self.request);path=artifact_path(model)
191
+ if not path.is_relative_to(self.folder) or path.stat().st_size>16*1024**2 or sha(path)!=_hash(expected_sha256) or not proof:raise ValueError('Owned complete artifact/provenance required')
192
+ for ref in proof:
193
+ if sha(source_path(ref['path']))!=_hash(ref['sha256']):raise ValueError('Changed provenance')
194
+ check_model(path,self.request.value())
195
+ _write(self.folder/'COMPLETE.json',{'key':self.request.key,'request':self.request.value(),'artifact':{'path':path.relative_to(ROOT).as_posix(),'sha256':expected_sha256,'bytes':path.stat().st_size},'proof':proof,'origin':'COMPUTED_UNDER_EXCLUSIVE_LEASE','compute_jobs':1})
196
+ self.result=self.store._result(self.request,self.folder);self.store.event('COMPUTE_JOB_COMPLETED',key=self.request.key)
197
+
198
+ def native_binary_component(store,train,indices,*,logical_id,context):
199
+ """One component adapter. A hit returns before estimator construction/fit.
200
+
201
+ Current qualification covers verified hit/no-budget/role paths only. A future
202
+ fit-capable campaign must independently qualify the miss branch and environment.
203
+ """
204
+ request=learning_request(train,indices,**context);v=request.value()
205
+ if v['task']!='binary' or v['objective']!='binary_logistic_Newton':raise ValueError('This adapter qualifies binary fixed-objective components only')
206
+ with store.claim(request,logical_id=logical_id) as lease:
207
+ reused=lease.hit
208
+ if not lease.hit:
209
+ if not v['dependencies'].get('qualified_fit_environment_sha256'):raise ValueError('Fresh qualified fit environment required')
210
+ from morphtree import MorphTreeClassifier,AlgorithmGenome,HyperParams,MorphTreeConfig
211
+ model=MorphTreeClassifier(genome=AlgorithmGenome(**v['structure']),params=HyperParams(**v['params']),config=MorphTreeConfig(**v['config']),backend=v['backend'])
212
+ store.event('NATIVE_FIT_STARTED',key=request.key)
213
+ model.fit([train['X'][i] for i in indices],[train['y'][i] for i in indices],cancel=lambda:(store._active() or False))
214
+ path=artifact_path(lease.folder/'model.mtree');model.save_model(path)
215
+ lease.commit(path,expected_sha256=sha(path),proof=[v['protocol']]);store.event('NATIVE_FIT_COMPLETED',key=request.key)
216
+ from morphtree import load_model
217
+ return load_model(ROOT/lease.result['artifact']['path'],backend='pure'),{'key':request.key,'artifact':lease.result['artifact'],'reused':reused}
218
+
219
+ def configure(root,package):
220
+ global ROOT,CODE_ROOT
221
+ ROOT=Path(root).resolve();CODE_ROOT=Path(package).resolve()
222
+ if not ROOT.is_dir() or not CODE_ROOT.is_dir():raise ValueError('Explicit existing roots')
223
+ def native_sources():
224
+ return {'morphtree/'+p.relative_to(CODE_ROOT).as_posix():sha(p) for p in CODE_ROOT.rglob('*.py')}
225
+
226
+ class MultitaskStore(ComponentStore):
227
+
228
+ def events(self):
229
+ p = self.root / 'EVENTS.jsonl'
230
+ if not p.exists():
231
+ return []
232
+ raw = p.read_bytes()
233
+ if raw and (not raw.endswith(b'\n')):
234
+ raise ValueError('Partial journal tail requires reconciliation')
235
+ rows = []
236
+ previous = '0' * 64
237
+ for i, line in enumerate(raw.splitlines()):
238
+ row = json.loads(line)
239
+ body = {k: v for k, v in row.items() if k != 'sha256'}
240
+ if row.get('sequence') != i + 1 or row.get('previous') != previous or row.get('sha256') != digest(body):
241
+ raise ValueError('Corrupt execution journal')
242
+ previous = row['sha256']
243
+ rows.append(row)
244
+ return rows
245
+
246
+ def event(self, kind, **fields):
247
+ with exclusive_lock(artifact_path(self.root / 'journal-lock')):
248
+ rows = self.events()
249
+ body = {'sequence': len(rows) + 1, 'previous': rows[-1]['sha256'] if rows else '0' * 64, 'event': kind, 'UTC_unix': time.time(), **fields}
250
+ row = {**body, 'sha256': digest(body)}
251
+ with (self.root / 'EVENTS.jsonl').open('a', encoding='utf8') as f:
252
+ f.write(json.dumps(row, sort_keys=True, allow_nan=False) + '\n')
253
+ f.flush()
254
+ os.fsync(f.fileno())
255
+
256
+ def _result(self, request, folder):
257
+ row = super()._result(request, folder)
258
+ validate_native(artifact_path(row['artifact']['path']), request)
259
+ return row
260
+
261
+ def validate_native(path,request):
262
+ from .execution_v2 import validate_model
263
+ return validate_model(path,request.value())
@@ -0,0 +1,307 @@
1
+ """One read-only projection of the existing component journal, not a journal.
2
+
3
+ Packaged with the experimental orchestrator only. Callbacks must reopen native
4
+ artifacts/proofs and reproduce predictions; a summary flag is never evidence.
5
+ Historical reports are inputs to an optional consistency check, never authority.
6
+ """
7
+ import copy
8
+ import hashlib
9
+ import json
10
+ import math
11
+
12
+
13
+ def _digest(value):
14
+ return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':'),
15
+ allow_nan=False).encode('utf8')).hexdigest()
16
+
17
+
18
+ def verified_evolution_report(rows, *, packets, verify_packet, verify_oof,
19
+ program, metric, final_packet, mode,
20
+ claimed=None, finalization=None,budget_recovery=None):
21
+ """Fail closed on contradictions; UNKNOWN remains explicit in partial traces.
22
+
23
+ No authority, fitting, file mutation or second ledger is created. Canonical
24
+ events below are views referencing exact source sequence/hash/timestamp.
25
+ A missing historical timestamp is UNKNOWN, never the reconciliation time.
26
+ """
27
+ if mode not in ('LIVE', 'FIXTURE', 'PREFIT', 'PUBLIC_USER') or len(rows)>10000:
28
+ raise ValueError('Bounded explicit evidence mode required')
29
+ previous='0'*64
30
+ for i,r in enumerate(rows,1):
31
+ body={k:v for k,v in r.items() if k!='sha256'}
32
+ if r.get('sequence')!=i or r.get('previous')!=previous or r.get('sha256')!=_digest(body):
33
+ raise ValueError('Broken canonical journal chain')
34
+ if type(r.get('UTC_unix')) not in (int,float) or not math.isfinite(r['UTC_unix']):
35
+ raise ValueError('Invalid event timestamp')
36
+ previous=r['sha256']
37
+ events=[];forms={};contexts={};current=None;frozen=None;steps=[]
38
+ starts={};ends={};trials={};trial_ends={};pending=None;artifact_keys=set();diagnoses=[]
39
+ trial_numbers=set();created_trials={};budget_stop=None;interrupted_steps=[]
40
+ def emit(kind,r,*,parent=None,status='COMPLETED',**values):
41
+ events.append(dict(id='view:'+str(r['sequence'])+':'+kind,kind=kind,
42
+ parent=parent,timestamp=r['UTC_unix'],status=status,
43
+ provenance={'sequence':r['sequence'],'sha256':r['sha256']},**values))
44
+ def checked(name):
45
+ if name not in packets:raise ValueError('Unverifiable program packet')
46
+ p=packets[name]
47
+ if p['id']!=name:raise ValueError('Changed packet identity')
48
+ proof=verify_packet(copy.deepcopy(p))
49
+ if proof['packet_sha256']!=_digest(p):raise ValueError('Artifact differs from program packet')
50
+ pred=proof['prediction'];loss=metric(p,pred)
51
+ artifact_keys.update(proof['component_keys'])
52
+ if not math.isfinite(loss):raise ValueError('Nonfinite SEARCH loss')
53
+ return {'packet':copy.deepcopy(p),'program':program(p),'SEARCH_primary':loss,
54
+ 'TRAIN_primary':proof.get('TRAIN_primary','UNKNOWN'),
55
+ 'artifacts':proof['artifacts'],'component_keys':list(proof['component_keys']),
56
+ 'prediction_sha256':_digest(pred)}
57
+ for r in rows:
58
+ k=r['event'];v=r.get('value',{});op=v.get('operation')
59
+ if k in ('NATIVE_FIT_STARTED','NATIVE_FIT_COMPLETED'):
60
+ target=starts if k.endswith('STARTED') else ends;key=r['key']
61
+ if key in target:raise ValueError('Duplicate physical fit event')
62
+ if target is ends and (key not in starts or starts[key]['phase']!=r['phase']):
63
+ raise ValueError('Completion without corresponding physical start')
64
+ target[key]=r;emit('FIT_STARTED' if target is starts else 'FIT_COMPLETED',r,
65
+ status='STARTED' if target is starts else 'COMPLETED',component=key,phase=r['phase'])
66
+ elif k=='NUMERIC_TRIAL_INTENT':
67
+ # Existing producer reserves before Optuna.ask(), so its intent has
68
+ # no Optuna number yet. Keep the reservation distinct from the
69
+ # number later returned by tell; never invent an executed trial.
70
+ n=r['sequence']
71
+ if n in trials or frozen is None:raise ValueError('Trial duplicated or before freeze')
72
+ trials[n]=r;emit('NUMERIC_TUNING_STARTED',r,status='STARTED',real=r['real'],
73
+ reservation_sequence=n,number=r.get('number','UNKNOWN_UNTIL_ASK'))
74
+ elif k=='NUMERIC_TRIAL_ASKED' and mode=='PUBLIC_USER':
75
+ eligible=[n for n in trials if n not in created_trials and n not in trial_ends]
76
+ if len(eligible)!=1 or type(r.get('number')) is not int or type(r.get('real')) is not bool:
77
+ raise ValueError('Created user trial without a unique reservation')
78
+ n=eligible[0]
79
+ if r['real']!=trials[n]['real'] or r['number'] in {x['number'] for x in created_trials.values()}:
80
+ raise ValueError('Changed driver evidence or repeated user trial number')
81
+ created_trials[n]=r
82
+ emit('NUMERIC_TRIAL_CREATED',r,number=r['number'],real=r['real'],reservation_sequence=n)
83
+ elif k=='NUMERIC_TRIAL_COMPLETED':
84
+ eligible=[n for n in trials if n not in trial_ends]
85
+ if len(eligible)!=1 or r['number'] in trial_numbers:
86
+ raise ValueError('Trial completion without matching intent')
87
+ n=eligible[0]
88
+ if mode=='PUBLIC_USER' and (n not in created_trials or created_trials[n]['number']!=r['number']):
89
+ raise ValueError('Completion before an actual returned user trial')
90
+ if r['real']!=trials[n]['real'] or trials[n].get('number',r['number'])!=r['number']:
91
+ raise ValueError('Trial number or evidence changed')
92
+ trial_numbers.add(r['number'])
93
+ trial_ends[n]=r
94
+ elif k=='OOF_CONTEXT_PRODUCED':
95
+ proof=verify_oof(copy.deepcopy(r))
96
+ if (proof['context']!=r['context'] or proof['parent']!=r['parent']
97
+ or proof['components']!=r['components'] or proof['mode']!=mode):
98
+ raise ValueError('OOF artifacts/provenance differ')
99
+ if r['context'] in contexts:raise ValueError('Duplicate OOF acquisition')
100
+ contexts[r['context']]=proof
101
+ artifact_keys.update(proof['component_keys'])
102
+ emit('OOF_COMPLETED',r,parent=r['parent'],components=proof['components'],
103
+ evidence=proof['evidence'],artifacts=proof['artifacts'])
104
+ elif k=='OOF_CONTEXT_REUSED':
105
+ if r['context'] not in contexts or r['physical_fits']!=0:
106
+ raise ValueError('Unverifiable reused OOF context')
107
+ elif op=='learning_completed':
108
+ name=v['id']
109
+ if name in forms:raise ValueError('Duplicate completed program')
110
+ forms[name]=checked(name);emit('PROGRAM_CREATED',r,program=forms[name]['program'],packet=name)
111
+ if 'packet' in v and v['packet']!=packets[name]:raise ValueError('Executed packet differs')
112
+ elif op=='family_selection' and v['status']=='COMPLETED':
113
+ name=v['retained']
114
+ if name not in forms:raise ValueError('Parent not executed')
115
+ # Verify the actual metric, including initial family comparison.
116
+ if v['loss']!=forms[name]['SEARCH_primary']:raise ValueError('Stale family metric')
117
+ if any(f['SEARCH_primary']<forms[name]['SEARCH_primary'] for f in forms.values()):
118
+ raise ValueError('Lost initial verified SEARCH anchor')
119
+ current=name;emit('PROGRAM_SELECTED_AS_PARENT',r,parent=name,program=forms[name]['program'])
120
+ elif op=='diagnosis_completed':
121
+ if current is None:current=v['parent']
122
+ p=packets.get(current);ctx=contexts.get(v['context_sha256'])
123
+ if (p is None or v['parent']!=current or ctx is None
124
+ or v['parent_packet_sha256']!=_digest(p)
125
+ or ctx['parent']!=_digest(p) or ctx['program_sha256']!=_digest(program(p))):
126
+ raise ValueError('Unverified completed diagnosis')
127
+ diagnoses.append(v['context_sha256'])
128
+ emit('DIAGNOSIS_COMPLETED',r,parent=current,evidence=ctx['evidence'],context=v['context_sha256'])
129
+ elif op=='evolution_intent':
130
+ if current is None:current=v['parent'] # single-family path; checked below
131
+ if current not in forms or pending is not None or frozen is not None:
132
+ raise ValueError('Invalid mutation ordering')
133
+ p=packets[current];ctx=contexts.get(v['diagnostic_context_sha256'])
134
+ if (v['parent']!=current or v['parent_packet_sha256']!=_digest(p)
135
+ or v['parent_program_sha256']!=_digest(program(p)) or ctx is None
136
+ or ctx['parent']!=_digest(p) or ctx['program_sha256']!=_digest(program(p))):
137
+ raise ValueError('Wrong parent or diagnostic provenance')
138
+ child=v['child_program'];program_packet={'task':child['task'],'family':child['family'],
139
+ 'recipe':{'genome':child['genome']}}
140
+ if program(program_packet)!=child:raise ValueError('Declared instructions differ from dispatch')
141
+ changed=[g for g,val in p['recipe']['genome'].items() if child['genome'][g]!=val]
142
+ if changed!=[v['gene']] or child['family']!=p['family'] or v['numeric_parameters_sha256']!=_digest(p['params']):
143
+ raise ValueError('Not a single typed structural transformation')
144
+ pending=copy.deepcopy(v)
145
+ if v['diagnostic_context_sha256'] not in diagnoses:
146
+ # Historical journal has no separate diagnosis-complete record.
147
+ # This proposal proves one consumption; the timestamp is its
148
+ # recorded upper bound, not an invented diagnosis finish time.
149
+ emit('DIAGNOSIS_COMPLETED',r,parent=current,evidence=ctx['evidence'],
150
+ context=v['diagnostic_context_sha256'],timestamp_semantics='PROPOSAL_BOUND_UPPER_BOUND')
151
+ emit('MUTATION_PROPOSED',r,parent=current,program=child,gene=v['gene'])
152
+ elif op=='search_budget_stopped':
153
+ if (mode!='PUBLIC_USER' or v.get('retained') not in forms
154
+ or v.get('stage') not in ('FAMILY','EVOLUTION','NUMERIC')):
155
+ raise ValueError('Unverified typed public budget stop')
156
+ if current is None:current=v['retained']
157
+ if v['retained']!=current:raise ValueError('Budget stop lost the verified current anchor')
158
+ budget_stop=copy.deepcopy(v)
159
+ emit('SEARCH_STOPPED_BUDGET',r,parent=current,details=budget_stop,status='STOPPED_BUDGET')
160
+ elif op=='evolution_interrupted':
161
+ if (pending is None or budget_stop is None or budget_stop['stage']!='EVOLUTION'
162
+ or v.get('reason')!='TYPED_SEARCH_BUDGET'
163
+ or v.get('index')!=pending['index'] or v.get('parent')!=current
164
+ or v.get('retained')!=current or v.get('decision')!='RESTORE_VERIFIED_PARENT'):
165
+ raise ValueError('Invalid incomplete mutation recovery')
166
+ interrupted_steps.append({'index':pending['index'],'parent':current,
167
+ 'status':'INTERRUPTED_NOT_ADOPTED','retained':current})
168
+ emit('MUTATION_INTERRUPTED',r,parent=current,status='INTERRUPTED_NOT_ADOPTED')
169
+ pending=None
170
+ elif op=='numeric_budget_skipped':
171
+ if frozen is None or budget_stop is None or v.get('retained')!=current:
172
+ raise ValueError('Unverified numerical budget skip')
173
+ emit('NUMERIC_TUNING_SKIPPED',r,parent=current,reason='SEARCH_BUDGET')
174
+ elif op=='evolution_selection' and v['status']=='COMPLETED':
175
+ if pending is None or v['parent']!=current or v['candidate'] not in forms:
176
+ raise ValueError('Child without a verified parent')
177
+ child=checked(v['candidate']);parent=forms[current];cp=child['packet'];pp=parent['packet']
178
+ if (child['program']!=pending['child_program'] or cp['params']!=pp['params']
179
+ or cp['learning_identity']!=pp['learning_identity']
180
+ or cp['recipe']['classes']!=pp['recipe']['classes']
181
+ or cp['recipe']['encoder']!=pp['recipe']['encoder']):
182
+ raise ValueError('Executed child differs from nominated program/data/numbers')
183
+ decision='ACCEPT' if child['SEARCH_primary']<parent['SEARCH_primary'] else 'ROLLBACK'
184
+ retained=v['candidate'] if decision=='ACCEPT' else current
185
+ if v['decision']!=decision or v['retained']!=retained:
186
+ raise ValueError('False acceptance/rejection or retained anchor')
187
+ emit('MUTATED_PROGRAM_EXECUTED',r,parent=current,packet=cp['id'],program=child['program'])
188
+ emit('CANDIDATE_EVALUATED',r,parent=current,SEARCH_parent=parent['SEARCH_primary'],
189
+ SEARCH_candidate=child['SEARCH_primary'])
190
+ emit('MUTATION_ACCEPTED' if decision=='ACCEPT' else 'MUTATION_REJECTED',r,parent=current,child=cp['id'])
191
+ if decision=='ROLLBACK':emit('ROLLBACK_COMPLETED',r,parent=current,retained=retained)
192
+ steps.append(dict(index=pending['index'],parent=current,child=cp['id'],
193
+ parent_program=parent['program'],child_program=child['program'],decision=decision,
194
+ retained=retained,SEARCH_parent=parent['SEARCH_primary'],SEARCH_candidate=child['SEARCH_primary'],
195
+ SEARCH_retained=forms[retained]['SEARCH_primary'],execution=pending['execution']))
196
+ current=retained;pending=None
197
+ elif op=='evolution_complete':
198
+ if current is None:current=v['retained']
199
+ if current not in forms or v['retained']!=current or v['program']!=forms[current]['program'] or pending:
200
+ raise ValueError('Unverifiable terminal program')
201
+ elif op=='structure_frozen' and 'packet' in v:
202
+ p=v['packet']
203
+ if current is None:current=p['id']
204
+ if current not in forms or p!=packets[current] or pending:raise ValueError('Incompatible frozen anchor')
205
+ if frozen is not None and frozen!=p:raise ValueError('Changed frozen program')
206
+ frozen=copy.deepcopy(p);emit('PROGRAM_FROZEN',r,parent=current,program=program(p),params=p['params'])
207
+ if v.get('numeric_budget')==0:emit('NUMERIC_TUNING_SKIPPED',r,parent=current,reason='ZERO_ADMITTED_NUMERIC_BUDGET')
208
+ elif op=='numeric_selection' and v.get('status')=='COMPLETED':
209
+ if frozen is None or v['retained'] not in forms:raise ValueError('Unverified numeric selection')
210
+ chosen=checked(v['retained'])
211
+ if (chosen['program']!=program(frozen)
212
+ or chosen['packet']['learning_identity']!=frozen['learning_identity']
213
+ or chosen['SEARCH_primary']>forms[current]['SEARCH_primary']):
214
+ raise ValueError('Numeric phase changed structure/data or lost verified anchor')
215
+ current=v['retained']
216
+ if (frozen is None or current!=final_packet['id'] or final_packet!=packets[current]
217
+ or program(final_packet)!=program(frozen)):
218
+ raise ValueError('Final model is not the frozen verified anchor')
219
+ # Reopen selected weights again after traversal; verifier must bind payload.
220
+ final=checked(current)
221
+ stop=next((r['value']['stop'] for r in reversed(rows) if r['event']=='ORCHESTRATION'
222
+ and r['value'].get('operation')=='evolution_complete'),'UNKNOWN')
223
+ counts={'physical_fits_started':len(starts),'physical_fits_completed':len(ends),
224
+ 'incomplete_native_fits':len(starts.keys()-ends.keys()),
225
+ 'scalar_trees_started':sum(r['scalar_trees'] for r in starts.values()),
226
+ 'OOF_components':sum(p['components'] for p in contexts.values()),
227
+ 'OOF_physical_fits_completed':sum(r['phase']=='OOF' for r in ends.values()),
228
+ 'OOF_contexts_acquired':len(contexts),
229
+ 'OOF_contexts_reused':sum(r['event']=='OOF_CONTEXT_REUSED' for r in rows),
230
+ 'explicit_diagnosis_completions':len(diagnoses),
231
+ 'cache_hits':sum(r['event']=='CACHE_HIT' for r in rows),
232
+ 'real_trials_started':sum(r['real'] for r in trials.values()),
233
+ 'real_trials_completed':sum(r['real'] and r['state']=='COMPLETE' for r in trial_ends.values()),
234
+ 'simulated_trials_started':sum(not r['real'] for r in trials.values())}
235
+ if mode=='PUBLIC_USER':
236
+ counts['trial_reservations']=len(trials)
237
+ counts['real_trials_created']=sum(r['real'] for r in created_trials.values())
238
+ counts['simulated_trials_created']=sum(not r['real'] for r in created_trials.values())
239
+ hits={r['key'] for r in rows if r['event']=='CACHE_HIT'}
240
+ fixture_keys={r['key'] for r in rows if r['event']=='FIXTURE_COMPLETED'}
241
+ unassigned=(set(ends)|hits|fixture_keys)-artifact_keys
242
+ interrupted=set(starts)-set(ends);unadopted={}
243
+ if unassigned or interrupted:
244
+ if mode!='PUBLIC_USER' or budget_stop is None or not callable(budget_recovery):
245
+ raise ValueError('Physical/cache/fixture accounting does not match verified artifacts')
246
+ unadopted=budget_recovery(unassigned|interrupted)
247
+ if type(unadopted) is not dict or set(unadopted)!=(unassigned|interrupted):
248
+ raise ValueError('Every unadopted component needs explicit artifact/intent verification')
249
+ for key,p in unadopted.items():
250
+ if (p.get('key')!=key or type(p.get('committed')) is not bool
251
+ or p.get('used_in_final_model') is not False
252
+ or not isinstance(p.get('intent_sha256'),str) or len(p['intent_sha256'])!=64
253
+ or p['committed'] and not isinstance(p.get('artifact'),dict)):
254
+ raise ValueError('Invalid unadopted component evidence')
255
+ p['native_completed']=key in ends
256
+ p['status']='COMMITTED_NOT_ADOPTED' if p['committed'] else 'NATIVE_COMPLETE_NOT_COMMITTED' if key in ends else 'INTERRUPTED_NOT_ADOPTED'
257
+ if artifact_keys-(set(ends)|hits|fixture_keys):
258
+ raise ValueError('Reported component has no physical/cache/fixture evidence')
259
+ def interval_union(keys):
260
+ intervals=sorted((starts[k]['UTC_unix'],ends[k]['UTC_unix']) for k in keys if k in starts and k in ends)
261
+ total=0.;last=None
262
+ for a,b in intervals:
263
+ if b<a:raise ValueError('Native completion precedes start')
264
+ total+=max(0.,b-max(a,last if last is not None else a));last=max(b,last if last is not None else b)
265
+ return total
266
+ for f in forms.values():
267
+ f['native_logged_wall_union_seconds']=interval_union(f['component_keys'])
268
+ f['native_physical_fits']=sum(k in ends for k in f['component_keys'])
269
+ f['cost_scope']='NATIVE_EVENT_WINDOWS_ONLY_NOT_COMPLETE_SEARCH_OR_BENCHMARK'
270
+ # checked() reopens final weights; annotate its same scoped native costs.
271
+ final.update({k:forms[current][k] for k in ('native_logged_wall_union_seconds','native_physical_fits','cost_scope')})
272
+ if mode in ('LIVE','PUBLIC_USER') and counts['OOF_components']!=counts['OOF_physical_fits_completed']:
273
+ # Cache-reused components must be explicitly distinguished by the proof.
274
+ cached=sum(p.get('cache_components',0) for p in contexts.values())
275
+ orphan_oof=sum(k in unadopted and r['phase']=='OOF' for k,r in ends.items())
276
+ if counts['OOF_components']!=counts['OOF_physical_fits_completed']-orphan_oof+cached:
277
+ raise ValueError('OOF component/native accounting contradiction')
278
+ status='MODEL_READY_EXPORT_NOT_VERIFIED'
279
+ if finalization is not None:
280
+ # Verification occurs in the existing postfit safe-loader/V3/standalone
281
+ # path or a read-only replay, never inferred from a report boolean.
282
+ f=finalization(copy.deepcopy(final_packet))
283
+ if f['packet_sha256']!=_digest(final_packet) or f['weights_sha256']!=final_packet['artifact_sha256']:
284
+ raise ValueError('Finalized weights differ from retained program')
285
+ source=next((r for r in reversed(rows) if r['event']=='WORKFLOW_COMPLETED'),None)
286
+ if source is not None:
287
+ if source['model']!=f['model']:raise ValueError('Final artifact differs from workflow')
288
+ emit('MODEL_FINALIZED',source,parent=current,artifacts=f)
289
+ status='COMPLETED_VERIFIED_EXPORT'
290
+ out={'schema':'comp010-verified-evolution-report-v1','status':status,'evidence':mode,
291
+ 'journal_head_sha256':previous,'events':events,'forms':forms,'steps':steps,
292
+ 'counts':counts,'stop':stop,'final':final,'diagnosis_executed':bool(contexts),
293
+ 'diagnostic_utility':'INCONCLUSIVE','performance_qualified':False,
294
+ 'OOF_acquisition_method_seconds':sum(p['seconds'] for p in contexts.values()),
295
+ 'cost_scope':'INCLUSIVE_OPERATIONAL_METHOD_ONLY_EXCLUDES_OUTER_ADMISSION_AND_REVERIFICATION',
296
+ 'CPU':'UNKNOWN','peak_RSS':'UNKNOWN','global_optimum_claim':False}
297
+ if budget_stop is not None:
298
+ out['budget_recovery']={'schema':'product006-verified-budget-recovery-v1',
299
+ 'stop':budget_stop,'unadopted_components':unadopted,
300
+ 'interrupted_steps':interrupted_steps,'phase_completion':'PARTIAL_BUDGET_VALID_ANCHOR_RETURNED',
301
+ 'not_claimed':'No completed mutation/OOF/trial inferred from partial physical work'}
302
+ if claimed is not None:
303
+ # Optional validation is exact; legacy summaries require an explicit
304
+ # discrepancy overlay and must never be silently accepted as verified.
305
+ for key in ('counts','steps','final','diagnosis_executed','journal_head_sha256'):
306
+ if claimed.get(key)!=out[key]:raise ValueError('Stale or contradictory report: '+key)
307
+ return out