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,38 @@
|
|
|
1
|
+
"""Kernel enumeration types and execution constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import Enum, Flag, auto
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KernelApplyLevel(Enum):
|
|
9
|
+
"""Defines the level at which kernel computation is applied.
|
|
10
|
+
|
|
11
|
+
PARAMETER: Apply kernel to individual parameters.
|
|
12
|
+
IN_MODEL: Apply kernel within a single model scope.
|
|
13
|
+
CROSS_MODEL: Apply kernel across multiple models.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
PARAMETER = auto()
|
|
17
|
+
IN_MODEL = auto()
|
|
18
|
+
CROSS_MODEL = auto()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class KernelExecutionProtocol(Enum):
|
|
22
|
+
"""Defines the execution protocol for kernel processing.
|
|
23
|
+
|
|
24
|
+
SEQUENTIAL: Execute kernel tasks sequentially.
|
|
25
|
+
PARALLEL: Execute kernel tasks in parallel.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
SEQUENTIAL = auto()
|
|
29
|
+
PARALLEL = auto()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class KernelRestrictions(Flag):
|
|
33
|
+
"""Flag-based restrictions that can be applied to kernel arguments.
|
|
34
|
+
|
|
35
|
+
BINARY: Restrict kernel to binary operation (exactly two arguments).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
BINARY = auto()
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Kernel execution engine and orchestration utilities.
|
|
2
|
+
|
|
3
|
+
Provides the main KernelExecutor class that orchestrates kernel execution
|
|
4
|
+
with dependency resolution, batching, and parallel processing support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from types import TracebackType
|
|
11
|
+
from typing import TYPE_CHECKING, Self
|
|
12
|
+
|
|
13
|
+
from .aggregation_runner import AggregationKernelRunner
|
|
14
|
+
from .enums import KernelApplyLevel
|
|
15
|
+
from .parameter_runner import ParameterKernelRunner
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
19
|
+
|
|
20
|
+
from diffract.core.compute.registry import KernelRegistry
|
|
21
|
+
from diffract.core.data.nn.aggregates import AggregateRepository
|
|
22
|
+
from diffract.core.data.nn.params.interface import IParameterView
|
|
23
|
+
from diffract.core.parallel import ParallelContext
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class KernelExecutor:
|
|
29
|
+
"""Kernel execution orchestrator with dependency resolution and batching.
|
|
30
|
+
|
|
31
|
+
Main execution engine that coordinates kernel execution across parameter
|
|
32
|
+
collections with support for dependency resolution, batching, and parallel
|
|
33
|
+
processing. Handles both individual parameter processing and aggregated
|
|
34
|
+
computations across models.
|
|
35
|
+
|
|
36
|
+
The executor delegates actual kernel execution to specialized runners:
|
|
37
|
+
- ParameterKernelRunner: handles PARAMETER-level kernels
|
|
38
|
+
- AggregationKernelRunner: handles IN_MODEL and CROSS_MODEL kernels
|
|
39
|
+
|
|
40
|
+
Memory constraints are handled via budget-based chunking in the runners.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
registry: KernelRegistry,
|
|
46
|
+
process_pool: ProcessPoolExecutor | None = None,
|
|
47
|
+
parallel: ParallelContext | None = None,
|
|
48
|
+
aggregate_repository: AggregateRepository | None = None,
|
|
49
|
+
**_kwargs: object,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Initialize kernel executor.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
registry: Kernel registry containing registered kernels.
|
|
55
|
+
process_pool: Optional process pool for parallel kernel execution.
|
|
56
|
+
parallel: Optional parallel context for parameter view operations.
|
|
57
|
+
aggregate_repository: Optional repository for storing aggregation results.
|
|
58
|
+
**_kwargs: Additional config arguments (ignored, for DI compatibility).
|
|
59
|
+
"""
|
|
60
|
+
self._registry = registry
|
|
61
|
+
self._executed: set[str] = set()
|
|
62
|
+
|
|
63
|
+
self._parameter_runner = ParameterKernelRunner(
|
|
64
|
+
registry=registry,
|
|
65
|
+
process_pool=process_pool,
|
|
66
|
+
parallel=parallel,
|
|
67
|
+
)
|
|
68
|
+
self._aggregation_runner = AggregationKernelRunner(
|
|
69
|
+
registry=registry,
|
|
70
|
+
process_pool=process_pool,
|
|
71
|
+
parallel=parallel,
|
|
72
|
+
aggregate_repository=aggregate_repository,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def __enter__(self) -> Self:
|
|
76
|
+
"""Enter context manager."""
|
|
77
|
+
return self
|
|
78
|
+
|
|
79
|
+
def __exit__(
|
|
80
|
+
self,
|
|
81
|
+
exc_type: type[BaseException] | None,
|
|
82
|
+
exc_val: BaseException | None,
|
|
83
|
+
exc_tb: TracebackType | None,
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Exit context manager, clearing execution state."""
|
|
86
|
+
self.clear_execution_state()
|
|
87
|
+
|
|
88
|
+
def clear_execution_state(self) -> None:
|
|
89
|
+
"""Reset executed kernels set, allowing re-execution."""
|
|
90
|
+
self._executed.clear()
|
|
91
|
+
|
|
92
|
+
def execute(self, field_or_kernel_name: str, parameters: IParameterView) -> None:
|
|
93
|
+
"""Execute a kernel or produce a field with dependency resolution.
|
|
94
|
+
|
|
95
|
+
Main entry point for kernel execution. Resolves all dependencies
|
|
96
|
+
and executes required kernels in proper order.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
field_or_kernel_name: Kernel name or field name to execute/produce.
|
|
100
|
+
parameters: Parameter collection to process.
|
|
101
|
+
"""
|
|
102
|
+
if field_or_kernel_name in self._executed:
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
dependencies = self._registry.resolve_dependencies(field_or_kernel_name)
|
|
106
|
+
|
|
107
|
+
for dep in dependencies:
|
|
108
|
+
if dep != field_or_kernel_name and dep not in self._executed:
|
|
109
|
+
self.execute(dep, parameters)
|
|
110
|
+
|
|
111
|
+
if self._registry.has_kernel(field_or_kernel_name):
|
|
112
|
+
self._dispatch_kernel(field_or_kernel_name, parameters)
|
|
113
|
+
self._executed.add(field_or_kernel_name)
|
|
114
|
+
|
|
115
|
+
def _dispatch_kernel(self, kernel_name: str, parameters: IParameterView) -> None:
|
|
116
|
+
"""Dispatch kernel execution to the appropriate runner."""
|
|
117
|
+
apply_level = self._registry.get_kernel_apply_level(kernel_name)
|
|
118
|
+
|
|
119
|
+
if apply_level == KernelApplyLevel.PARAMETER:
|
|
120
|
+
self._parameter_runner.run(kernel_name, parameters)
|
|
121
|
+
elif apply_level in (KernelApplyLevel.IN_MODEL, KernelApplyLevel.CROSS_MODEL):
|
|
122
|
+
self._aggregation_runner.run(kernel_name, parameters)
|
|
123
|
+
else:
|
|
124
|
+
msg = f"Unknown kernel apply level: {apply_level}"
|
|
125
|
+
raise RuntimeError(msg)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Parameter-level kernel execution logic."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from .restrictions import apply_restrictions_filter
|
|
9
|
+
from .strategy import create_execution_strategy
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from collections.abc import Iterator
|
|
13
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
14
|
+
|
|
15
|
+
from diffract.core.compute.registry import KernelRegistry
|
|
16
|
+
from diffract.core.data.nn.params.interface import IParameterView
|
|
17
|
+
from diffract.core.parallel import ParallelContext
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ParameterKernelRunner:
|
|
23
|
+
"""Executes parameter-level kernels with batching and streaming writes.
|
|
24
|
+
|
|
25
|
+
Handles execution of kernels that operate on individual parameters,
|
|
26
|
+
including memory-aware chunking, prefetching, and result streaming.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
registry: KernelRegistry,
|
|
32
|
+
process_pool: ProcessPoolExecutor | None,
|
|
33
|
+
parallel: ParallelContext | None,
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Initialize the parameter kernel runner.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
registry: Kernel registry for metadata lookup.
|
|
39
|
+
process_pool: Optional process pool for parallel execution.
|
|
40
|
+
parallel: Optional parallel context for view operations.
|
|
41
|
+
"""
|
|
42
|
+
self._registry = registry
|
|
43
|
+
self._process_pool = process_pool
|
|
44
|
+
self._parallel = parallel
|
|
45
|
+
|
|
46
|
+
def run(self, kernel_name: str, parameters: IParameterView) -> None:
|
|
47
|
+
"""Execute kernel on individual parameters.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
kernel_name: Name of the registered kernel.
|
|
51
|
+
parameters: Parameter collection to process.
|
|
52
|
+
"""
|
|
53
|
+
target_fields = self._registry.get_fields_kernel_produce(kernel_name)
|
|
54
|
+
pending = parameters.filter_by_fields(
|
|
55
|
+
*target_fields,
|
|
56
|
+
inverse_mask=True,
|
|
57
|
+
parallel=self._parallel,
|
|
58
|
+
)
|
|
59
|
+
if not pending:
|
|
60
|
+
logger.debug(
|
|
61
|
+
"Skip execution of kernel '%s': no pending parameters", kernel_name
|
|
62
|
+
)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
logger.info("Executing kernel '%s'", kernel_name)
|
|
66
|
+
|
|
67
|
+
required_fields = list(self._registry.get_fields_kernel_require(kernel_name))
|
|
68
|
+
required_by_uid = {p.meta.uid: required_fields for p in pending}
|
|
69
|
+
|
|
70
|
+
chunks = list(
|
|
71
|
+
pending.iter_chunks_by_read_budget(
|
|
72
|
+
required_fields_by_uid=required_by_uid,
|
|
73
|
+
parallel=self._parallel,
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
if not chunks:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
for chunk in chunks:
|
|
80
|
+
chunk.prefetch_fields(fields=required_fields, parallel=self._parallel)
|
|
81
|
+
self._execute_batch(kernel_name, chunk)
|
|
82
|
+
|
|
83
|
+
def _execute_batch(self, kernel_name: str, batch: IParameterView) -> None:
|
|
84
|
+
"""Execute parameter-level kernel on a batch with streaming writes.
|
|
85
|
+
|
|
86
|
+
Results are written immediately as they become available, reducing
|
|
87
|
+
memory pressure for heavy computations (e.g., SVD matrices).
|
|
88
|
+
"""
|
|
89
|
+
required_args = self._registry.get_fields_kernel_require(kernel_name)
|
|
90
|
+
tasks: dict[tuple[str, str], tuple[Any, ...]] = {}
|
|
91
|
+
|
|
92
|
+
for param in batch:
|
|
93
|
+
args = tuple(param.get_field(arg) for arg in required_args)
|
|
94
|
+
tasks[(param.meta.model_id, param.meta.uid)] = args
|
|
95
|
+
|
|
96
|
+
apply_restrictions_filter(
|
|
97
|
+
kernel_name,
|
|
98
|
+
tasks,
|
|
99
|
+
self._registry.get_kernel_restrictions(kernel_name),
|
|
100
|
+
)
|
|
101
|
+
if not tasks:
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
param_lookup = {(p.meta.model_id, p.meta.uid): p for p in batch}
|
|
105
|
+
|
|
106
|
+
with batch:
|
|
107
|
+
for key, result in self._stream_results(kernel_name, tasks):
|
|
108
|
+
normalized = self._registry.normalize_kernel_result(kernel_name, result)
|
|
109
|
+
param = param_lookup[key]
|
|
110
|
+
for field_name, value in normalized.items():
|
|
111
|
+
param.set_field(field_name, value)
|
|
112
|
+
|
|
113
|
+
def _stream_results(
|
|
114
|
+
self,
|
|
115
|
+
kernel_name: str,
|
|
116
|
+
tasks: dict[Any, tuple[Any, ...]],
|
|
117
|
+
) -> Iterator[tuple[Any, Any]]:
|
|
118
|
+
"""Execute kernel tasks and yield results as they become available."""
|
|
119
|
+
strategy = create_execution_strategy(
|
|
120
|
+
kernel_name,
|
|
121
|
+
self._registry,
|
|
122
|
+
self._process_pool,
|
|
123
|
+
)
|
|
124
|
+
implementation = self._registry.get_kernel_implementation(kernel_name)
|
|
125
|
+
yield from strategy.execute_tasks(kernel_name, tasks, implementation)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Kernel restriction checking utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from collections.abc import Hashable
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .enums import KernelRestrictions
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
_BINARY_ARG_COUNT = 2
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def apply_restrictions_filter(
|
|
17
|
+
kernel_name: str,
|
|
18
|
+
tasks: dict[Hashable, tuple[Any, ...]],
|
|
19
|
+
restrictions: KernelRestrictions | None,
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Remove tasks that don't meet kernel restrictions (mutates tasks dict).
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
kernel_name: Name of the kernel for logging.
|
|
25
|
+
tasks: Dictionary of task_id -> args to filter in place.
|
|
26
|
+
restrictions: Optional kernel restrictions to check.
|
|
27
|
+
"""
|
|
28
|
+
if not restrictions:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
to_remove: list[Hashable] = []
|
|
32
|
+
for task_id, args in tasks.items():
|
|
33
|
+
if not _check_restrictions(restrictions, args):
|
|
34
|
+
logger.debug(
|
|
35
|
+
"Skip computation for kernel '%s', task '%s': restriction violated",
|
|
36
|
+
kernel_name,
|
|
37
|
+
task_id,
|
|
38
|
+
)
|
|
39
|
+
to_remove.append(task_id)
|
|
40
|
+
|
|
41
|
+
for task_id in to_remove:
|
|
42
|
+
del tasks[task_id]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _check_restrictions(
|
|
46
|
+
restrictions: KernelRestrictions, args: tuple[Any, ...]
|
|
47
|
+
) -> bool:
|
|
48
|
+
"""Check if arguments satisfy kernel restrictions.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
restrictions: Kernel restrictions flags.
|
|
52
|
+
args: Tuple of arguments to check.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
True if all restrictions are satisfied, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
if restrictions & KernelRestrictions.BINARY:
|
|
58
|
+
for arg in args:
|
|
59
|
+
if hasattr(arg, "__len__") and len(arg) != _BINARY_ARG_COUNT:
|
|
60
|
+
return False
|
|
61
|
+
return True
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Execution strategy implementations for kernel processing.
|
|
2
|
+
|
|
3
|
+
Provides strategy pattern implementations for sequential and parallel
|
|
4
|
+
kernel execution, abstracting the execution mechanism from kernel runners.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from collections.abc import Callable, Iterator
|
|
11
|
+
from concurrent.futures import ProcessPoolExecutor, as_completed
|
|
12
|
+
from typing import TYPE_CHECKING, Any
|
|
13
|
+
|
|
14
|
+
from .enums import KernelExecutionProtocol
|
|
15
|
+
from .utils import execute_kernel, marshal_kernel
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from diffract.core.compute.registry import KernelRegistry
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ExecutionStrategy(ABC):
|
|
22
|
+
"""Abstract execution strategy interface."""
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def execute_tasks(
|
|
26
|
+
self,
|
|
27
|
+
kernel_name: str,
|
|
28
|
+
tasks: dict[Any, tuple[Any, ...]],
|
|
29
|
+
implementation: Callable[..., Any],
|
|
30
|
+
) -> Iterator[tuple[Any, Any]]:
|
|
31
|
+
"""Execute tasks and yield (task_id, result) pairs as they complete.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
kernel_name: Name of the kernel for error reporting.
|
|
35
|
+
tasks: Dictionary mapping task_id to argument tuples.
|
|
36
|
+
implementation: Kernel function to execute.
|
|
37
|
+
|
|
38
|
+
Yields:
|
|
39
|
+
Tuples of (task_id, result) as execution completes.
|
|
40
|
+
"""
|
|
41
|
+
raise NotImplementedError
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SequentialStrategy(ExecutionStrategy):
|
|
45
|
+
"""Sequential execution strategy.
|
|
46
|
+
|
|
47
|
+
Executes tasks one by one in the current process, yielding
|
|
48
|
+
results immediately after each execution completes.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def execute_tasks(
|
|
52
|
+
self,
|
|
53
|
+
kernel_name: str,
|
|
54
|
+
tasks: dict[Any, tuple[Any, ...]],
|
|
55
|
+
implementation: Callable[..., Any],
|
|
56
|
+
) -> Iterator[tuple[Any, Any]]:
|
|
57
|
+
"""Execute tasks sequentially, yielding results one by one."""
|
|
58
|
+
for task_id, args in tasks.items():
|
|
59
|
+
result = execute_kernel(kernel_name, implementation, args)
|
|
60
|
+
yield task_id, result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ParallelStrategy(ExecutionStrategy):
|
|
64
|
+
"""Parallel execution strategy using ProcessPoolExecutor.
|
|
65
|
+
|
|
66
|
+
Distributes tasks across worker processes and yields results
|
|
67
|
+
as they complete (not necessarily in submission order).
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, pool: ProcessPoolExecutor) -> None:
|
|
71
|
+
"""Initialize with a process pool.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
pool: ProcessPoolExecutor for parallel execution.
|
|
75
|
+
"""
|
|
76
|
+
self._pool = pool
|
|
77
|
+
|
|
78
|
+
def execute_tasks(
|
|
79
|
+
self,
|
|
80
|
+
kernel_name: str,
|
|
81
|
+
tasks: dict[Any, tuple[Any, ...]],
|
|
82
|
+
implementation: Callable[..., Any],
|
|
83
|
+
) -> Iterator[tuple[Any, Any]]:
|
|
84
|
+
"""Execute tasks in parallel, yielding results as they complete."""
|
|
85
|
+
marshaled = marshal_kernel(implementation)
|
|
86
|
+
future_to_task = {
|
|
87
|
+
self._pool.submit(execute_kernel, kernel_name, marshaled, args): task_id
|
|
88
|
+
for task_id, args in tasks.items()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for future in as_completed(future_to_task):
|
|
92
|
+
task_id = future_to_task[future]
|
|
93
|
+
yield task_id, future.result()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def create_execution_strategy(
|
|
97
|
+
kernel_name: str,
|
|
98
|
+
registry: KernelRegistry,
|
|
99
|
+
process_pool: ProcessPoolExecutor | None,
|
|
100
|
+
) -> ExecutionStrategy:
|
|
101
|
+
"""Create appropriate execution strategy based on kernel config and pool.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
kernel_name: Name of the kernel to execute.
|
|
105
|
+
registry: Kernel registry for protocol lookup.
|
|
106
|
+
process_pool: Optional process pool for parallel execution.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Configured execution strategy instance.
|
|
110
|
+
"""
|
|
111
|
+
if process_pool is None:
|
|
112
|
+
return SequentialStrategy()
|
|
113
|
+
|
|
114
|
+
protocol = registry.get_kernel_execution_protocol(kernel_name)
|
|
115
|
+
if protocol == KernelExecutionProtocol.PARALLEL:
|
|
116
|
+
return ParallelStrategy(process_pool)
|
|
117
|
+
|
|
118
|
+
return SequentialStrategy()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Multiprocessing utilities for kernel execution.
|
|
2
|
+
|
|
3
|
+
Provides functions for marshaling, unmarshaling, and executing kernels
|
|
4
|
+
in multiprocessing contexts with proper serialization handling.
|
|
5
|
+
|
|
6
|
+
The marshal/unmarshal functions enable kernel functions to be transmitted
|
|
7
|
+
across process boundaries for parallel execution, while execute_kernel
|
|
8
|
+
provides uniform error handling across all execution modes.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import marshal
|
|
14
|
+
import pickle
|
|
15
|
+
import types
|
|
16
|
+
from typing import TYPE_CHECKING, Any
|
|
17
|
+
|
|
18
|
+
from diffract.core.compute.exceptions import KernelExecutionError
|
|
19
|
+
from diffract.core.utils.exceptions import format_exception_message
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Callable
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def marshal_kernel(kernel_implementation: Callable[..., Any]) -> bytes:
|
|
26
|
+
"""Serialize a Python function to bytes for cross-process transmission.
|
|
27
|
+
|
|
28
|
+
Converts a kernel function into a serialized format suitable for
|
|
29
|
+
transmission across process boundaries in multiprocessing scenarios.
|
|
30
|
+
Captures the function's code, defaults, and closure state.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
kernel_implementation: Function to serialize.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Serialized function as bytes.
|
|
37
|
+
|
|
38
|
+
Note:
|
|
39
|
+
The function must be picklable. Closures with non-serializable
|
|
40
|
+
state will fail to serialize.
|
|
41
|
+
"""
|
|
42
|
+
kernel_code = kernel_implementation.__code__
|
|
43
|
+
kernel_defaults = kernel_implementation.__defaults__
|
|
44
|
+
|
|
45
|
+
kernel_closure_bytes: bytes | None = None
|
|
46
|
+
if kernel_implementation.__closure__:
|
|
47
|
+
kernel_closure = tuple(
|
|
48
|
+
c.cell_contents for c in kernel_implementation.__closure__
|
|
49
|
+
)
|
|
50
|
+
kernel_closure_bytes = pickle.dumps(kernel_closure)
|
|
51
|
+
|
|
52
|
+
return marshal.dumps((kernel_code, kernel_defaults, kernel_closure_bytes))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def unmarshal_kernel(serialized: bytes) -> Callable[..., Any]:
|
|
56
|
+
"""Reconstruct a Python function from serialized bytes.
|
|
57
|
+
|
|
58
|
+
Deserializes a kernel function from bytes back into an executable
|
|
59
|
+
function object with proper code, defaults, and closure restoration.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
serialized: Serialized function bytes from marshal_kernel.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Reconstructed function object.
|
|
66
|
+
"""
|
|
67
|
+
kernel_code, kernel_defaults, kernel_closure = marshal.loads(serialized) # noqa: S302
|
|
68
|
+
kernel_closure = (
|
|
69
|
+
pickle.loads(kernel_closure) # noqa: S301
|
|
70
|
+
if kernel_closure
|
|
71
|
+
else None
|
|
72
|
+
)
|
|
73
|
+
if kernel_closure:
|
|
74
|
+
kernel_closure = tuple(types.CellType(obj) for obj in kernel_closure)
|
|
75
|
+
return types.FunctionType(
|
|
76
|
+
code=kernel_code,
|
|
77
|
+
globals=globals(),
|
|
78
|
+
name=None,
|
|
79
|
+
argdefs=kernel_defaults,
|
|
80
|
+
closure=kernel_closure,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def execute_kernel(
|
|
85
|
+
kernel_name: str,
|
|
86
|
+
kernel_implementation: Callable[..., Any] | bytes,
|
|
87
|
+
kernel_args: tuple[Any, ...],
|
|
88
|
+
) -> Any:
|
|
89
|
+
"""Execute a kernel implementation with arguments, handling errors uniformly.
|
|
90
|
+
|
|
91
|
+
Executes a kernel function, automatically deserializing if needed,
|
|
92
|
+
and wrapping any exceptions in KernelExecutionError for consistent
|
|
93
|
+
error handling across the execution pipeline.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
kernel_name: Name of the kernel for error reporting.
|
|
97
|
+
kernel_implementation: Function or serialized function bytes.
|
|
98
|
+
kernel_args: Arguments to pass to the kernel function.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Result of kernel execution.
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
KernelExecutionError: If kernel execution fails for any reason.
|
|
105
|
+
"""
|
|
106
|
+
try:
|
|
107
|
+
if isinstance(kernel_implementation, bytes):
|
|
108
|
+
kernel_implementation = unmarshal_kernel(kernel_implementation)
|
|
109
|
+
return kernel_implementation(*kernel_args)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
msg = f"Kernel '{kernel_name}' execution failed: {format_exception_message(e)}"
|
|
112
|
+
raise KernelExecutionError(msg) from e
|
|
File without changes
|