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,748 @@
|
|
|
1
|
+
"""Installed COMP002 hooks for the existing LearningControl, not a new learner.
|
|
2
|
+
|
|
3
|
+
Copied into the private wheel with relative imports. No import of this file as
|
|
4
|
+
workspace runtime and no implicit authority from the public fit API.
|
|
5
|
+
"""
|
|
6
|
+
import copy
|
|
7
|
+
from dataclasses import asdict
|
|
8
|
+
from morphtree.config import AlgorithmGenome
|
|
9
|
+
from morphtree.mutations import propose_mutations
|
|
10
|
+
from .admission import AdmissionError, digest
|
|
11
|
+
from .product import MorphTree as Consumer
|
|
12
|
+
from .learning import LearningControl, mutation_admission
|
|
13
|
+
from .support import FamilyChoice, TaskFinalizer
|
|
14
|
+
from .execution_v2 import ACTIVE, require_session, SearchBudgetExhausted
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def learning_program(packet):
|
|
18
|
+
"""Bounded native procedure, separate from numbers, weights and lineage.
|
|
19
|
+
|
|
20
|
+
The instructions are derived from the existing executable genome, never
|
|
21
|
+
arbitrary Python. This is the current native grammar, not a new general
|
|
22
|
+
programming language or evidence that every primitive is scientifically useful.
|
|
23
|
+
"""
|
|
24
|
+
task, family = packet['task'], packet['family']
|
|
25
|
+
if task not in ('binary', 'multiclass', 'regression') or family not in ('boost', 'bag'):
|
|
26
|
+
raise ValueError('Existing native task/family required')
|
|
27
|
+
genome = AlgorithmGenome(**packet['recipe']['genome'])
|
|
28
|
+
if task != 'binary' and genome != AlgorithmGenome():
|
|
29
|
+
# Only the separately versioned native capability may admit this.
|
|
30
|
+
# Consolidated/older runtimes keep rejecting nonbinary transformations.
|
|
31
|
+
from morphtree import config as native_config
|
|
32
|
+
validate=getattr(native_config,'validate_task_genome',None)
|
|
33
|
+
if not callable(validate):
|
|
34
|
+
raise ValueError('Nonbinary structural dispatch is not qualified')
|
|
35
|
+
validate(task,genome)
|
|
36
|
+
body={'schema':'comp008-native-learning-program-v2', 'version':2, 'task':task,
|
|
37
|
+
'family':family, 'genome':genome.to_dict(),
|
|
38
|
+
'instructions':[
|
|
39
|
+
{'op':'preprocess', 'implementation':'native.TRAIN_only_encoder'},
|
|
40
|
+
{'op':'learning', 'implementation':'native.sequential_derivatives' if family=='boost'
|
|
41
|
+
else 'native.bootstrap_independent_one_stage'},
|
|
42
|
+
{'op':'tree_growth', 'implementation':genome.growth_policy},
|
|
43
|
+
{'op':'split_score', 'stack':list(genome.split_program)},
|
|
44
|
+
{'op':'leaf_update', 'implementation':genome.leaf_policy},
|
|
45
|
+
{'op':'prediction', 'implementation':'native.stage_sum' if family=='boost' else 'native.member_mean'}],
|
|
46
|
+
'execution':'EXISTING_NATIVE_CONSTRUCTORS', 'code_evaluation':'NONE',
|
|
47
|
+
'grammar':'EXISTING_ALGORITHM_GENOME_NATIVE_PRIMITIVES',
|
|
48
|
+
'sampling':'native.configured_row_sampling' if family=='boost' else 'native.bootstrap',
|
|
49
|
+
'state_update':'native.sequential_boosting' if family=='boost' else 'native.independent_member_append',
|
|
50
|
+
'termination':'FIXED_NATIVE_LEARNING_BUDGET_AND_COOPERATIVE_CANCEL'}
|
|
51
|
+
return dict(body,program_id='mt-program:'+digest(body))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def program_dispatch(program):
|
|
55
|
+
"""Validate all instructions against their actual native dispatch."""
|
|
56
|
+
packet={'task':program['task'], 'family':program['family'],
|
|
57
|
+
'recipe':{'genome':program['genome']}}
|
|
58
|
+
if program != learning_program(packet):
|
|
59
|
+
raise ValueError('Program instructions do not describe the native implementation')
|
|
60
|
+
return program['family'], AlgorithmGenome(**program['genome'])
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def transformed_program(parent, proposal):
|
|
64
|
+
family, genome = program_dispatch(parent)
|
|
65
|
+
changed=[k for k,v in genome.to_dict().items() if proposal.genome.to_dict()[k]!=v]
|
|
66
|
+
if changed != [proposal.gene]:
|
|
67
|
+
raise ValueError('One actual typed native gene must change, not a numeric alias')
|
|
68
|
+
return learning_program({'task':parent['task'],'family':family,
|
|
69
|
+
'recipe':{'genome':proposal.genome.to_dict()}})
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def evolution_context(context, selected, program, *, authenticate):
|
|
73
|
+
"""Validate information timing; authentication stays with the owned producer.
|
|
74
|
+
|
|
75
|
+
An OOF label, hash or fold declaration alone cannot authenticate a producer.
|
|
76
|
+
Current installed sessions have no such producer and therefore abstain. A
|
|
77
|
+
later LIVE binding needs its own actual fold-artifact qualification.
|
|
78
|
+
"""
|
|
79
|
+
required={'role','parent_packet_sha256','program_sha256','learning_identity',
|
|
80
|
+
'profile','allowed_programs','evidence'}
|
|
81
|
+
if type(context) is not dict or set(context)!=required:
|
|
82
|
+
raise ValueError('Exact internal diagnostic context required')
|
|
83
|
+
if (context['role']!='TRAIN_OOF' or context['parent_packet_sha256']!=digest(selected)
|
|
84
|
+
or context['program_sha256']!=digest(program)
|
|
85
|
+
or context['learning_identity']!=selected['learning_identity']):
|
|
86
|
+
raise ValueError('Only current fixed-parent TRAIN OOF may guide a transformation')
|
|
87
|
+
if not callable(authenticate) or authenticate(copy.deepcopy(context),copy.deepcopy(selected)) is not True:
|
|
88
|
+
raise ValueError('Actual OOF producer/artifacts have not been authenticated')
|
|
89
|
+
rows=context['profile'].get('diagnoses')
|
|
90
|
+
if type(rows) is not list or len(rows)>32 or len({r['name'] for r in rows})!=len(rows):
|
|
91
|
+
raise ValueError('Bounded distinct diagnostic hypotheses required')
|
|
92
|
+
for r in rows:
|
|
93
|
+
if not isinstance(r['name'],str) or any(type(r.get(k)) not in (int,float) or not 0<=r[k]<=1 for k in ('strength','confidence')):
|
|
94
|
+
raise ValueError('Finite bounded diagnostic strengths/confidences')
|
|
95
|
+
allowed=context['allowed_programs']
|
|
96
|
+
if type(allowed) is not list or len(allowed)>64 or len(set(allowed))!=len(allowed):
|
|
97
|
+
raise ValueError('Explicit bounded admitted program identities required')
|
|
98
|
+
return context
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class StructuralChoice(FamilyChoice):
|
|
102
|
+
"""Same coordinator checks/strict loss ordering, one typed growth proposal.
|
|
103
|
+
|
|
104
|
+
FamilyChoice's family-change precondition is deliberately replaced only
|
|
105
|
+
for this versioned structural case. Verification and anchor semantics stay
|
|
106
|
+
those of the original coordinator.
|
|
107
|
+
"""
|
|
108
|
+
def offer_growth(self, candidate):
|
|
109
|
+
if self.failed or self.done:raise RuntimeError('Terminal structural decision; no replay')
|
|
110
|
+
before = copy.deepcopy(self.best)
|
|
111
|
+
old = before['packet']; new = candidate
|
|
112
|
+
if new['task'] != 'binary' or old['task'] != 'binary':
|
|
113
|
+
raise ValueError('Existing binary growth operator only')
|
|
114
|
+
if new['recipe']['genome'] != AlgorithmGenome(growth_policy='depthwise').to_dict():
|
|
115
|
+
raise ValueError('Exact existing depthwise rule required')
|
|
116
|
+
try:
|
|
117
|
+
checked = self._checked(candidate)
|
|
118
|
+
if checked['loss'] < before['loss']:
|
|
119
|
+
self.best = checked
|
|
120
|
+
# Recheck retained predictions, including a candidate that lost.
|
|
121
|
+
again = self._checked(self.best['packet'])
|
|
122
|
+
if again != self.best: raise ValueError('Retained anchor changed')
|
|
123
|
+
self.checkpoint({'phase': 'STRUCTURAL', 'status': 'COMPLETED',
|
|
124
|
+
'retained': self.best['packet']['id'], 'candidate': new['id'],
|
|
125
|
+
'decision': 'ACCEPT' if self.best['packet']['id']==new['id'] else 'ROLLBACK'})
|
|
126
|
+
self.done=True
|
|
127
|
+
return self.best
|
|
128
|
+
except BaseException:
|
|
129
|
+
self.best = before
|
|
130
|
+
self.failed=True
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
def offer_transform(self, candidate, program):
|
|
134
|
+
"""Reuse original verified anchor/loss semantics for a typed child.
|
|
135
|
+
|
|
136
|
+
A new instance is used for each step, anchored to the current retained
|
|
137
|
+
weights. Strict ties retain the parent. No in-place update is claimed.
|
|
138
|
+
"""
|
|
139
|
+
if self.failed or self.done:raise RuntimeError('Terminal structural decision')
|
|
140
|
+
before=copy.deepcopy(self.best)
|
|
141
|
+
try:
|
|
142
|
+
if (candidate['family']!=self.anchor['family']
|
|
143
|
+
or candidate['params']!=self.anchor['params']
|
|
144
|
+
or candidate['recipe']['classes']!=self.anchor['recipe']['classes']
|
|
145
|
+
or candidate['recipe']['encoder']!=self.anchor['recipe']['encoder']
|
|
146
|
+
or learning_program(candidate)!=program
|
|
147
|
+
or learning_program(self.anchor)==program):
|
|
148
|
+
raise ValueError('Child must execute the proposed program with frozen numbers/data')
|
|
149
|
+
checked=self._checked(candidate)
|
|
150
|
+
if checked['loss']<before['loss']:self.best=checked
|
|
151
|
+
if self._checked(self.best['packet'])!=self.best:
|
|
152
|
+
raise ValueError('Retained parent/child predictions changed')
|
|
153
|
+
self.checkpoint({'phase':'EVOLUTION','status':'COMPLETED',
|
|
154
|
+
'parent':self.anchor['id'],'candidate':candidate['id'],
|
|
155
|
+
'retained':self.best['packet']['id'],
|
|
156
|
+
'decision':'ACCEPT' if self.best['packet']['id']==candidate['id'] else 'ROLLBACK'})
|
|
157
|
+
self.done=True
|
|
158
|
+
return self.best
|
|
159
|
+
except BaseException:
|
|
160
|
+
self.best=before;self.failed=True
|
|
161
|
+
raise
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class MorphTree(Consumer):
|
|
165
|
+
"""Original orchestration reached through the installed canonical API.
|
|
166
|
+
|
|
167
|
+
An exact current session is mandatory. A software session only supplies
|
|
168
|
+
explicitly constructed/retained weights; PREFIT stops before native work.
|
|
169
|
+
"""
|
|
170
|
+
_numeric_plan = LearningControl._numeric_plan
|
|
171
|
+
_log = LearningControl._log
|
|
172
|
+
|
|
173
|
+
def __init__(self, *, task='auto', families='boost', morphing='none', tuning='none', **kwargs):
|
|
174
|
+
super().__init__(task=task, families=families, morphing=morphing, tuning=tuning, **kwargs)
|
|
175
|
+
self._research_growth = False
|
|
176
|
+
self._evolving_program = False # New public source opts in; old installed v6 is preserved.
|
|
177
|
+
self._max_mutations = 2
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def baseline(cls, *, family='boost', **kwargs):
|
|
181
|
+
if family not in ('boost', 'bag') or set(kwargs) & {'families', 'morphing'}:
|
|
182
|
+
raise ValueError('Baseline requires one native family and no structural search')
|
|
183
|
+
return cls(families=family, morphing='none', **kwargs)
|
|
184
|
+
|
|
185
|
+
@classmethod
|
|
186
|
+
def adaptive(cls, **kwargs):
|
|
187
|
+
if set(kwargs) & {'families', 'morphing'}:
|
|
188
|
+
raise ValueError('Adaptive uses common native family exploration and diagnostic abstention')
|
|
189
|
+
return cls(families='auto', morphing='auto', **kwargs)
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def research(cls, *, operators, **kwargs):
|
|
193
|
+
if tuple(operators) != ('growth_policy:depthwise',):
|
|
194
|
+
raise ValueError('Explicit qualified research operator required')
|
|
195
|
+
obj = cls.adaptive(**kwargs)
|
|
196
|
+
obj._research_growth = True
|
|
197
|
+
return obj
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def mode(self):
|
|
201
|
+
if self._research_growth:
|
|
202
|
+
return 'research'
|
|
203
|
+
return 'adaptive' if self.families == 'auto' or self.morphing == 'auto' else 'baseline'
|
|
204
|
+
|
|
205
|
+
def _make_executor(self):
|
|
206
|
+
return require_session().executor(self)
|
|
207
|
+
|
|
208
|
+
def _execution_check(self):
|
|
209
|
+
require_session() # require_session already checks the same Session.
|
|
210
|
+
|
|
211
|
+
def _search_budget_open(self):
|
|
212
|
+
s=ACTIVE.get()
|
|
213
|
+
return s is None or s.mode!='PUBLIC_USER' or not s._search_closed
|
|
214
|
+
|
|
215
|
+
def _close_search_budget(self,reason):
|
|
216
|
+
s=ACTIVE.get()
|
|
217
|
+
if s is None or s.mode!='PUBLIC_USER':raise PermissionError('No ordinary search scope')
|
|
218
|
+
s.finish_search(reason)
|
|
219
|
+
|
|
220
|
+
def _finish_search_if_public(self,reason):
|
|
221
|
+
s=ACTIVE.get()
|
|
222
|
+
if s is not None and s.mode=='PUBLIC_USER':s.finish_search(reason)
|
|
223
|
+
|
|
224
|
+
def _begin_numeric_window(self):
|
|
225
|
+
s=ACTIVE.get()
|
|
226
|
+
if s is not None and s.mode=='PUBLIC_USER':
|
|
227
|
+
if s._search_deadline is None:s.begin_search()
|
|
228
|
+
else:s.check() # No renewal after family/structural work.
|
|
229
|
+
|
|
230
|
+
def _admit_learning_batch(self,task,family,params,kind):
|
|
231
|
+
s=require_session()
|
|
232
|
+
if s.mode=='PUBLIC_USER':s.admit_batch(task,family,asdict(params),kind=kind)
|
|
233
|
+
|
|
234
|
+
def _budget_stopped(self,stage,anchor,attempt,log,error):
|
|
235
|
+
self._close_search_budget('SEARCH_BUDGET_'+stage)
|
|
236
|
+
attempt['search_budget_stop']={'stage':stage,'retained':anchor['id'],
|
|
237
|
+
'reason':str(error),'tuning_complete':False}
|
|
238
|
+
log('search_budget_stopped',**copy.deepcopy(attempt['search_budget_stop']))
|
|
239
|
+
|
|
240
|
+
def _record_event(self, event):
|
|
241
|
+
require_session().event('ORCHESTRATION', value=event)
|
|
242
|
+
|
|
243
|
+
def _capture_report_packet(self, packet, model, selection, train):
|
|
244
|
+
"""Retain actual executed objects for post-fit verification, not authority."""
|
|
245
|
+
self._report_evidence[packet['id']] = (copy.deepcopy(packet), model,
|
|
246
|
+
copy.deepcopy(selection), copy.deepcopy(train))
|
|
247
|
+
|
|
248
|
+
def _verify_evolution_report(self, *, finalization=None):
|
|
249
|
+
from .evolution_report import verified_evolution_report
|
|
250
|
+
from .base import _payloads
|
|
251
|
+
from .support import risk
|
|
252
|
+
session=require_session()
|
|
253
|
+
from . import component_cache_v2 as cache
|
|
254
|
+
from .runtime_io import artifact_path
|
|
255
|
+
from .execution_v2 import validate_model
|
|
256
|
+
from .support import FamilyModel
|
|
257
|
+
snapshots=None
|
|
258
|
+
def components():
|
|
259
|
+
# Projection-local snapshot only. No reusable runtime/cache trust.
|
|
260
|
+
# Decode each completed component once,then recheck matching receipt,
|
|
261
|
+
# artifact hash and provenance on every packet/final verification.
|
|
262
|
+
nonlocal snapshots
|
|
263
|
+
if snapshots is None:
|
|
264
|
+
snapshots={}
|
|
265
|
+
for path in session.store.keys.glob('*/COMPLETE.json'):
|
|
266
|
+
session.check();row=cache._read(path)
|
|
267
|
+
request=cache.FitRequest(cache.canonical(row['request']))
|
|
268
|
+
receipt=session.store._result(request,path.parent)
|
|
269
|
+
native=validate_model(artifact_path(receipt['artifact']['path']),request.value())
|
|
270
|
+
normalized=_payloads(FamilyModel(row['request']['task'],[native],[1.]))[0]
|
|
271
|
+
snapshots[request.key]=(path,request,receipt,normalized)
|
|
272
|
+
return snapshots
|
|
273
|
+
def verify(packet):
|
|
274
|
+
p,model,search,train=self._report_evidence[packet['id']]
|
|
275
|
+
if p!=packet or digest(_payloads(model))!=packet['artifact_sha256']:
|
|
276
|
+
raise ValueError('Report refers to altered native weights or packet')
|
|
277
|
+
# Request keys are bound to exactly these native payloads by the
|
|
278
|
+
# committed store and executor. Reopen each receipt, do not count
|
|
279
|
+
# a family packet as one physical bagging member.
|
|
280
|
+
keys=[]
|
|
281
|
+
payloads=_payloads(model)
|
|
282
|
+
for path,request,receipt,normalized in components().values():
|
|
283
|
+
v=request.value()
|
|
284
|
+
if (normalized in payloads and v['task']==packet['task'] and v['rows']==len(train['y'])
|
|
285
|
+
and v['family']==packet['family']
|
|
286
|
+
and v['structure']==packet['recipe']['genome']):
|
|
287
|
+
session.check()
|
|
288
|
+
if session.store._result(request,path.parent)!=receipt:
|
|
289
|
+
raise ValueError('Report receipt/provenance changed after snapshot')
|
|
290
|
+
keys.append(request.key)
|
|
291
|
+
if len(keys)!=len(payloads):raise ValueError('Missing committed report components')
|
|
292
|
+
return {'packet_sha256':digest(packet),'component_keys':keys,
|
|
293
|
+
'prediction':model.probabilities_or_values(search['X']),
|
|
294
|
+
'TRAIN_primary':risk(packet['task'],train['y'],model.probabilities_or_values(train['X']),packet['recipe']['classes']),
|
|
295
|
+
'artifacts':{'native_payload_sha256':packet['artifact_sha256']}}
|
|
296
|
+
def oof(row):
|
|
297
|
+
p=session._diagnostic_proofs.get(row['context'])
|
|
298
|
+
if p is None or not session.verify_evolution_context(p['context'],p['parent']):
|
|
299
|
+
raise ValueError('Missing authenticated OOF proof for report')
|
|
300
|
+
from . import component_cache_v2 as cache
|
|
301
|
+
from .runtime_io import artifact_path
|
|
302
|
+
proof=cache._read(artifact_path(p['artifact']))
|
|
303
|
+
receipts=[x for f in proof['folds'] for x in f['receipts']]
|
|
304
|
+
keys={cache.FitRequest(cache.canonical(x['request'])).key for x in receipts}
|
|
305
|
+
cached=sum(r['event']=='CACHE_HIT' and r.get('key') in keys for r in session.store.events())
|
|
306
|
+
return {'context':row['context'],'parent':digest(p['parent']),
|
|
307
|
+
'program_sha256':p['context']['program_sha256'],'components':len(receipts),
|
|
308
|
+
'cache_components':cached,'mode':proof['mode'],'evidence':p['context']['evidence'],
|
|
309
|
+
'seconds':proof['acquisition_seconds_operational'],
|
|
310
|
+
'component_keys':sorted(keys),
|
|
311
|
+
'artifacts':{'path':p['artifact'],'sha256':p['sha256']}}
|
|
312
|
+
packets={n:p[0] for n,p in self._report_evidence.items()}
|
|
313
|
+
def metric(packet,pred):
|
|
314
|
+
search=self._report_evidence[packet['id']][2]
|
|
315
|
+
return risk(packet['task'],search['y'],pred,packet['recipe']['classes'])
|
|
316
|
+
final=self.last_attempt_['best_verified_anchor']
|
|
317
|
+
def unadopted(keys):
|
|
318
|
+
if session.mode!='PUBLIC_USER' or not self.last_attempt_.get('search_budget_stop'):
|
|
319
|
+
raise ValueError('Only explicit public typed-cutoff recovery may account unadopted work')
|
|
320
|
+
rows={}
|
|
321
|
+
for key in keys:
|
|
322
|
+
session.check();path=artifact_path(session.store.keys/key/'INTENT.json')
|
|
323
|
+
intent=cache._read(path);request=cache.FitRequest(cache.canonical(intent['request']))
|
|
324
|
+
if request.key!=key or intent['key']!=key:raise ValueError('Invalid incomplete component intent')
|
|
325
|
+
committed=key in components();artifact=None
|
|
326
|
+
if committed:
|
|
327
|
+
cp,r,receipt,_=components()[key]
|
|
328
|
+
if r!=request or session.store._result(r,cp.parent)!=receipt:
|
|
329
|
+
raise ValueError('Unadopted completed component changed')
|
|
330
|
+
artifact=copy.deepcopy(receipt['artifact'])
|
|
331
|
+
rows[key]={'key':key,'intent_sha256':cache.sha(path),
|
|
332
|
+
'committed':committed,'artifact':artifact,'used_in_final_model':False}
|
|
333
|
+
return rows
|
|
334
|
+
projection=verified_evolution_report(session.store.events(),packets=packets,
|
|
335
|
+
verify_packet=verify,verify_oof=oof,program=learning_program,metric=metric,
|
|
336
|
+
final_packet=final,mode=session.mode,finalization=finalization,
|
|
337
|
+
budget_recovery=unadopted if session.mode=='PUBLIC_USER' else None)
|
|
338
|
+
projection['component_audit']={'native_decodes':len(snapshots or {}),
|
|
339
|
+
'scope':'ONE_DECODE_PER_COMMITTED_COMPONENT_PER_PROJECTION',
|
|
340
|
+
'integrity':'Matching receipt/artifact/provenance rechecked on every packet and final verification;no cross-projection cache'}
|
|
341
|
+
m=self.report_['morphing'];m['verified_report']=projection
|
|
342
|
+
m.update(diagnosis_executed=projection['diagnosis_executed'],
|
|
343
|
+
decision='EVOLUTION_EVALUATED' if projection['steps'] else 'ABSTAIN_NO_ADMITTED_TRANSFORMATION',
|
|
344
|
+
information_acquisition={'new_OOF':projection['counts']['OOF_physical_fits_completed'],
|
|
345
|
+
'new_fits':projection['counts']['OOF_physical_fits_completed'],
|
|
346
|
+
'measured_seconds':projection['OOF_acquisition_method_seconds'],
|
|
347
|
+
'cost_scope':projection['cost_scope']})
|
|
348
|
+
if projection.get('budget_recovery'):
|
|
349
|
+
m['decision']='PARTIAL_SEARCH_BUDGET_VERIFIED_ANCHOR_RETURNED'
|
|
350
|
+
m['information_acquisition']['complete_contexts']=projection['counts']['OOF_contexts_acquired']
|
|
351
|
+
m['information_acquisition']['OOF_information_status']='Only committed fold proofs are diagnostic contexts;partial native OOF work is unadopted'
|
|
352
|
+
self.report_['limitations']=[
|
|
353
|
+
'No independent adaptive utility/performance/platform qualification',
|
|
354
|
+
'Child FULL_RETRAIN; no in-place warm start',
|
|
355
|
+
'Training export is an executor-bound fragment, not autonomous search',
|
|
356
|
+
'Ordinary user-data execution is not a scientific source/owner permit' if session.mode=='PUBLIC_USER' else 'General public training outside exact owned admission is blocked']
|
|
357
|
+
self.orchestration_report_=copy.deepcopy(self.report_)
|
|
358
|
+
return projection
|
|
359
|
+
|
|
360
|
+
def _parameters(self, params):
|
|
361
|
+
return require_session().parameters(params)
|
|
362
|
+
|
|
363
|
+
def _structure_packet(self, genome, classes, members, encoder):
|
|
364
|
+
session = require_session()
|
|
365
|
+
lineage = session.packet_lineage(genome) if hasattr(session, 'packet_lineage') else {}
|
|
366
|
+
if not lineage and genome!=AlgorithmGenome() and hasattr(self,'_active_evolution_parent'):
|
|
367
|
+
parent=self._active_evolution_parent
|
|
368
|
+
lineage={'gene':'growth_policy','parent_packet_sha256':digest(parent),
|
|
369
|
+
'from':copy.deepcopy(parent['recipe']['genome']),'to':genome.to_dict(),
|
|
370
|
+
'reason':'OOF_HEURISTIC_HYPOTHESIS_NOT_CAUSAL_UTILITY'}
|
|
371
|
+
return {'genome':genome.to_dict(), 'classes':list(range(len(classes))),
|
|
372
|
+
'members':members, 'encoder':encoder.to_dict(), 'lineage':lineage}
|
|
373
|
+
|
|
374
|
+
def _extra_opportunities(self):
|
|
375
|
+
if self._evolving_program and self.morphing=='auto':return self._max_mutations
|
|
376
|
+
return int(require_session().structural and self.morphing=='auto' and self._research_growth)
|
|
377
|
+
|
|
378
|
+
def _optional_optuna(self):
|
|
379
|
+
return require_session().optional_optuna()
|
|
380
|
+
|
|
381
|
+
def _numeric_admitted(self, executor):
|
|
382
|
+
return require_session().numeric_admitted(executor)
|
|
383
|
+
|
|
384
|
+
def _load_optuna(self):
|
|
385
|
+
return require_session().load_optuna()
|
|
386
|
+
|
|
387
|
+
def _diagnose(self, context):
|
|
388
|
+
if context != {'OOF':None, 'admitted_operators':[]}:
|
|
389
|
+
raise ValueError('No caller-supplied OOF/GUARD in this profile')
|
|
390
|
+
result = mutation_admission(self.morphing)
|
|
391
|
+
if self.morphing == 'auto':
|
|
392
|
+
result.update(diagnosis_status='ABSTAIN_NO_AUTHENTIC_FIXED_TRAIN_OOF',
|
|
393
|
+
decision='ABSTAIN_DIAGNOSTIC_GUIDANCE', utility='INCONCLUSIVE')
|
|
394
|
+
return result
|
|
395
|
+
|
|
396
|
+
def _structural_stage(self, selected, result, acquire, verify, selection, params, log, attempt):
|
|
397
|
+
if self._evolving_program:
|
|
398
|
+
try:return self._evolve_program(selected,result,acquire,verify,selection,params,log,attempt)
|
|
399
|
+
except SearchBudgetExhausted as exc:
|
|
400
|
+
anchor=copy.deepcopy(attempt['best_verified_anchor'])
|
|
401
|
+
self._budget_stopped('EVOLUTION',anchor,attempt,log,exc)
|
|
402
|
+
trace=attempt['evolution']
|
|
403
|
+
if trace['steps'] and trace['steps'][-1]['status']=='INTERRUPTED':
|
|
404
|
+
step=trace['steps'][-1]
|
|
405
|
+
log('evolution_interrupted',index=step['index'],parent=step['parent'],
|
|
406
|
+
retained=anchor['id'],reason='TYPED_SEARCH_BUDGET',
|
|
407
|
+
decision='RESTORE_VERIFIED_PARENT')
|
|
408
|
+
trace.update(stop='SEARCH_BUDGET_DURING_EVOLUTION',retained=anchor['id'],
|
|
409
|
+
accepted_program=learning_program(anchor))
|
|
410
|
+
log('evolution_complete',program=learning_program(anchor),stop=trace['stop'],
|
|
411
|
+
retained=anchor['id'],utility='NOT_DEMONSTRATED')
|
|
412
|
+
from .support import risk
|
|
413
|
+
prediction=verify(anchor,selection)
|
|
414
|
+
return anchor,{'packet':anchor,'prediction':prediction,
|
|
415
|
+
'loss':risk(anchor['task'],selection['y'],prediction,anchor['recipe']['classes'])}
|
|
416
|
+
session = require_session()
|
|
417
|
+
if not self._research_growth or not session.structural or self.morphing=='none' or selected['task']!='binary':
|
|
418
|
+
return selected, result
|
|
419
|
+
# Explicit static technical nomination, never inferred diagnostic value.
|
|
420
|
+
proposals = [p for p in propose_mutations(AlgorithmGenome(), {'diagnoses':[]})
|
|
421
|
+
if p.gene=='growth_policy' and p.genome.growth_policy=='depthwise']
|
|
422
|
+
if len(proposals)!=1: raise ValueError('Existing canonical operator missing/ambiguous')
|
|
423
|
+
proposal = proposals[0]
|
|
424
|
+
log('structural_proposal', gene=proposal.gene, genome=proposal.genome.to_dict(), parent_packet=copy.deepcopy(selected),
|
|
425
|
+
origin='EXPLICIT_STATIC_TECHNICAL_NOMINATION_NOT_SELF_DIAGNOSIS')
|
|
426
|
+
coordinator = StructuralChoice(selected, selection, verify=verify,
|
|
427
|
+
checkpoint=lambda e:log('structural_selection', **e))
|
|
428
|
+
technical={'origin':'STATIC_TECHNICAL_NOT_DIAGNOSTIC_GUIDANCE',
|
|
429
|
+
'gene':proposal.gene,'parent_id':selected['id'],
|
|
430
|
+
'parent_packet_sha256':digest(selected),'candidate_id':'growth:depthwise',
|
|
431
|
+
'candidate_genome':proposal.genome.to_dict(),'status':'INTENT'}
|
|
432
|
+
attempt['structural']=technical
|
|
433
|
+
try:
|
|
434
|
+
# The selected family is preserved; bag members reset just as in Base.
|
|
435
|
+
candidate = acquire('growth:depthwise', selected['family'], params, proposal.genome)
|
|
436
|
+
result = coordinator.offer_growth(candidate)
|
|
437
|
+
selected = result['packet']
|
|
438
|
+
attempt['best_verified_anchor'] = copy.deepcopy(selected)
|
|
439
|
+
technical.update(status='COMPLETED',retained=selected['id'],
|
|
440
|
+
decision='ACCEPT' if selected['id']=='growth:depthwise' else 'ROLLBACK',
|
|
441
|
+
SEARCH_primary=result['loss'])
|
|
442
|
+
except (TimeoutError, KeyboardInterrupt):
|
|
443
|
+
raise
|
|
444
|
+
except Exception as exc:
|
|
445
|
+
session.check() # STOP/deadline must not be swallowed as a candidate failure.
|
|
446
|
+
log('structural_rollback', reason=type(exc).__name__, message=str(exc),
|
|
447
|
+
retained=selected['id'])
|
|
448
|
+
result = coordinator.best
|
|
449
|
+
technical.update(status='FAILED_ROLLED_BACK',retained=selected['id'],
|
|
450
|
+
decision='ROLLBACK',error=type(exc).__name__,message=str(exc))
|
|
451
|
+
return selected, result
|
|
452
|
+
|
|
453
|
+
def _evolve_program(self, selected, result, acquire, verify, selection, params, log, attempt):
|
|
454
|
+
"""Continue from accepted native programs; no static winner preselection.
|
|
455
|
+
|
|
456
|
+
Uses the same component acquisition/coordinator. Every current child is
|
|
457
|
+
explicitly a FULL_RETRAIN, not warm start. Original weights stay intact.
|
|
458
|
+
Session/worker/store own physical caps, STOP and deadlines; this finite
|
|
459
|
+
opportunity limit never grants learning authority or resets their caps.
|
|
460
|
+
"""
|
|
461
|
+
session=require_session();current=learning_program(selected)
|
|
462
|
+
trace={'schema':'comp007-evolution-trace-v1','initial_program':copy.deepcopy(current),
|
|
463
|
+
'initial_anchor':selected['id'],'steps':[], 'diagnostics':[], 'evidence':session.mode,
|
|
464
|
+
'stop':'UNKNOWN','accepted_program':copy.deepcopy(current),
|
|
465
|
+
'continuation':'ACCEPTED_PROGRAM_AND_RETAINED_PARENT_WEIGHTS',
|
|
466
|
+
'warm_start_implemented':False,'global_optimum_claim':False,
|
|
467
|
+
'max_mutation_opportunities':self._max_mutations}
|
|
468
|
+
attempt['evolution']=trace
|
|
469
|
+
seen={digest(current)};attempted=0
|
|
470
|
+
def stopped(reason):
|
|
471
|
+
trace.update(stop=reason,retained=selected['id'])
|
|
472
|
+
log('evolution_complete',program=copy.deepcopy(current),stop=reason,
|
|
473
|
+
retained=selected['id'],utility='NOT_DEMONSTRATED')
|
|
474
|
+
return selected,result
|
|
475
|
+
if self.morphing=='none':return stopped('EXPLICIT_BASELINE')
|
|
476
|
+
if selected['task']!='binary':
|
|
477
|
+
from morphtree.diagnostics import ErrorProfiler
|
|
478
|
+
if not callable(getattr(ErrorProfiler,'profile_task',None)):
|
|
479
|
+
return stopped('ABSTAIN_NONBINARY_STRUCTURAL_DISPATCH_UNQUALIFIED')
|
|
480
|
+
if not callable(getattr(session,'evolution_context',None)):
|
|
481
|
+
return stopped('ABSTAIN_NO_AUTHENTIC_FIXED_PARENT_TRAIN_OOF')
|
|
482
|
+
while attempted<self._max_mutations:
|
|
483
|
+
if not self._search_budget_open():return stopped('SEARCH_BUDGET_BEFORE_DIAGNOSIS')
|
|
484
|
+
session.check()
|
|
485
|
+
context=session.evolution_context(copy.deepcopy(selected),copy.deepcopy(current))
|
|
486
|
+
if context is None:
|
|
487
|
+
trace['stop']='ABSTAIN_INSUFFICIENT_DIAGNOSTIC_INFORMATION';break
|
|
488
|
+
context=evolution_context(context,selected,current,
|
|
489
|
+
authenticate=getattr(session,'verify_evolution_context',None))
|
|
490
|
+
trace['diagnostics'].append({'parent':selected['id'],'program_sha256':digest(current),
|
|
491
|
+
'context_sha256':digest(context),'profile':copy.deepcopy(context['profile']),
|
|
492
|
+
'evidence':context['evidence'],'causal_status':'HYPOTHESES_NOT_IDENTIFIED_CAUSES'})
|
|
493
|
+
log('diagnosis_completed',parent=selected['id'],parent_packet_sha256=digest(selected),
|
|
494
|
+
context_sha256=digest(context),program_sha256=digest(current),evidence=context['evidence'])
|
|
495
|
+
strengths={r['name']:r['strength']*r['confidence'] for r in context['profile']['diagnoses']}
|
|
496
|
+
proposals=[]
|
|
497
|
+
for p in propose_mutations(AlgorithmGenome(**current['genome']),context['profile']):
|
|
498
|
+
# Existing proposals are hypotheses, never automatic admission.
|
|
499
|
+
try:child=transformed_program(current,p)
|
|
500
|
+
except ValueError as exc:
|
|
501
|
+
log('evolution_proposal_rejected',gene=p.gene,
|
|
502
|
+
reason='INCOMPATIBLE_TYPED_PROGRAM',detail=str(exc))
|
|
503
|
+
continue # No training or admission follows this proposal.
|
|
504
|
+
key=digest(child)
|
|
505
|
+
signal=p.reason.removeprefix('Hypothesis ').split(':',1)[0]
|
|
506
|
+
if key not in seen and key in context['allowed_programs'] and strengths.get(signal,0)>0:
|
|
507
|
+
proposals.append((p,child,key))
|
|
508
|
+
if not proposals:
|
|
509
|
+
trace['stop']='NO_ADMITTED_UNTRIED_DIAGNOSTIC_TRANSFORMATION';break
|
|
510
|
+
p,child,key=proposals[0];seen.add(key);attempted+=1
|
|
511
|
+
before=copy.deepcopy(selected)
|
|
512
|
+
step={'index':attempted,'parent':before['id'],'parent_program_sha256':digest(current),
|
|
513
|
+
'parent_packet_sha256':digest(before),
|
|
514
|
+
'child_program':copy.deepcopy(child),'gene':p.gene,'hypothesis':p.reason,
|
|
515
|
+
'diagnostic_context_sha256':digest(context),'diagnostic_evidence':context['evidence'],
|
|
516
|
+
'execution':'FULL_RETRAIN_REQUIRED_NOT_IN_PLACE','status':'INTENT',
|
|
517
|
+
'numeric_parameters_sha256':digest(before['params']),
|
|
518
|
+
'cost_before':copy.deepcopy(session.counters()) if callable(getattr(session,'counters',None)) else 'UNKNOWN'}
|
|
519
|
+
trace['steps'].append(step);log('evolution_intent',**copy.deepcopy(step))
|
|
520
|
+
coordinator=StructuralChoice(before,selection,verify=verify,
|
|
521
|
+
checkpoint=lambda e:log('evolution_selection',**e))
|
|
522
|
+
try:
|
|
523
|
+
family,genome=program_dispatch(child)
|
|
524
|
+
self._active_evolution_parent=copy.deepcopy(before)
|
|
525
|
+
candidate=acquire('evolution:'+str(attempted),family,type(params)(**before['params']),genome)
|
|
526
|
+
result=coordinator.offer_transform(candidate,child);selected=result['packet']
|
|
527
|
+
accepted=selected['id']==candidate['id']
|
|
528
|
+
step.update(status='COMPLETED',decision='ACCEPT' if accepted else 'ROLLBACK',
|
|
529
|
+
retained=selected['id'],SEARCH_primary=result['loss'])
|
|
530
|
+
if accepted:current=copy.deepcopy(child)
|
|
531
|
+
# Re-diagnosis next iteration is bound to THIS accepted parent.
|
|
532
|
+
attempt['best_verified_anchor']=copy.deepcopy(selected)
|
|
533
|
+
trace['accepted_program']=copy.deepcopy(current)
|
|
534
|
+
if not accepted and set(context['allowed_programs'])<=seen:
|
|
535
|
+
# The identical parent/context cannot nominate any unseen
|
|
536
|
+
# admitted program. Do not reacquire/reverify its OOF just
|
|
537
|
+
# to rediscover the same exhausted candidate set.
|
|
538
|
+
trace['stop']='NO_UNTRIED_ALLOWED_PROGRAM_AFTER_ROLLBACK'
|
|
539
|
+
break
|
|
540
|
+
except (TimeoutError,KeyboardInterrupt):
|
|
541
|
+
step.update(status='INTERRUPTED',decision='RESTORE_VERIFIED_PARENT',retained=before['id'])
|
|
542
|
+
raise
|
|
543
|
+
except Exception as exc:
|
|
544
|
+
step.update(status='FAILED',decision='ROLLBACK',retained=before['id'],error=type(exc).__name__)
|
|
545
|
+
selected=before;result=coordinator.best
|
|
546
|
+
session.check() # Never swallow STOP/deadline as a mere candidate error.
|
|
547
|
+
log('evolution_failure',retained=before['id'],error=type(exc).__name__)
|
|
548
|
+
trace['stop']='CANDIDATE_ERROR_ROLLBACK';break
|
|
549
|
+
finally:
|
|
550
|
+
self.__dict__.pop('_active_evolution_parent',None)
|
|
551
|
+
step['cost_after']=copy.deepcopy(session.counters()) if callable(getattr(session,'counters',None)) else 'UNKNOWN'
|
|
552
|
+
else:trace['stop']='MUTATION_OPPORTUNITY_BUDGET_EXHAUSTED'
|
|
553
|
+
trace['retained']=selected['id']
|
|
554
|
+
log('evolution_complete',program=copy.deepcopy(current),stop=trace['stop'],
|
|
555
|
+
retained=selected['id'],utility='NOT_DEMONSTRATED')
|
|
556
|
+
return selected,result
|
|
557
|
+
|
|
558
|
+
def fit(self, X, y):
|
|
559
|
+
# Post-learning report verification is part of the transaction too.
|
|
560
|
+
# Existing fitted objects remain recoverable if that verification fails.
|
|
561
|
+
before=dict(self.__dict__)
|
|
562
|
+
try:return self._fit_with_verified_reporting(X,y)
|
|
563
|
+
except BaseException:
|
|
564
|
+
attempt=copy.deepcopy(getattr(self,'last_attempt_',{}))
|
|
565
|
+
self.__dict__.clear();self.__dict__.update(before)
|
|
566
|
+
self.last_attempt_=attempt
|
|
567
|
+
if attempt.get('status')=='PASS':
|
|
568
|
+
self.last_attempt_.update(status='FAIL',stop_reason='REPORT_VERIFICATION_FAILED_RESTORED_PRIOR_OBJECT')
|
|
569
|
+
raise
|
|
570
|
+
|
|
571
|
+
def _fit_with_verified_reporting(self, X, y):
|
|
572
|
+
session = require_session()
|
|
573
|
+
session.bind_input(self, X, y)
|
|
574
|
+
if session.mode=='PUBLIC_USER':session.begin_search()
|
|
575
|
+
self._report_evidence = {}
|
|
576
|
+
self._workdir = str(session.root)
|
|
577
|
+
# Reuse the original finite fit loop (generated hooks documented in build).
|
|
578
|
+
with session.orchestration_operation(self):
|
|
579
|
+
result = LearningControl.fit(self, X, y)
|
|
580
|
+
self._input_sha256 = None
|
|
581
|
+
if session.mode=='FIXTURE' and self.report_['trials']:
|
|
582
|
+
self.report_['trial_evidence']='SIMULATED_OPTUNA_FIXTURE_NOT_REAL_TRIALS'
|
|
583
|
+
elif session.mode=='PUBLIC_USER' and self.report_['trials']:
|
|
584
|
+
self.report_['trial_evidence']='USER_DATA_REAL_OPTUNA_NOT_SCIENTIFIC_CONFIRMATION'
|
|
585
|
+
elif session.mode=='LIVE' and self.report_['trials']:
|
|
586
|
+
self.report_['trial_evidence']='EXACT_OWNER_BOUND_REAL_OPTUNA_TRIALS'
|
|
587
|
+
technical=self.last_attempt_.get('structural')
|
|
588
|
+
if technical is not None:
|
|
589
|
+
self.report_['morphing']['technical_structural_exploration']=copy.deepcopy(technical)
|
|
590
|
+
self.report_['morphing']['attempted']=[technical['candidate_id']]
|
|
591
|
+
key='accepted' if technical['decision']=='ACCEPT' else 'rollback'
|
|
592
|
+
self.report_['morphing'][key]=[technical['candidate_id']]
|
|
593
|
+
self.report_['stop_details']['structural']='FINITE_TECHNICAL_COMPARISON_'+technical['status']
|
|
594
|
+
evolution=self.last_attempt_.get('evolution')
|
|
595
|
+
if evolution is not None:
|
|
596
|
+
self.algorithm_program_=copy.deepcopy(evolution['accepted_program'])
|
|
597
|
+
self.program_history_=copy.deepcopy(evolution)
|
|
598
|
+
self.report_['morphing']['evolution']=copy.deepcopy(evolution)
|
|
599
|
+
if evolution['diagnostics']:
|
|
600
|
+
self.report_['morphing'].update(
|
|
601
|
+
diagnosis_status='CONSUMED_INTERNAL_FIXED_PARENT_PROFILES_'+session.mode,
|
|
602
|
+
diagnostic_profiles_consumed=len(evolution['diagnostics']),
|
|
603
|
+
rediagnosis='CURRENT_RETAINED_PARENT_BOUND_CONTEXTS',utility='INCONCLUSIVE')
|
|
604
|
+
self.report_['morphing']['attempted']=[s['index'] for s in evolution['steps']]
|
|
605
|
+
self.report_['morphing']['accepted']=[s['index'] for s in evolution['steps'] if s.get('decision')=='ACCEPT']
|
|
606
|
+
self.report_['morphing']['rollback']=[s['index'] for s in evolution['steps'] if s.get('decision')=='ROLLBACK']
|
|
607
|
+
self.report_['stop_details']['structural']=evolution['stop']
|
|
608
|
+
self.report_['accounting']['physical_fits']=session.physical
|
|
609
|
+
self.report_['execution_mode']=self.mode
|
|
610
|
+
if self.report_['tuning'] in ('OPTUNA_STOPPED_SEARCH_BUDGET','SKIPPED_SEARCH_BUDGET') or self.last_attempt_.get('search_budget_stop'):
|
|
611
|
+
self.report_['stop_details']['numeric']=self.report_['tuning']
|
|
612
|
+
self.report_['stop_reason']='SEARCH_BUDGET_RESERVED_FOR_FINALIZATION'
|
|
613
|
+
self.stop_reason_=self.report_['stop_reason']
|
|
614
|
+
self.last_attempt_['stop_reason']=self.stop_reason_
|
|
615
|
+
if not self.report_['trials'] and 'tuned' in self._variants:
|
|
616
|
+
self._variants.pop('tuned');self._selected_variant='pure'
|
|
617
|
+
if self.last_attempt_.get('search_budget_stop'):
|
|
618
|
+
self.report_['stop_details']['family']='COMPLETE' if not self.report_['families']['not_evaluated'] else 'PARTIAL_BUDGET'
|
|
619
|
+
self.report_['events'].append({'operation':'FIT_TERMINATION',
|
|
620
|
+
'model':'COMPLETE_VERIFIED_ANCHOR',
|
|
621
|
+
'search':'PARTIAL_BUDGET' if self.last_attempt_.get('search_budget_stop') else 'CONFIGURED_PATH_COMPLETE',
|
|
622
|
+
'tuning':self.report_['tuning'],'families':copy.deepcopy(self.report_['families'])})
|
|
623
|
+
self.orchestration_report_ = copy.deepcopy(self.report_)
|
|
624
|
+
# Existing V3 outer report is strict. Keep full cost/decisions in events;
|
|
625
|
+
# do not broaden its schema or drop native/recipe state.
|
|
626
|
+
allowed = {'schema','task','metric','classes','seed','best_family','SEARCH_primary',
|
|
627
|
+
'selection','tuning','trials','morphing','stop_reason','worker_admission','events',
|
|
628
|
+
'refit_performed','GUARD_used','final_model_role','wall_seconds',
|
|
629
|
+
'process_cpu_seconds','descendant_cpu','peak_RSS','cost_scope','limitations'}
|
|
630
|
+
extras = {k:v for k,v in self.report_.items() if k not in allowed}
|
|
631
|
+
self.report_ = {k:v for k,v in self.report_.items() if k in allowed}
|
|
632
|
+
self.report_['events'].append({'operation':'COMP003_ACCOUNTING_AND_SCOPE',
|
|
633
|
+
'details':extras, 'counts':session.counters(), 'scope':session.mode})
|
|
634
|
+
self._cost = session.counters()
|
|
635
|
+
self.last_attempt_['physical_fits']=session.physical
|
|
636
|
+
# Fixtures may be saved only under the explicitly versioned fixture
|
|
637
|
+
# envelope, and retain their software evidence label after loading.
|
|
638
|
+
from .producer import evolution_profile
|
|
639
|
+
form='product006-frozen-form-v1' if session.mode=='PUBLIC_USER' else 'product002-frozen-form-v1'
|
|
640
|
+
for variant in self._variants.values():
|
|
641
|
+
variant['recipe']['schema']=form
|
|
642
|
+
variant['exportable']=True
|
|
643
|
+
self.best_recipe_['schema']=form
|
|
644
|
+
if evolution is not None:
|
|
645
|
+
self._verify_evolution_report()
|
|
646
|
+
self._cost=session.counters()
|
|
647
|
+
return result
|
|
648
|
+
|
|
649
|
+
def get_algorithm_program(self):
|
|
650
|
+
self._fitted()
|
|
651
|
+
if not hasattr(self,'algorithm_program_'):
|
|
652
|
+
raise ValueError('This historical model has no recorded evolving training program')
|
|
653
|
+
return copy.deepcopy(self.algorithm_program_)
|
|
654
|
+
|
|
655
|
+
def describe_algorithm(self):
|
|
656
|
+
p=self.get_algorithm_program();history=self.program_history_
|
|
657
|
+
return {'program':p,'evolution':copy.deepcopy(history),
|
|
658
|
+
'training_code_export':'EXECUTOR_BOUND_TRAIN_ENCODED_FRAGMENT_NOT_STANDALONE',
|
|
659
|
+
'inference_export':'USE_EXISTING_EXPORT_PYTHON',
|
|
660
|
+
'utility':'INCONCLUSIVE','continuation':'FULL_RETRAIN_CHILDREN; RETAIN_PARENT_ON_ROLLBACK'}
|
|
661
|
+
|
|
662
|
+
def export_algorithm(self,path):
|
|
663
|
+
"""Export a validated native training fragment, NOT autonomous training.
|
|
664
|
+
|
|
665
|
+
The generated code requires this installed runtime and an already
|
|
666
|
+
admitted/prepared Session, TRAIN encoding, identity and inspect scope.
|
|
667
|
+
It neither creates a grant nor reproduces the whole evolutionary search.
|
|
668
|
+
Data and generated arbitrary source never enter executable expressions.
|
|
669
|
+
"""
|
|
670
|
+
self._io()
|
|
671
|
+
from .runtime_io import artifact_path, publish
|
|
672
|
+
p=self.get_algorithm_program();program_dispatch(p)
|
|
673
|
+
from morphtree.config import HyperParams
|
|
674
|
+
from pathlib import Path
|
|
675
|
+
from .admission import sha
|
|
676
|
+
HyperParams(**self.best_recipe_['params'])
|
|
677
|
+
text='''# MorphTree validated native training fragment; not standalone.
|
|
678
|
+
# Requires an admitted installed Session and encoded TRAIN with exact provenance.
|
|
679
|
+
# No search, OOF, new authority or arbitrary evaluation is generated here.
|
|
680
|
+
from morphtree.config import HyperParams
|
|
681
|
+
from morphtree._experimental.integrated import program_dispatch
|
|
682
|
+
from morphtree._experimental.execution_v2 import require_session
|
|
683
|
+
PROGRAM = %r
|
|
684
|
+
PARAMETERS = %r
|
|
685
|
+
SEED = %r
|
|
686
|
+
RUNTIME_MANIFEST_SHA256 = %r
|
|
687
|
+
EXPORT_SCOPE = 'EXECUTOR_BOUND_TRAIN_ENCODED_FRAGMENT_NOT_END_TO_END_SEARCH'
|
|
688
|
+
def train_encoded(X, y, *, identity):
|
|
689
|
+
family, genome = program_dispatch(PROGRAM)
|
|
690
|
+
session = require_session()
|
|
691
|
+
if session.engine.identity['manifest_sha256'] != RUNTIME_MANIFEST_SHA256:
|
|
692
|
+
raise ValueError('Training fragment requires its exact admitted runtime manifest')
|
|
693
|
+
return session.executor(None).fit_family(PROGRAM['task'], family, X, y,
|
|
694
|
+
seed=SEED, params=HyperParams(**PARAMETERS), genome=genome,
|
|
695
|
+
check=session.check, identity=identity)
|
|
696
|
+
''' % (p,copy.deepcopy(self.best_recipe_['params']),self.best_recipe_['seed'],
|
|
697
|
+
sha(Path(__file__).with_name('manifest.json')))
|
|
698
|
+
publish(artifact_path(path),text,lambda:None)
|
|
699
|
+
|
|
700
|
+
def get_mutation_history(self):
|
|
701
|
+
self.get_algorithm_program()
|
|
702
|
+
return copy.deepcopy(self.program_history_)
|
|
703
|
+
|
|
704
|
+
def get_recipe(self):
|
|
705
|
+
return self.recipe()
|
|
706
|
+
|
|
707
|
+
def get_search_report(self):
|
|
708
|
+
"""Verified projection when available; never relabel legacy summaries."""
|
|
709
|
+
self._fitted()
|
|
710
|
+
projection=self.report_.get('morphing',{}).get('verified_report')
|
|
711
|
+
if projection is None:
|
|
712
|
+
return {'status':'HISTORICAL_UNRECONCILED_REPORT','verified':False,
|
|
713
|
+
'report':self.report()}
|
|
714
|
+
value=copy.deepcopy(projection)
|
|
715
|
+
if not hasattr(self,'_report_evidence'):
|
|
716
|
+
value['recorded_status']=value['status']
|
|
717
|
+
value['status']='PERSISTED_REPORT_NOT_REVERIFIED'
|
|
718
|
+
value['verified']=False
|
|
719
|
+
value['reverification']='PERSISTED_REPORT_JOURNAL_NOT_REOPENED'
|
|
720
|
+
else:value['verified']=True
|
|
721
|
+
return value
|
|
722
|
+
|
|
723
|
+
@classmethod
|
|
724
|
+
def load(cls,path,*,sha256,workdir):
|
|
725
|
+
obj=super().load(path,sha256=sha256,workdir=workdir)
|
|
726
|
+
trace=obj.report_.get('morphing',{}).get('evolution')
|
|
727
|
+
if trace is not None:
|
|
728
|
+
# The report is descriptive, never fit authority. Reconstruct its
|
|
729
|
+
# final program only if it describes the safely loaded native form.
|
|
730
|
+
if type(trace) is not dict or trace.get('schema')!='comp007-evolution-trace-v1':
|
|
731
|
+
raise ValueError('Unknown program history')
|
|
732
|
+
expected=learning_program({'task':obj.task_,'family':obj.best_family_,
|
|
733
|
+
'recipe':obj.best_recipe_['structure']})
|
|
734
|
+
if trace.get('accepted_program')!=expected:
|
|
735
|
+
raise ValueError('Recorded learning program differs from loaded native model')
|
|
736
|
+
cap=trace.get('max_mutation_opportunities')
|
|
737
|
+
if type(cap) is not int or not 0<=cap<=8 or type(trace.get('steps')) is not list or len(trace['steps'])>cap:
|
|
738
|
+
raise ValueError('Invalid bounded program history')
|
|
739
|
+
obj.algorithm_program_=copy.deepcopy(expected)
|
|
740
|
+
obj.program_history_=copy.deepcopy(trace);obj._max_mutations=cap
|
|
741
|
+
return obj
|
|
742
|
+
|
|
743
|
+
def cost_report(self):
|
|
744
|
+
if hasattr(self, '_cost'):
|
|
745
|
+
return copy.deepcopy(self._cost)|{'scope':self.report_['worker_admission'],
|
|
746
|
+
'performance_qualified':False,'peak_RSS':'UNKNOWN'}
|
|
747
|
+
return super().cost_report()
|
|
748
|
+
|