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.
Files changed (193) hide show
  1. diffract/__init__.py +52 -0
  2. diffract/configs/fast_speed_without_disk.ini +48 -0
  3. diffract/configs/hybrid.ini +74 -0
  4. diffract/configs/sqlite.ini +62 -0
  5. diffract/containers.py +405 -0
  6. diffract/core/__init__.py +23 -0
  7. diffract/core/cache/__init__.py +24 -0
  8. diffract/core/cache/containers.py +93 -0
  9. diffract/core/cache/interface.py +117 -0
  10. diffract/core/cache/redis_manager.py +464 -0
  11. diffract/core/cache/simple_manager.py +478 -0
  12. diffract/core/compute/__init__.py +46 -0
  13. diffract/core/compute/config.py +48 -0
  14. diffract/core/compute/containers.py +81 -0
  15. diffract/core/compute/decorator.py +125 -0
  16. diffract/core/compute/exceptions.py +31 -0
  17. diffract/core/compute/execution/__init__.py +14 -0
  18. diffract/core/compute/execution/_types.py +70 -0
  19. diffract/core/compute/execution/aggregation.py +74 -0
  20. diffract/core/compute/execution/aggregation_runner.py +362 -0
  21. diffract/core/compute/execution/enums.py +38 -0
  22. diffract/core/compute/execution/executor.py +125 -0
  23. diffract/core/compute/execution/parameter_runner.py +125 -0
  24. diffract/core/compute/execution/restrictions.py +61 -0
  25. diffract/core/compute/execution/strategy.py +118 -0
  26. diffract/core/compute/execution/utils.py +112 -0
  27. diffract/core/compute/extensions/__init__.py +0 -0
  28. diffract/core/compute/extensions/power_law.py +1051 -0
  29. diffract/core/compute/extensions/rmt.py +150 -0
  30. diffract/core/compute/extensions/utils.py +36 -0
  31. diffract/core/compute/field_signature.py +183 -0
  32. diffract/core/compute/kernels/__init__.py +48 -0
  33. diffract/core/compute/kernels/alignment.py +106 -0
  34. diffract/core/compute/kernels/heavy_tailed.py +394 -0
  35. diffract/core/compute/kernels/marchenko_pastur.py +99 -0
  36. diffract/core/compute/kernels/mat_decomposition.py +101 -0
  37. diffract/core/compute/kernels/mat_properties.py +53 -0
  38. diffract/core/compute/kernels/model_quality.py +27 -0
  39. diffract/core/compute/kernels/norms.py +88 -0
  40. diffract/core/compute/kernels/ranks.py +35 -0
  41. diffract/core/compute/kernels/tracy_widom.py +36 -0
  42. diffract/core/compute/metadata.py +67 -0
  43. diffract/core/compute/registry.py +485 -0
  44. diffract/core/constants.py +147 -0
  45. diffract/core/data/__init__.py +32 -0
  46. diffract/core/data/interface.py +304 -0
  47. diffract/core/data/metadata/__init__.py +15 -0
  48. diffract/core/data/metadata/containers.py +60 -0
  49. diffract/core/data/metadata/interface.py +208 -0
  50. diffract/core/data/metadata/sqlite_index.py +604 -0
  51. diffract/core/data/nn/__init__.py +43 -0
  52. diffract/core/data/nn/aggregates/__init__.py +35 -0
  53. diffract/core/data/nn/aggregates/metadata.py +96 -0
  54. diffract/core/data/nn/aggregates/proxy.py +57 -0
  55. diffract/core/data/nn/aggregates/repository.py +87 -0
  56. diffract/core/data/nn/aggregates/view.py +307 -0
  57. diffract/core/data/nn/containers.py +86 -0
  58. diffract/core/data/nn/extractors/__init__.py +49 -0
  59. diffract/core/data/nn/extractors/base.py +300 -0
  60. diffract/core/data/nn/extractors/factory.py +234 -0
  61. diffract/core/data/nn/extractors/flax.py +112 -0
  62. diffract/core/data/nn/extractors/handlers/__init__.py +39 -0
  63. diffract/core/data/nn/extractors/handlers/base.py +110 -0
  64. diffract/core/data/nn/extractors/handlers/flax_handlers.py +71 -0
  65. diffract/core/data/nn/extractors/handlers/numpy_handlers.py +63 -0
  66. diffract/core/data/nn/extractors/handlers/onnx_handlers.py +67 -0
  67. diffract/core/data/nn/extractors/handlers/tensorflow_handlers.py +82 -0
  68. diffract/core/data/nn/extractors/handlers/torch_handlers.py +124 -0
  69. diffract/core/data/nn/extractors/interface.py +56 -0
  70. diffract/core/data/nn/extractors/numpy.py +94 -0
  71. diffract/core/data/nn/extractors/onnx.py +108 -0
  72. diffract/core/data/nn/extractors/tensorflow.py +99 -0
  73. diffract/core/data/nn/extractors/torch.py +204 -0
  74. diffract/core/data/nn/params/__init__.py +27 -0
  75. diffract/core/data/nn/params/interface.py +402 -0
  76. diffract/core/data/nn/params/metadata.py +110 -0
  77. diffract/core/data/nn/params/proxy.py +93 -0
  78. diffract/core/data/nn/params/repository.py +45 -0
  79. diffract/core/data/nn/params/schema.py +82 -0
  80. diffract/core/data/nn/params/view.py +393 -0
  81. diffract/core/data/proxy.py +246 -0
  82. diffract/core/data/repository.py +227 -0
  83. diffract/core/data/utils.py +33 -0
  84. diffract/core/data/view.py +389 -0
  85. diffract/core/export/__init__.py +27 -0
  86. diffract/core/export/containers.py +47 -0
  87. diffract/core/export/exporters.py +191 -0
  88. diffract/core/export/formatters/__init__.py +23 -0
  89. diffract/core/export/formatters/base.py +68 -0
  90. diffract/core/export/formatters/dict_formatter.py +38 -0
  91. diffract/core/export/formatters/json_formatter.py +58 -0
  92. diffract/core/export/formatters/list_formatter.py +41 -0
  93. diffract/core/export/formatters/pandas_formatter.py +86 -0
  94. diffract/core/export/formatters/polars_formatter.py +86 -0
  95. diffract/core/export/formatters/registry.py +84 -0
  96. diffract/core/export/interface.py +105 -0
  97. diffract/core/parallel/__init__.py +21 -0
  98. diffract/core/parallel/containers.py +57 -0
  99. diffract/core/parallel/execution.py +91 -0
  100. diffract/core/parallel/runtime.py +91 -0
  101. diffract/core/storage/__init__.py +35 -0
  102. diffract/core/storage/base_manager.py +330 -0
  103. diffract/core/storage/containers.py +122 -0
  104. diffract/core/storage/hdf5_manager.py +856 -0
  105. diffract/core/storage/hybrid_manager.py +218 -0
  106. diffract/core/storage/interface.py +222 -0
  107. diffract/core/storage/metadata.py +89 -0
  108. diffract/core/storage/ram_manager.py +159 -0
  109. diffract/core/storage/sqlite_manager.py +1062 -0
  110. diffract/core/storage/zarr_manager.py +992 -0
  111. diffract/core/utils/__init__.py +45 -0
  112. diffract/core/utils/build.py +46 -0
  113. diffract/core/utils/exceptions.py +11 -0
  114. diffract/core/utils/hashing.py +90 -0
  115. diffract/core/utils/imports.py +292 -0
  116. diffract/core/utils/math.py +15 -0
  117. diffract/py.typed +0 -0
  118. diffract/session/__init__.py +24 -0
  119. diffract/session/errors.py +17 -0
  120. diffract/session/field_cache.py +216 -0
  121. diffract/session/namespaces/__init__.py +15 -0
  122. diffract/session/namespaces/compute/__init__.py +219 -0
  123. diffract/session/namespaces/models/__init__.py +282 -0
  124. diffract/session/namespaces/models/parameters/__init__.py +133 -0
  125. diffract/session/namespaces/models/parameters/meta_patcher.py +167 -0
  126. diffract/session/namespaces/results/__init__.py +378 -0
  127. diffract/session/namespaces/results/eraser.py +129 -0
  128. diffract/session/namespaces/results/injester.py +249 -0
  129. diffract/session/namespaces/utils/__init__.py +141 -0
  130. diffract/session/namespaces/utils/merger.py +295 -0
  131. diffract/session/namespaces/viz/__init__.py +91 -0
  132. diffract/session/namespaces/viz/_utils.py +14 -0
  133. diffract/session/namespaces/viz/box.py +207 -0
  134. diffract/session/namespaces/viz/grid.py +160 -0
  135. diffract/session/namespaces/viz/heatmap.py +164 -0
  136. diffract/session/namespaces/viz/scatter.py +202 -0
  137. diffract/session/namespaces/viz/sparkline.py +270 -0
  138. diffract/session/namespaces/viz/violin.py +212 -0
  139. diffract/session/session.py +260 -0
  140. diffract/session/utils.py +81 -0
  141. diffract/viz/__init__.py +42 -0
  142. diffract/viz/data/__init__.py +34 -0
  143. diffract/viz/data/detection.py +57 -0
  144. diffract/viz/data/extraction.py +117 -0
  145. diffract/viz/data/filtering.py +57 -0
  146. diffract/viz/data/ordering.py +129 -0
  147. diffract/viz/data/provider.py +99 -0
  148. diffract/viz/data/types.py +98 -0
  149. diffract/viz/plots/__init__.py +37 -0
  150. diffract/viz/plots/base/__init__.py +20 -0
  151. diffract/viz/plots/base/axis.py +219 -0
  152. diffract/viz/plots/base/coloraxis.py +133 -0
  153. diffract/viz/plots/base/configurator.py +18 -0
  154. diffract/viz/plots/base/jitter.py +368 -0
  155. diffract/viz/plots/base/line.py +197 -0
  156. diffract/viz/plots/base/marker.py +278 -0
  157. diffract/viz/plots/base/overlay.py +14 -0
  158. diffract/viz/plots/base/plot.py +110 -0
  159. diffract/viz/plots/base/update.py +50 -0
  160. diffract/viz/plots/boxplot.py +283 -0
  161. diffract/viz/plots/cluster.py +374 -0
  162. diffract/viz/plots/heatmap.py +168 -0
  163. diffract/viz/plots/scatter.py +233 -0
  164. diffract/viz/plots/sparkline.py +405 -0
  165. diffract/viz/plots/subplots/__init__.py +32 -0
  166. diffract/viz/plots/subplots/coloraxis.py +339 -0
  167. diffract/viz/plots/subplots/factory.py +713 -0
  168. diffract/viz/plots/subplots/grid.py +214 -0
  169. diffract/viz/plots/subplots/layout.py +370 -0
  170. diffract/viz/plots/subplots/spec.py +39 -0
  171. diffract/viz/plots/violin.py +298 -0
  172. diffract/viz/renderer.py +513 -0
  173. diffract/viz/styling/__init__.py +78 -0
  174. diffract/viz/styling/palettes/__init__.py +20 -0
  175. diffract/viz/styling/palettes/bundle.py +16 -0
  176. diffract/viz/styling/palettes/color.py +83 -0
  177. diffract/viz/styling/palettes/dashes.py +27 -0
  178. diffract/viz/styling/palettes/symbols.py +42 -0
  179. diffract/viz/styling/resolvers/__init__.py +11 -0
  180. diffract/viz/styling/resolvers/categorical.py +68 -0
  181. diffract/viz/styling/resolvers/color.py +77 -0
  182. diffract/viz/styling/resolvers/numeric.py +108 -0
  183. diffract/viz/styling/sources.py +27 -0
  184. diffract/viz/styling/theme/__init__.py +29 -0
  185. diffract/viz/styling/theme/applier.py +114 -0
  186. diffract/viz/styling/theme/components.py +66 -0
  187. diffract/viz/styling/theme/presets.py +58 -0
  188. diffract/viz/styling/theme/theme.py +36 -0
  189. diffract_core-0.2.1.dist-info/METADATA +530 -0
  190. diffract_core-0.2.1.dist-info/RECORD +193 -0
  191. diffract_core-0.2.1.dist-info/WHEEL +4 -0
  192. diffract_core-0.2.1.dist-info/licenses/LICENSE +201 -0
  193. 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