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,170 @@
|
|
|
1
|
+
"""Finite workload-based allowances, not dataset size admission thresholds.
|
|
2
|
+
|
|
3
|
+
Coefficients are conservative engineering heuristics, not a completion-time or
|
|
4
|
+
RSS theorem. Explicit user allowances always win. No target values, scores or
|
|
5
|
+
EXTERNAL data enter the plan; no samples or evolutionary stages are removed.
|
|
6
|
+
"""
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def memory_snapshot(host_available):
|
|
11
|
+
"""Read-only OS limits, without changing a job/cgroup or foreign process.
|
|
12
|
+
|
|
13
|
+
Windows scope is the immediate job plus an optional supervisor-verified
|
|
14
|
+
process ceiling. Numeric context never holds a job handle, which would keep
|
|
15
|
+
a kill-on-last-handle-close cleanup job alive after its owner died.
|
|
16
|
+
Linux scope is the visible cgroup hierarchy and RLIMIT_AS. Unsupported or
|
|
17
|
+
unreadable limits are reported, never claimed as fully qualified coverage.
|
|
18
|
+
"""
|
|
19
|
+
import os
|
|
20
|
+
constraints=[];unavailable=[];private=None
|
|
21
|
+
if os.name=='nt':
|
|
22
|
+
import ctypes
|
|
23
|
+
from ctypes import wintypes as w
|
|
24
|
+
class Basic(ctypes.Structure):
|
|
25
|
+
_fields_=[('process_time',ctypes.c_int64),('job_time',ctypes.c_int64),('flags',w.DWORD),('min_ws',ctypes.c_size_t),('max_ws',ctypes.c_size_t),('active',w.DWORD),('affinity',ctypes.c_size_t),('priority',w.DWORD),('scheduling',w.DWORD)]
|
|
26
|
+
class IO(ctypes.Structure):_fields_=[(n,ctypes.c_uint64) for n in ('ro','wo','oo','rb','wb','ob')]
|
|
27
|
+
class Extended(ctypes.Structure):_fields_=[('basic',Basic),('io',IO),('process_limit',ctypes.c_size_t),('job_limit',ctypes.c_size_t),('process_peak',ctypes.c_size_t),('job_peak',ctypes.c_size_t)]
|
|
28
|
+
class Usage(ctypes.Structure):_fields_=[('current',ctypes.c_uint64),('peak',ctypes.c_uint64)]
|
|
29
|
+
class Process(ctypes.Structure):_fields_=[('size',w.DWORD),('faults',w.DWORD)]+[(n,ctypes.c_size_t) for n in ('peak_ws','ws','peak_paged','paged','peak_nonpaged','nonpaged','pagefile','peak_pagefile','private')]
|
|
30
|
+
kernel=ctypes.WinDLL('kernel32',use_last_error=True);psapi=ctypes.WinDLL('psapi',use_last_error=True)
|
|
31
|
+
kernel.QueryInformationJobObject.argtypes=[w.HANDLE,ctypes.c_int,ctypes.c_void_p,w.DWORD,ctypes.c_void_p]
|
|
32
|
+
psapi.GetProcessMemoryInfo.argtypes=[w.HANDLE,ctypes.c_void_p,w.DWORD]
|
|
33
|
+
current_process=Process();current_process.size=ctypes.sizeof(current_process)
|
|
34
|
+
if psapi.GetProcessMemoryInfo(ctypes.c_void_p(-1),ctypes.byref(current_process),current_process.size):private=current_process.private
|
|
35
|
+
else:unavailable.append('Windows_process_commit_usage')
|
|
36
|
+
for handle,scope in [(None,'immediate')]:
|
|
37
|
+
limits=Extended()
|
|
38
|
+
if kernel.QueryInformationJobObject(handle,9,ctypes.byref(limits),ctypes.sizeof(limits),None):
|
|
39
|
+
if limits.basic.flags&0x100:
|
|
40
|
+
if private is not None:constraints.append(dict(kind='Windows_process_commit',job_scope=scope,limit=limits.process_limit,current=private))
|
|
41
|
+
else:unavailable.append(scope+'_process_commit_usage')
|
|
42
|
+
if limits.basic.flags&0x200:
|
|
43
|
+
current=Usage()
|
|
44
|
+
if kernel.QueryInformationJobObject(handle,28,ctypes.byref(current),ctypes.sizeof(current),None):constraints.append(dict(kind='Windows_job_commit',job_scope=scope,limit=limits.job_limit,current=current.current))
|
|
45
|
+
else:unavailable.append(scope+'_job_current_usage')
|
|
46
|
+
else:unavailable.append(scope+'_job_unavailable')
|
|
47
|
+
declared=getattr(memory_snapshot,'_supervised_process_limit',None)
|
|
48
|
+
if declared is not None:
|
|
49
|
+
if type(declared) is not int or declared<1:raise ValueError('Positive supervisor-verified process ceiling')
|
|
50
|
+
if private is None:raise MemoryError('Cannot measure process commit under supervisor ceiling')
|
|
51
|
+
constraints.append(dict(kind='Windows_process_commit',job_scope='supervisor_verified_policy',limit=declared,current=private))
|
|
52
|
+
unavailable.append('Other_Windows_ancestor_jobs_not_enumerated')
|
|
53
|
+
elif os.name=='posix':
|
|
54
|
+
from pathlib import Path
|
|
55
|
+
try:
|
|
56
|
+
entries=Path('/proc/self/cgroup').read_text('ascii').splitlines()
|
|
57
|
+
for entry in entries:
|
|
58
|
+
hierarchy,controllers,relative=entry.split(':',2)
|
|
59
|
+
if hierarchy=='0' and not controllers:base=Path('/sys/fs/cgroup');names=('memory.max','memory.current')
|
|
60
|
+
elif 'memory' in controllers.split(','):base=Path('/sys/fs/cgroup/memory');names=('memory.limit_in_bytes','memory.usage_in_bytes')
|
|
61
|
+
else:continue
|
|
62
|
+
current=(base/relative.lstrip('/')).resolve()
|
|
63
|
+
if not current.is_relative_to(base):raise ValueError('Unsafe cgroup path')
|
|
64
|
+
for parent in (current,*current.parents):
|
|
65
|
+
if not parent.is_relative_to(base):break
|
|
66
|
+
if not (parent/names[0]).exists():continue
|
|
67
|
+
maximum=(parent/names[0]).read_text('ascii').strip()
|
|
68
|
+
if maximum!='max':constraints.append(dict(kind='Linux_visible_cgroup',limit=int(maximum),current=int((parent/names[1]).read_text('ascii').strip())))
|
|
69
|
+
except (OSError,ValueError):unavailable.append('Linux_cgroup_limits')
|
|
70
|
+
try:
|
|
71
|
+
import resource
|
|
72
|
+
limit=resource.getrlimit(resource.RLIMIT_AS)[0]
|
|
73
|
+
if limit!=resource.RLIM_INFINITY:
|
|
74
|
+
status=Path('/proc/self/status').read_text('ascii');virtual=next(int(line.split()[1])*1024 for line in status.splitlines() if line.startswith('VmSize:'))
|
|
75
|
+
constraints.append(dict(kind='POSIX_address_space',limit=limit,current=virtual))
|
|
76
|
+
except (OSError,ValueError,StopIteration,ImportError):unavailable.append('POSIX_address_space_limit')
|
|
77
|
+
for c in constraints:c['remaining']=max(0,c['limit']-c['current'])
|
|
78
|
+
effective=min([host_available]+[c['remaining'] for c in constraints])
|
|
79
|
+
return dict(host_available_bytes=host_available,effective_available_bytes=effective,process_private_bytes=private,constraints=constraints,unavailable=unavailable,scope='Visible limits plus optional externally verified process ceiling; unknown ancestors remain explicit. Commit/address-space and RAM are distinct,not a portable hardRSS proof')
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def memory_allowance(requested,available):
|
|
83
|
+
if type(available) is not int or available<0:raise ValueError('Nonnegative available memory observation')
|
|
84
|
+
if not available:raise MemoryError('No available RAM/commit/address-space headroom')
|
|
85
|
+
if requested=='auto':return max(1,available//2)
|
|
86
|
+
if type(requested) is not int or requested<1:raise ValueError('Positive byte allowance or auto')
|
|
87
|
+
return min(requested,available)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def fit_plan(rows,features,classes,params,mutations,trials,refit,available,time_budget,memory_budget):
|
|
91
|
+
if any(type(v) is not int or v<1 for v in (rows,features,classes)):raise ValueError('Positive shape/class dimensions')
|
|
92
|
+
m=params.n_estimators
|
|
93
|
+
# Worst of the two families; fresh diagnosis after every accepted parent.
|
|
94
|
+
# OOF consists of two half-TRAIN fits per member, never a new size sample.
|
|
95
|
+
native=1+m*(1+3*mutations+trials+int(refit))
|
|
96
|
+
scalar_classes=classes if classes>2 else 1
|
|
97
|
+
scalar=m*(2+3*mutations+trials+int(refit))*scalar_classes
|
|
98
|
+
row_passes=m*((2+2*mutations+trials)*.75*rows+int(refit)*rows)*scalar_classes
|
|
99
|
+
depth=min(params.max_depth,params.max_leaves-1)
|
|
100
|
+
tree_work=row_passes*features*depth
|
|
101
|
+
# Conservative sorting envelope even when bounded prepared bins hit.
|
|
102
|
+
sorting_work=rows*features*math.log2(max(2,rows))*native
|
|
103
|
+
try:automatic=max(150.,30.+tree_work/750000.+sorting_work/40000000.)
|
|
104
|
+
except OverflowError:raise ValueError('Workload outside finite Python time representation') from None
|
|
105
|
+
seconds=automatic if time_budget=='auto' else float(time_budget)
|
|
106
|
+
if not math.isfinite(seconds) or seconds<=0:raise ValueError('Positive finite time allowance')
|
|
107
|
+
memory=memory_allowance(memory_budget,available);cache=min(32*1024**2,memory//8)
|
|
108
|
+
minimum=rows*features*10+rows*(256+80*classes)+features*512+cache+scalar*(2*params.max_leaves-1)*1024
|
|
109
|
+
return dict(schema='SCALABLE_RESOURCE_PLAN_1',rows=rows,features=features,classes=classes,
|
|
110
|
+
memory_mode='auto' if memory_budget=='auto' else 'explicit',available_bytes=available,
|
|
111
|
+
memory_bytes=memory,estimated_workspace_bytes=minimum,prepared_cache_bytes=cache,
|
|
112
|
+
time_mode='auto' if time_budget=='auto' else 'explicit',seconds=seconds,
|
|
113
|
+
estimated_native_fit_ceiling=native,estimated_scalar_tree_ceiling=scalar,
|
|
114
|
+
scalar_row_passes=row_passes,tree_work_units=tree_work,sort_work_units=sorting_work,
|
|
115
|
+
automatic_seconds=automatic,seconds_floor=150.,tree_units_per_second=750000.,sort_units_per_second=40000000.,
|
|
116
|
+
guarantee='Heuristic forecast plus observed resource checks; not a hard peak or completion guarantee')
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def inference_seconds(rows,features,scalar_trees,depth):
|
|
120
|
+
try:seconds=max(150.,30.+rows*(features+scalar_trees*depth)/750000.)
|
|
121
|
+
except OverflowError:raise ValueError('Inference workload outside finite time representation') from None
|
|
122
|
+
if not math.isfinite(seconds):raise ValueError('Finite inference workload required')
|
|
123
|
+
return seconds
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def io_seconds(nbytes):return max(150.,nbytes/(1024**2))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def numeric_capacity_plan(plan,params,mutations,trials,refit,explicit_estimators):
|
|
130
|
+
"""Derive a finite final numeric domain without using any model score.
|
|
131
|
+
|
|
132
|
+
Initial screening and structural evolution keep their original capacity.
|
|
133
|
+
At most45percent of the initial configured time allowance funds final
|
|
134
|
+
numeric candidates/refit. Rates are the existing conservative work model,
|
|
135
|
+
not measured host speed; seeds/shape/explicit allowances are reproducible.
|
|
136
|
+
Memory includes retained candidate models.4096 is the existing HyperParams
|
|
137
|
+
numerical contract,not an admission limit on input observations/features.
|
|
138
|
+
"""
|
|
139
|
+
result=dict(plan);base=params.n_estimators;k=plan['classes'] if plan['classes']>2 else 1;n=plan['rows'];f=plan['features'];depth=min(params.max_depth,params.max_leaves-1)
|
|
140
|
+
per_seconds=n*f*depth*k*(.75*trials+int(refit))/plan['tree_units_per_second']+n*f*math.log2(max(2,n))*(trials+int(refit))/plan['sort_units_per_second']
|
|
141
|
+
per_bytes=(trials+int(refit))*((2*params.max_leaves-1)*1024*k+f*params.max_bins*64)
|
|
142
|
+
maximum=base;reason='Explicit estimator capacity' if explicit_estimators else 'Numeric tuning disabled'
|
|
143
|
+
if trials and not explicit_estimators:
|
|
144
|
+
time_max=max(base,math.floor(.45*plan['seconds']/max(1e-12,per_seconds)))
|
|
145
|
+
memory_max=base+max(0,plan['memory_bytes']-plan['estimated_workspace_bytes'])//max(1,per_bytes)
|
|
146
|
+
maximum=max(base,min(4096,time_max,memory_max));reason='Input shape and configured time/memory allowances only'
|
|
147
|
+
result['numeric_capacity']=dict(minimum_estimators=base,maximum_estimators=maximum,reason=reason,score_or_target_values_used=False,time_fraction=.45,seconds_per_estimator_all_numeric_and_refit=per_seconds,bytes_per_additional_estimator_all_candidates=per_bytes)
|
|
148
|
+
result['estimated_native_fit_ceiling']=1+base*(1+3*mutations)+(trials+int(refit))*maximum
|
|
149
|
+
result['estimated_scalar_tree_ceiling']=(base*(2+3*mutations)+(trials+int(refit))*maximum)*k
|
|
150
|
+
result['estimated_workspace_bytes']+=(maximum-base)*per_bytes
|
|
151
|
+
result['scalar_row_passes']=(base*(2+2*mutations)*.75*n+maximum*(.75*trials+int(refit))*n)*k
|
|
152
|
+
result['tree_work_units']=result['scalar_row_passes']*f*depth
|
|
153
|
+
result['sort_work_units']=n*f*math.log2(max(2,n))*result['estimated_native_fit_ceiling']
|
|
154
|
+
result['expanded_work_forecast_seconds']=max(150.,30.+result['tree_work_units']/plan['tree_units_per_second']+result['sort_work_units']/plan['sort_units_per_second'])
|
|
155
|
+
# The selected numerical domain is fixed above. Do not recalculate it
|
|
156
|
+
# from an extended deadline:that would create a capacity/time feedback loop.
|
|
157
|
+
# Auto time must cover the work envelope it selected;explicit deadlines
|
|
158
|
+
# remain exact caller policy and can still stop a best-effort fit early.
|
|
159
|
+
initial_seconds=result['seconds']
|
|
160
|
+
extension=0.
|
|
161
|
+
if plan.get('time_mode')=='auto' and maximum>base:
|
|
162
|
+
extension=max(0.,result['expanded_work_forecast_seconds']-initial_seconds)
|
|
163
|
+
if extension:
|
|
164
|
+
result['seconds']=result['expanded_work_forecast_seconds']
|
|
165
|
+
result['automatic_seconds']=result['seconds']
|
|
166
|
+
result['numeric_capacity']['initial_time_allowance_seconds']=initial_seconds
|
|
167
|
+
result['numeric_capacity']['automatic_deadline_extension_seconds']=extension
|
|
168
|
+
result['numeric_capacity']['domain_recomputed_after_extension']=False
|
|
169
|
+
result['numeric_capacity']['forecast_within_configured_time']=result['expanded_work_forecast_seconds']<=result['seconds'] or math.isclose(result['expanded_work_forecast_seconds'],result['seconds'],rel_tol=1e-12,abs_tol=1e-12)
|
|
170
|
+
return result
|