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.
- morphtree/__init__.py +28 -0
- morphtree/__main__.py +3 -0
- morphtree/_experimental/__init__.py +1 -0
- morphtree/_experimental/admission.py +364 -0
- morphtree/_experimental/base.py +215 -0
- morphtree/_experimental/component_cache_v2.py +263 -0
- morphtree/_experimental/evolution_report.py +307 -0
- morphtree/_experimental/execution_v2.py +697 -0
- morphtree/_experimental/facade.py +289 -0
- morphtree/_experimental/integrated.py +748 -0
- morphtree/_experimental/learning.py +418 -0
- morphtree/_experimental/manifest.json +204 -0
- morphtree/_experimental/numeric_fixture.py +21 -0
- morphtree/_experimental/parameter_contract.py +38 -0
- morphtree/_experimental/postfit.py +51 -0
- morphtree/_experimental/preprocessing.py +133 -0
- morphtree/_experimental/processes.py +191 -0
- morphtree/_experimental/producer.py +542 -0
- morphtree/_experimental/product.py +156 -0
- morphtree/_experimental/product_cli.py +43 -0
- morphtree/_experimental/roundtrip_v3.py +311 -0
- morphtree/_experimental/runtime_io.py +65 -0
- morphtree/_experimental/sampling.py +26 -0
- morphtree/_experimental/support.py +281 -0
- morphtree/_experimental/target.py +59 -0
- morphtree/_experimental/training.py +53 -0
- morphtree/_experimental/worker.py +682 -0
- morphtree/_installed.py +17 -0
- morphtree/_native/__init__.py +1 -0
- morphtree/_native/bin/win_amd64/morphtree_kernels.dll +0 -0
- morphtree/_native/buffers.py +82 -0
- morphtree/_native/loader.py +39 -0
- morphtree/_native/manifest.json +29 -0
- morphtree/_native/runtime.py +48 -0
- morphtree/_native/src/histogram.c +61 -0
- morphtree/_native/src/traversal.c +51 -0
- morphtree/api.py +388 -0
- morphtree/api_compat.py +65 -0
- morphtree/backend.py +162 -0
- morphtree/booster.py +252 -0
- morphtree/cli.py +52 -0
- morphtree/config.py +262 -0
- morphtree/control.py +20 -0
- morphtree/data.py +300 -0
- morphtree/diagnostics.py +158 -0
- morphtree/estimators.py +156 -0
- morphtree/examples.py +25 -0
- morphtree/exporting.py +224 -0
- morphtree/legacy_artifacts.py +32 -0
- morphtree/metrics.py +156 -0
- morphtree/model.py +1010 -0
- morphtree/mutations.py +205 -0
- morphtree/native_lab_backend.py +79 -0
- morphtree/numpy_backend.py +180 -0
- morphtree/optuna_tuner.py +154 -0
- morphtree/persistence.py +414 -0
- morphtree/prediction_output.py +35 -0
- morphtree/prepared_bins.py +82 -0
- morphtree/public.py +106 -0
- morphtree/resource_plan.py +170 -0
- morphtree/scalable.py +781 -0
- morphtree/scalable_backend.py +74 -0
- morphtree/scalable_checkpoint.py +129 -0
- morphtree/scalable_data.py +255 -0
- morphtree/scalable_receipts.py +92 -0
- morphtree/scalable_target.py +68 -0
- morphtree/scalable_variants.py +133 -0
- morphtree/sklearn_view.py +79 -0
- morphtree/source_markers.py +25 -0
- morphtree/species.py +47 -0
- morphtree/temporary.py +65 -0
- morphtree/training_fragment.py +130 -0
- morphtree/tree.py +231 -0
- morphtree/tuning.py +150 -0
- morphtree-2.1.0.dist-info/METADATA +212 -0
- morphtree-2.1.0.dist-info/RECORD +79 -0
- morphtree-2.1.0.dist-info/WHEEL +4 -0
- morphtree-2.1.0.dist-info/entry_points.txt +2 -0
- 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
|