diffract-core 0.2.1__py3-none-any.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.
- diffract/__init__.py +52 -0
- diffract/configs/fast_speed_without_disk.ini +48 -0
- diffract/configs/hybrid.ini +74 -0
- diffract/configs/sqlite.ini +62 -0
- diffract/containers.py +405 -0
- diffract/core/__init__.py +23 -0
- diffract/core/cache/__init__.py +24 -0
- diffract/core/cache/containers.py +93 -0
- diffract/core/cache/interface.py +117 -0
- diffract/core/cache/redis_manager.py +464 -0
- diffract/core/cache/simple_manager.py +478 -0
- diffract/core/compute/__init__.py +46 -0
- diffract/core/compute/config.py +48 -0
- diffract/core/compute/containers.py +81 -0
- diffract/core/compute/decorator.py +125 -0
- diffract/core/compute/exceptions.py +31 -0
- diffract/core/compute/execution/__init__.py +14 -0
- diffract/core/compute/execution/_types.py +70 -0
- diffract/core/compute/execution/aggregation.py +74 -0
- diffract/core/compute/execution/aggregation_runner.py +362 -0
- diffract/core/compute/execution/enums.py +38 -0
- diffract/core/compute/execution/executor.py +125 -0
- diffract/core/compute/execution/parameter_runner.py +125 -0
- diffract/core/compute/execution/restrictions.py +61 -0
- diffract/core/compute/execution/strategy.py +118 -0
- diffract/core/compute/execution/utils.py +112 -0
- diffract/core/compute/extensions/__init__.py +0 -0
- diffract/core/compute/extensions/power_law.py +1051 -0
- diffract/core/compute/extensions/rmt.py +150 -0
- diffract/core/compute/extensions/utils.py +36 -0
- diffract/core/compute/field_signature.py +183 -0
- diffract/core/compute/kernels/__init__.py +48 -0
- diffract/core/compute/kernels/alignment.py +106 -0
- diffract/core/compute/kernels/heavy_tailed.py +394 -0
- diffract/core/compute/kernels/marchenko_pastur.py +99 -0
- diffract/core/compute/kernels/mat_decomposition.py +101 -0
- diffract/core/compute/kernels/mat_properties.py +53 -0
- diffract/core/compute/kernels/model_quality.py +27 -0
- diffract/core/compute/kernels/norms.py +88 -0
- diffract/core/compute/kernels/ranks.py +35 -0
- diffract/core/compute/kernels/tracy_widom.py +36 -0
- diffract/core/compute/metadata.py +67 -0
- diffract/core/compute/registry.py +485 -0
- diffract/core/constants.py +147 -0
- diffract/core/data/__init__.py +32 -0
- diffract/core/data/interface.py +304 -0
- diffract/core/data/metadata/__init__.py +15 -0
- diffract/core/data/metadata/containers.py +60 -0
- diffract/core/data/metadata/interface.py +208 -0
- diffract/core/data/metadata/sqlite_index.py +604 -0
- diffract/core/data/nn/__init__.py +43 -0
- diffract/core/data/nn/aggregates/__init__.py +35 -0
- diffract/core/data/nn/aggregates/metadata.py +96 -0
- diffract/core/data/nn/aggregates/proxy.py +57 -0
- diffract/core/data/nn/aggregates/repository.py +87 -0
- diffract/core/data/nn/aggregates/view.py +307 -0
- diffract/core/data/nn/containers.py +86 -0
- diffract/core/data/nn/extractors/__init__.py +49 -0
- diffract/core/data/nn/extractors/base.py +300 -0
- diffract/core/data/nn/extractors/factory.py +234 -0
- diffract/core/data/nn/extractors/flax.py +112 -0
- diffract/core/data/nn/extractors/handlers/__init__.py +39 -0
- diffract/core/data/nn/extractors/handlers/base.py +110 -0
- diffract/core/data/nn/extractors/handlers/flax_handlers.py +71 -0
- diffract/core/data/nn/extractors/handlers/numpy_handlers.py +63 -0
- diffract/core/data/nn/extractors/handlers/onnx_handlers.py +67 -0
- diffract/core/data/nn/extractors/handlers/tensorflow_handlers.py +82 -0
- diffract/core/data/nn/extractors/handlers/torch_handlers.py +124 -0
- diffract/core/data/nn/extractors/interface.py +56 -0
- diffract/core/data/nn/extractors/numpy.py +94 -0
- diffract/core/data/nn/extractors/onnx.py +108 -0
- diffract/core/data/nn/extractors/tensorflow.py +99 -0
- diffract/core/data/nn/extractors/torch.py +204 -0
- diffract/core/data/nn/params/__init__.py +27 -0
- diffract/core/data/nn/params/interface.py +402 -0
- diffract/core/data/nn/params/metadata.py +110 -0
- diffract/core/data/nn/params/proxy.py +93 -0
- diffract/core/data/nn/params/repository.py +45 -0
- diffract/core/data/nn/params/schema.py +82 -0
- diffract/core/data/nn/params/view.py +393 -0
- diffract/core/data/proxy.py +246 -0
- diffract/core/data/repository.py +227 -0
- diffract/core/data/utils.py +33 -0
- diffract/core/data/view.py +389 -0
- diffract/core/export/__init__.py +27 -0
- diffract/core/export/containers.py +47 -0
- diffract/core/export/exporters.py +191 -0
- diffract/core/export/formatters/__init__.py +23 -0
- diffract/core/export/formatters/base.py +68 -0
- diffract/core/export/formatters/dict_formatter.py +38 -0
- diffract/core/export/formatters/json_formatter.py +58 -0
- diffract/core/export/formatters/list_formatter.py +41 -0
- diffract/core/export/formatters/pandas_formatter.py +86 -0
- diffract/core/export/formatters/polars_formatter.py +86 -0
- diffract/core/export/formatters/registry.py +84 -0
- diffract/core/export/interface.py +105 -0
- diffract/core/parallel/__init__.py +21 -0
- diffract/core/parallel/containers.py +57 -0
- diffract/core/parallel/execution.py +91 -0
- diffract/core/parallel/runtime.py +91 -0
- diffract/core/storage/__init__.py +35 -0
- diffract/core/storage/base_manager.py +330 -0
- diffract/core/storage/containers.py +122 -0
- diffract/core/storage/hdf5_manager.py +856 -0
- diffract/core/storage/hybrid_manager.py +218 -0
- diffract/core/storage/interface.py +222 -0
- diffract/core/storage/metadata.py +89 -0
- diffract/core/storage/ram_manager.py +159 -0
- diffract/core/storage/sqlite_manager.py +1062 -0
- diffract/core/storage/zarr_manager.py +992 -0
- diffract/core/utils/__init__.py +45 -0
- diffract/core/utils/build.py +46 -0
- diffract/core/utils/exceptions.py +11 -0
- diffract/core/utils/hashing.py +90 -0
- diffract/core/utils/imports.py +292 -0
- diffract/core/utils/math.py +15 -0
- diffract/py.typed +0 -0
- diffract/session/__init__.py +24 -0
- diffract/session/errors.py +17 -0
- diffract/session/field_cache.py +216 -0
- diffract/session/namespaces/__init__.py +15 -0
- diffract/session/namespaces/compute/__init__.py +219 -0
- diffract/session/namespaces/models/__init__.py +282 -0
- diffract/session/namespaces/models/parameters/__init__.py +133 -0
- diffract/session/namespaces/models/parameters/meta_patcher.py +167 -0
- diffract/session/namespaces/results/__init__.py +378 -0
- diffract/session/namespaces/results/eraser.py +129 -0
- diffract/session/namespaces/results/injester.py +249 -0
- diffract/session/namespaces/utils/__init__.py +141 -0
- diffract/session/namespaces/utils/merger.py +295 -0
- diffract/session/namespaces/viz/__init__.py +91 -0
- diffract/session/namespaces/viz/_utils.py +14 -0
- diffract/session/namespaces/viz/box.py +207 -0
- diffract/session/namespaces/viz/grid.py +160 -0
- diffract/session/namespaces/viz/heatmap.py +164 -0
- diffract/session/namespaces/viz/scatter.py +202 -0
- diffract/session/namespaces/viz/sparkline.py +270 -0
- diffract/session/namespaces/viz/violin.py +212 -0
- diffract/session/session.py +260 -0
- diffract/session/utils.py +81 -0
- diffract/viz/__init__.py +42 -0
- diffract/viz/data/__init__.py +34 -0
- diffract/viz/data/detection.py +57 -0
- diffract/viz/data/extraction.py +117 -0
- diffract/viz/data/filtering.py +57 -0
- diffract/viz/data/ordering.py +129 -0
- diffract/viz/data/provider.py +99 -0
- diffract/viz/data/types.py +98 -0
- diffract/viz/plots/__init__.py +37 -0
- diffract/viz/plots/base/__init__.py +20 -0
- diffract/viz/plots/base/axis.py +219 -0
- diffract/viz/plots/base/coloraxis.py +133 -0
- diffract/viz/plots/base/configurator.py +18 -0
- diffract/viz/plots/base/jitter.py +368 -0
- diffract/viz/plots/base/line.py +197 -0
- diffract/viz/plots/base/marker.py +278 -0
- diffract/viz/plots/base/overlay.py +14 -0
- diffract/viz/plots/base/plot.py +110 -0
- diffract/viz/plots/base/update.py +50 -0
- diffract/viz/plots/boxplot.py +283 -0
- diffract/viz/plots/cluster.py +374 -0
- diffract/viz/plots/heatmap.py +168 -0
- diffract/viz/plots/scatter.py +233 -0
- diffract/viz/plots/sparkline.py +405 -0
- diffract/viz/plots/subplots/__init__.py +32 -0
- diffract/viz/plots/subplots/coloraxis.py +339 -0
- diffract/viz/plots/subplots/factory.py +713 -0
- diffract/viz/plots/subplots/grid.py +214 -0
- diffract/viz/plots/subplots/layout.py +370 -0
- diffract/viz/plots/subplots/spec.py +39 -0
- diffract/viz/plots/violin.py +298 -0
- diffract/viz/renderer.py +513 -0
- diffract/viz/styling/__init__.py +78 -0
- diffract/viz/styling/palettes/__init__.py +20 -0
- diffract/viz/styling/palettes/bundle.py +16 -0
- diffract/viz/styling/palettes/color.py +83 -0
- diffract/viz/styling/palettes/dashes.py +27 -0
- diffract/viz/styling/palettes/symbols.py +42 -0
- diffract/viz/styling/resolvers/__init__.py +11 -0
- diffract/viz/styling/resolvers/categorical.py +68 -0
- diffract/viz/styling/resolvers/color.py +77 -0
- diffract/viz/styling/resolvers/numeric.py +108 -0
- diffract/viz/styling/sources.py +27 -0
- diffract/viz/styling/theme/__init__.py +29 -0
- diffract/viz/styling/theme/applier.py +114 -0
- diffract/viz/styling/theme/components.py +66 -0
- diffract/viz/styling/theme/presets.py +58 -0
- diffract/viz/styling/theme/theme.py +36 -0
- diffract_core-0.2.1.dist-info/METADATA +530 -0
- diffract_core-0.2.1.dist-info/RECORD +193 -0
- diffract_core-0.2.1.dist-info/WHEEL +4 -0
- diffract_core-0.2.1.dist-info/licenses/LICENSE +201 -0
- diffract_core-0.2.1.dist-info/licenses/NOTICE +2 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Session-level cache for field availability.
|
|
2
|
+
|
|
3
|
+
Caches the result of list_fields_by_uid() to avoid repeated expensive
|
|
4
|
+
storage scans when multiple export_metrics() calls are made without
|
|
5
|
+
intervening compute.apply() or other mutation operations.
|
|
6
|
+
|
|
7
|
+
The cache uses a generation counter for lazy invalidation: when a mutation
|
|
8
|
+
occurs, the generation increments and cached data is considered stale.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from collections.abc import Iterable
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class SessionFieldCache:
|
|
22
|
+
"""Cache for field availability across session read operations.
|
|
23
|
+
|
|
24
|
+
This cache stores the mapping of parameter UID -> list of available fields,
|
|
25
|
+
avoiding repeated calls to list_fields_by_uid() which can be expensive
|
|
26
|
+
for large parameter collections.
|
|
27
|
+
|
|
28
|
+
The cache is automatically invalidated when mutating operations occur
|
|
29
|
+
(compute, add, erase, merge). It can also be incrementally updated
|
|
30
|
+
after compute() to avoid full rescans.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
cache = SessionFieldCache()
|
|
34
|
+
|
|
35
|
+
# First export_metrics() populates the cache
|
|
36
|
+
fields_by_uid = view.list_fields_by_uid()
|
|
37
|
+
cache.set(fields_by_uid)
|
|
38
|
+
|
|
39
|
+
# Second export_metrics() uses cached data
|
|
40
|
+
if cache.is_valid:
|
|
41
|
+
fields_by_uid = cache.get()
|
|
42
|
+
|
|
43
|
+
# compute.apply() invalidates the cache
|
|
44
|
+
cache.invalidate()
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
_generation: int = field(default=0, repr=False)
|
|
48
|
+
_fields_by_uid: dict[str, list[str]] | None = field(default=None, repr=False)
|
|
49
|
+
|
|
50
|
+
def get(self) -> dict[str, list[str]] | None:
|
|
51
|
+
"""Return cached field mapping or None if not populated.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dictionary mapping parameter UID to list of field names,
|
|
55
|
+
or None if cache is not populated.
|
|
56
|
+
"""
|
|
57
|
+
return self._fields_by_uid
|
|
58
|
+
|
|
59
|
+
def set(self, fields_by_uid: dict[str, list[str]]) -> None:
|
|
60
|
+
"""Store field mapping in cache.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
fields_by_uid: Mapping of parameter UID to list of available fields.
|
|
64
|
+
"""
|
|
65
|
+
self._fields_by_uid = fields_by_uid
|
|
66
|
+
logger.debug(
|
|
67
|
+
"Field cache populated with %d entries at generation %d",
|
|
68
|
+
len(fields_by_uid),
|
|
69
|
+
self._generation,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def update(self, fields_by_uid: dict[str, list[str]]) -> None:
|
|
73
|
+
"""Merge additional field mappings into the existing cache.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
fields_by_uid: Mapping of parameter UID to list of available
|
|
77
|
+
fields to merge into the current cache contents.
|
|
78
|
+
"""
|
|
79
|
+
self._fields_by_uid.update(**fields_by_uid)
|
|
80
|
+
|
|
81
|
+
def invalidate(self) -> None:
|
|
82
|
+
"""Invalidate cache (called after mutations).
|
|
83
|
+
|
|
84
|
+
Increments the generation counter and clears cached data.
|
|
85
|
+
This should be called after any operation that modifies storage:
|
|
86
|
+
compute.apply(), models.add(), models.erase(), results.erase(),
|
|
87
|
+
utils.merge_other_session().
|
|
88
|
+
"""
|
|
89
|
+
self._fields_by_uid = None
|
|
90
|
+
self._generation += 1
|
|
91
|
+
logger.debug("Field cache invalidated, new generation: %d", self._generation)
|
|
92
|
+
|
|
93
|
+
def add_computed_fields(
|
|
94
|
+
self,
|
|
95
|
+
affected_uids: Iterable[str],
|
|
96
|
+
new_fields: Iterable[str],
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Incrementally update cache after compute.apply() operation.
|
|
99
|
+
|
|
100
|
+
Instead of full invalidation, this method adds newly computed fields
|
|
101
|
+
to existing cache entries. This is an optimization for cases where
|
|
102
|
+
we know exactly which fields were added.
|
|
103
|
+
|
|
104
|
+
If cache is not populated, this is a no-op.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
affected_uids: UIDs of parameters that were computed.
|
|
108
|
+
new_fields: Field names that were produced by the computation.
|
|
109
|
+
"""
|
|
110
|
+
if self._fields_by_uid is None:
|
|
111
|
+
logger.debug("Skipping incremental update: cache not populated")
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
new_fields_set = set(new_fields)
|
|
115
|
+
updated_count = 0
|
|
116
|
+
|
|
117
|
+
for uid in affected_uids:
|
|
118
|
+
if uid in self._fields_by_uid:
|
|
119
|
+
existing = set(self._fields_by_uid[uid])
|
|
120
|
+
if not new_fields_set.issubset(existing):
|
|
121
|
+
existing.update(new_fields_set)
|
|
122
|
+
self._fields_by_uid[uid] = sorted(existing)
|
|
123
|
+
updated_count += 1
|
|
124
|
+
|
|
125
|
+
logger.debug(
|
|
126
|
+
"Incrementally updated %d cache entries with fields: %s",
|
|
127
|
+
updated_count,
|
|
128
|
+
", ".join(sorted(new_fields_set)),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def remove_fields_by_uids(
|
|
132
|
+
self, uids: Iterable[str], fields_to_remove: Iterable[str]
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Remove fields from cache entries after results.erase().
|
|
135
|
+
|
|
136
|
+
Instead of full invalidation, this method removes specified fields
|
|
137
|
+
from specified cache entries. This is an optimization for results.erase().
|
|
138
|
+
|
|
139
|
+
If cache is not populated, this is a no-op.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
uids: UIDs of cache entries to remove fields from.
|
|
143
|
+
fields_to_remove: Field names that were erased.
|
|
144
|
+
"""
|
|
145
|
+
if self._fields_by_uid is None:
|
|
146
|
+
logger.debug("Skipping field removal: cache not populated")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
fields_set = set(fields_to_remove)
|
|
150
|
+
updated_count = 0
|
|
151
|
+
|
|
152
|
+
for uid in uids:
|
|
153
|
+
original_len = len(self._fields_by_uid[uid])
|
|
154
|
+
self._fields_by_uid[uid] = [
|
|
155
|
+
f for f in self._fields_by_uid[uid] if f not in fields_set
|
|
156
|
+
]
|
|
157
|
+
if len(self._fields_by_uid[uid]) != original_len:
|
|
158
|
+
updated_count += 1
|
|
159
|
+
|
|
160
|
+
logger.debug(
|
|
161
|
+
"Removed fields from %d cache entries: %s",
|
|
162
|
+
updated_count,
|
|
163
|
+
", ".join(sorted(fields_set)),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def remove_uids(self, uids_to_remove: Iterable[str]) -> None:
|
|
167
|
+
"""Remove UIDs from cache after models.erase().
|
|
168
|
+
|
|
169
|
+
Instead of full invalidation, this method removes specified UIDs
|
|
170
|
+
from the cache. This is an optimization for models.erase().
|
|
171
|
+
|
|
172
|
+
If cache is not populated, this is a no-op.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
uids_to_remove: UIDs that were erased from storage.
|
|
176
|
+
"""
|
|
177
|
+
if self._fields_by_uid is None:
|
|
178
|
+
logger.debug("Skipping UID removal: cache not populated")
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
removed_count = 0
|
|
182
|
+
for uid in uids_to_remove:
|
|
183
|
+
if uid in self._fields_by_uid:
|
|
184
|
+
del self._fields_by_uid[uid]
|
|
185
|
+
removed_count += 1
|
|
186
|
+
|
|
187
|
+
logger.debug("Removed %d UIDs from cache", removed_count)
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def is_valid(self) -> bool:
|
|
191
|
+
"""Check if cache contains valid data.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
True if cache is populated and ready for use.
|
|
195
|
+
"""
|
|
196
|
+
return self._fields_by_uid is not None
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def generation(self) -> int:
|
|
200
|
+
"""Current generation counter.
|
|
201
|
+
|
|
202
|
+
Increments on each invalidation. Useful for debugging.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Current generation number.
|
|
206
|
+
"""
|
|
207
|
+
return self._generation
|
|
208
|
+
|
|
209
|
+
def clear(self) -> None:
|
|
210
|
+
"""Clear cache completely and reset generation.
|
|
211
|
+
|
|
212
|
+
Use this for full session reset, not for normal invalidation.
|
|
213
|
+
"""
|
|
214
|
+
self._fields_by_uid = None
|
|
215
|
+
self._generation = 0
|
|
216
|
+
logger.debug("Field cache cleared and reset")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Namespace helpers for Session facade."""
|
|
2
|
+
|
|
3
|
+
from .compute import ComputeNamespace
|
|
4
|
+
from .models import ModelsNamespace
|
|
5
|
+
from .results import ResultsNamespace
|
|
6
|
+
from .utils import UtilsNamespace
|
|
7
|
+
from .viz import VizNamespace
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ComputeNamespace",
|
|
11
|
+
"ModelsNamespace",
|
|
12
|
+
"ResultsNamespace",
|
|
13
|
+
"UtilsNamespace",
|
|
14
|
+
"VizNamespace",
|
|
15
|
+
]
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""Compute namespace for Session."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import difflib
|
|
6
|
+
import logging
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from dependency_injector.wiring import Provide, inject
|
|
10
|
+
|
|
11
|
+
from diffract.core.compute import KernelConfig, KernelExecutor, KernelRegistry
|
|
12
|
+
from diffract.core.compute.execution import (
|
|
13
|
+
KernelApplyLevel,
|
|
14
|
+
KernelExecutionProtocol,
|
|
15
|
+
KernelRestrictions,
|
|
16
|
+
)
|
|
17
|
+
from diffract.core.data.nn.params.interface import IParameterRepository
|
|
18
|
+
from diffract.core.utils.exceptions import format_exception_message
|
|
19
|
+
from diffract.session.errors import KernelNotFoundError, SessionError
|
|
20
|
+
from diffract.session.session import Session, SessionContext
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from collections.abc import Callable
|
|
24
|
+
|
|
25
|
+
from diffract.core.data.nn.params.schema import ParameterType
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _did_you_mean(name: str, candidates: list[str]) -> str:
|
|
31
|
+
close = difflib.get_close_matches(name, sorted(set(candidates)), n=3, cutoff=0.6)
|
|
32
|
+
return f" Did you mean: {', '.join(close)}?" if close else ""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ComputeNamespace:
|
|
36
|
+
"""Kernel compute API for Session."""
|
|
37
|
+
|
|
38
|
+
@inject
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
session_or_context: Session | SessionContext,
|
|
42
|
+
parameter_repository: IParameterRepository = Provide["nn.parameter_repository"],
|
|
43
|
+
kernel_registry: KernelRegistry = Provide["compute.kernel_registry"],
|
|
44
|
+
kernel_executor_factory: Callable[[], KernelExecutor] = Provide[
|
|
45
|
+
"compute.kernel_executor.provider"
|
|
46
|
+
],
|
|
47
|
+
) -> None:
|
|
48
|
+
self.__session_or_context = session_or_context
|
|
49
|
+
self.__param_repo = parameter_repository
|
|
50
|
+
self.__kernel_registry = kernel_registry
|
|
51
|
+
self.__kernel_executor_factory = kernel_executor_factory
|
|
52
|
+
|
|
53
|
+
def apply(
|
|
54
|
+
self,
|
|
55
|
+
*fields_to_produce: str,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Apply computational kernels to stored parameters.
|
|
58
|
+
|
|
59
|
+
Executes the specified kernels on filtered parameters in dependency order.
|
|
60
|
+
Results are automatically stored and can be retrieved using export_metrics().
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
*fields_to_produce: Names of fields to compute using registered kernels.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
KernelNotFoundError: If any specified field cannot be produced.
|
|
67
|
+
SessionError: If kernel execution fails.
|
|
68
|
+
"""
|
|
69
|
+
with self.__session_or_context:
|
|
70
|
+
if not fields_to_produce:
|
|
71
|
+
logger.warning("No fields specified for computation")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
for field_name in fields_to_produce:
|
|
75
|
+
if not self.__kernel_registry.can_produce_field(field_name):
|
|
76
|
+
available = self.__kernel_registry.list_fields_can_produce()
|
|
77
|
+
hint = _did_you_mean(field_name, available)
|
|
78
|
+
msg = (
|
|
79
|
+
f"Cannot produce '{field_name}': no registered kernel "
|
|
80
|
+
f"produces it.{hint} Use "
|
|
81
|
+
"session.compute.list_available_metrics() to see all fields."
|
|
82
|
+
)
|
|
83
|
+
raise KernelNotFoundError(msg)
|
|
84
|
+
|
|
85
|
+
if isinstance(self.__session_or_context, Session):
|
|
86
|
+
pending = self.__param_repo.create_view()
|
|
87
|
+
else:
|
|
88
|
+
pending = self.__session_or_context._param_filter_context
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
with self.__kernel_executor_factory() as executor:
|
|
92
|
+
for field_name in fields_to_produce:
|
|
93
|
+
executor.execute(
|
|
94
|
+
field_or_kernel_name=field_name, parameters=pending
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
logger.info(
|
|
98
|
+
"Successfully produced fields: %s", ", ".join(fields_to_produce)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
self.__session_or_context._field_cache.invalidate()
|
|
102
|
+
|
|
103
|
+
except SessionError:
|
|
104
|
+
raise
|
|
105
|
+
except Exception as e:
|
|
106
|
+
msg = f"Failed to produce fields: {format_exception_message(e)}"
|
|
107
|
+
raise SessionError(msg) from e
|
|
108
|
+
|
|
109
|
+
def list_available_kernels(self, verbose: bool = False) -> list[str]:
|
|
110
|
+
"""List all available computational kernels.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
verbose: If True, include detailed kernel information including
|
|
114
|
+
dependencies and field requirements.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
List of kernel names or detailed kernel descriptions.
|
|
118
|
+
"""
|
|
119
|
+
with self.__session_or_context:
|
|
120
|
+
return self.__kernel_registry.list_kernels(verbose=verbose)
|
|
121
|
+
|
|
122
|
+
def list_available_metrics(self, verbose: bool = False) -> list[str]:
|
|
123
|
+
"""List all fields that can be computed by registered kernels.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
verbose: If True, include detailed field information including
|
|
127
|
+
producing kernels and dependencies.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
List of computable field names or detailed field descriptions.
|
|
131
|
+
"""
|
|
132
|
+
with self.__session_or_context:
|
|
133
|
+
return self.__kernel_registry.list_fields_can_produce(verbose=verbose)
|
|
134
|
+
|
|
135
|
+
def configure_kernel(self, kernel_name: str, **config: Any) -> None:
|
|
136
|
+
"""Configure parameters for a specific kernel.
|
|
137
|
+
|
|
138
|
+
Updates the configuration for a registered kernel, affecting
|
|
139
|
+
subsequent computations using that kernel.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
kernel_name: Name of kernel to configure.
|
|
143
|
+
**config: Kernel configuration parameters as keyword arguments.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
KernelNotFoundError: If kernel is not registered in the registry.
|
|
147
|
+
"""
|
|
148
|
+
with self.__session_or_context:
|
|
149
|
+
if not self.__kernel_registry.has_kernel(kernel_name):
|
|
150
|
+
hint = _did_you_mean(kernel_name, self.__kernel_registry.list_kernels())
|
|
151
|
+
msg = f"Kernel '{kernel_name}' not found in registry.{hint}"
|
|
152
|
+
raise KernelNotFoundError(msg)
|
|
153
|
+
|
|
154
|
+
kernel_config = KernelConfig(**config)
|
|
155
|
+
self.__kernel_registry.configure_kernel(kernel_name, kernel_config)
|
|
156
|
+
|
|
157
|
+
logger.info("Configured kernel '%s' with: %s", kernel_name, config)
|
|
158
|
+
|
|
159
|
+
def kernel(
|
|
160
|
+
self,
|
|
161
|
+
_func: Callable[..., Any] | None = None,
|
|
162
|
+
*,
|
|
163
|
+
name: str | None = None,
|
|
164
|
+
require_fields: tuple[str, ...] | None = None,
|
|
165
|
+
produce_fields: tuple[str, ...] | None = None,
|
|
166
|
+
apply_level: KernelApplyLevel = KernelApplyLevel.PARAMETER,
|
|
167
|
+
execution_protocol: KernelExecutionProtocol = (
|
|
168
|
+
KernelExecutionProtocol.SEQUENTIAL
|
|
169
|
+
),
|
|
170
|
+
restrictions: KernelRestrictions | None = None,
|
|
171
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
172
|
+
"""Decorator for registering custom kernels in the session registry.
|
|
173
|
+
|
|
174
|
+
Creates a kernel that can compute metrics on neural network parameters.
|
|
175
|
+
Dependencies are automatically inferred from function arguments.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
name: Custom kernel name (defaults to function name).
|
|
179
|
+
require_fields: Input field names (inferred from signature if None).
|
|
180
|
+
produce_fields: Output field names (defaults to kernel name).
|
|
181
|
+
apply_level: Granularity at which the kernel is applied.
|
|
182
|
+
execution_protocol: Execution protocol for the kernel.
|
|
183
|
+
restrictions: Optional restrictions applied to kernel execution.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Decorator function for registering the kernel.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
>>> session = Session()
|
|
190
|
+
>>> with session:
|
|
191
|
+
...
|
|
192
|
+
... @session.compute.kernel()
|
|
193
|
+
... def my_metric(frob_norm: float, *, scale: float = 1.0) -> float:
|
|
194
|
+
... return frob_norm * scale
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
198
|
+
req_auto, cfg = self.__kernel_registry._split_signature(func)
|
|
199
|
+
final_name = name or func.__name__
|
|
200
|
+
final_require = require_fields or req_auto
|
|
201
|
+
final_produce = produce_fields or (final_name,)
|
|
202
|
+
|
|
203
|
+
self.__kernel_registry.register_kernel(
|
|
204
|
+
name=final_name,
|
|
205
|
+
require_fields=final_require,
|
|
206
|
+
produce_fields=final_produce,
|
|
207
|
+
implementation=func,
|
|
208
|
+
apply_level=apply_level,
|
|
209
|
+
execution_protocol=execution_protocol,
|
|
210
|
+
restrictions=restrictions,
|
|
211
|
+
config=cfg,
|
|
212
|
+
info=None,
|
|
213
|
+
)
|
|
214
|
+
return func
|
|
215
|
+
|
|
216
|
+
if _func is None:
|
|
217
|
+
return decorator
|
|
218
|
+
|
|
219
|
+
return decorator(_func)
|