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,110 @@
1
+ """Parameter handler interface and lightweight registry.
2
+
3
+ This module defines the abstract ParameterHandler interface and a minimal
4
+ registry for selecting and executing handlers. The design is intentionally
5
+ simple: handlers are tried in registration order without priorities, and
6
+ the registry returns both the processed weights and the handler instance
7
+ that performed the processing.
8
+
9
+ Key points:
10
+ - Single-pass selection in registration order
11
+ - Handlers declare produced ParameterType via parameter_type property
12
+ - Registry returns (ptype, weights, handler) to avoid double lookup
13
+ - Supported types are derived from registered handlers
14
+
15
+ Example:
16
+ >>> registry = ParameterHandlerRegistry()
17
+ >>> registry.register_handler_class(MyDenseHandler)
18
+ >>> out = registry.process_parameter(module, "layer.weight")
19
+ >>> if out:
20
+ ... ptype, weights, handler = out
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from abc import ABC, abstractmethod
26
+ from typing import TYPE_CHECKING, Any
27
+
28
+ if TYPE_CHECKING:
29
+ import numpy as np
30
+ from numpy.typing import NDArray
31
+
32
+ from diffract.core.data.nn.params.schema import ParameterType
33
+
34
+
35
+ class ParameterHandler[T](ABC):
36
+ """Base class for a concrete parameter-type handler (no priority)."""
37
+
38
+ @property
39
+ @abstractmethod
40
+ def parameter_type(self) -> ParameterType:
41
+ """ParameterType produced by this handler."""
42
+ raise NotImplementedError
43
+
44
+ @abstractmethod
45
+ def can_handle(self, parameter: T, param_name: str) -> bool:
46
+ """Return True if handler can process the given parameter."""
47
+ raise NotImplementedError
48
+
49
+ @abstractmethod
50
+ def process(self, parameter: T, param_name: str) -> NDArray[np.floating[Any]]:
51
+ """Return processed weights as a NumPy array."""
52
+ raise NotImplementedError
53
+
54
+ def get_additional_metadata(
55
+ self, _parameter: T, _param_name: str
56
+ ) -> dict[str, Any]:
57
+ """Return optional metadata; never raises and returns empty on failure."""
58
+ return {}
59
+
60
+
61
+ class ParameterHandlerRegistry[T]:
62
+ """Registry of handlers with registration-order selection."""
63
+
64
+ def __init__(self) -> None:
65
+ self._handlers: list[ParameterHandler[T]] = []
66
+
67
+ def register_handler(self, handler: ParameterHandler[T]) -> None:
68
+ """Register a handler instance for later selection."""
69
+ self._handlers.append(handler)
70
+
71
+ def register_handler_class(self, handler_cls: type[ParameterHandler[T]]) -> None:
72
+ """Instantiate and register a handler class."""
73
+ self.register_handler(handler_cls())
74
+
75
+ def list_handlers(self) -> list[ParameterHandler[T]]:
76
+ """Return a shallow copy of registered handlers."""
77
+ return list(self._handlers)
78
+
79
+ def get_supported_types(self) -> list[ParameterType]:
80
+ """Return unique ParameterType values supported by registered handlers."""
81
+ seen: set[ParameterType] = set()
82
+ out: list[ParameterType] = []
83
+ for h in self._handlers:
84
+ if h.parameter_type not in seen:
85
+ seen.add(h.parameter_type)
86
+ out.append(h.parameter_type)
87
+ return out
88
+
89
+ def process_parameter(
90
+ self,
91
+ parameter: T,
92
+ param_name: str,
93
+ ) -> tuple[ParameterType, NDArray[np.floating[Any]], ParameterHandler[T]] | None:
94
+ """Select a matching handler and process in one pass.
95
+
96
+ Tries handlers in registration order, returning the first match's
97
+ produced type, processed weights, and the handler instance.
98
+
99
+ Args:
100
+ parameter: Framework-specific parameter object.
101
+ param_name: Human-readable parameter name.
102
+
103
+ Returns:
104
+ Tuple of (parameter type, weights array, handler) or None if no match.
105
+ """
106
+ for handler in self._handlers:
107
+ if handler.can_handle(parameter, param_name):
108
+ weights = handler.process(parameter, param_name)
109
+ return handler.parameter_type, weights, handler
110
+ return None
@@ -0,0 +1,71 @@
1
+ """Flax/JAX-specific parameter handlers for dense parameters.
2
+
3
+ This module implements handlers for extracting dense layer weights from Flax
4
+ parameter trees (typically `variables["params"]`).
5
+
6
+ Currently supports only DENSE weights by selecting 2D arrays whose leaf name
7
+ is `kernel` (Flax Linen Dense convention).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from typing import TYPE_CHECKING, Any, Self, cast
14
+
15
+ import diffract.core.utils.imports as import_utils
16
+ from diffract.core.data.nn.params.schema import ParameterType
17
+
18
+ from .base import ParameterHandler
19
+
20
+ if TYPE_CHECKING:
21
+ import numpy as np
22
+ from numpy.typing import NDArray
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ if not (import_utils.is_available("flax") and import_utils.is_available("jax")):
28
+ logger.debug("Flax/JAX not available, disabling corresponding handlers")
29
+
30
+ class FlaxDenseHandler(ParameterHandler):
31
+ """Stub handler when Flax/JAX is not available."""
32
+
33
+ def __new__( # noqa: D102
34
+ cls,
35
+ *_args: Any,
36
+ **_kwargs: Any, # type: ignore[name-defined]
37
+ ) -> Self:
38
+ msg = "Flax/JAX packages not available"
39
+ raise ImportError(msg)
40
+
41
+ else:
42
+ jax = import_utils.require("jax")
43
+ import numpy as np
44
+
45
+ class FlaxDenseHandler(ParameterHandler[Any]):
46
+ """Handler for Flax Dense kernel weights in params trees."""
47
+
48
+ _EXPECTED_NDIM = 2
49
+
50
+ @property
51
+ def parameter_type(self) -> ParameterType: # noqa: D102
52
+ return ParameterType.DENSE
53
+
54
+ def can_handle(self, parameter: Any, param_name: str) -> bool: # noqa: D102
55
+ # `parameter` is expected to be a leaf array-like.
56
+ if not param_name.endswith(".kernel"):
57
+ return False
58
+ arr = np.asarray(jax.device_get(parameter))
59
+ return arr.ndim == self._EXPECTED_NDIM and arr.size > 1
60
+
61
+ def process( # noqa: D102
62
+ self, parameter: Any, _param_name: str
63
+ ) -> NDArray[np.floating[Any]]:
64
+ arr = np.asarray(jax.device_get(parameter))
65
+ if arr.ndim != self._EXPECTED_NDIM:
66
+ msg = (
67
+ f"Expected {self._EXPECTED_NDIM}D kernel array, "
68
+ f"got shape {arr.shape}"
69
+ )
70
+ raise ValueError(msg)
71
+ return cast("NDArray[np.floating[Any]]", arr)
@@ -0,0 +1,63 @@
1
+ """NumPy-specific parameter handlers for dense parameters.
2
+
3
+ This module implements handlers for extracting dense (fully-connected) layer
4
+ weights from plain dictionaries of NumPy arrays. Handlers declare their
5
+ produced ParameterType and provide optional metadata.
6
+
7
+ NumPy is a required dependency, so no availability stubs are defined.
8
+
9
+ Example:
10
+ >>> handler = NumpyDenseHandler()
11
+ >>> if handler.can_handle(array, name):
12
+ ... weights = handler.process(array, name)
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ from typing import Any, cast
19
+
20
+ import numpy as np
21
+ from numpy.typing import NDArray
22
+
23
+ from diffract.core.data.nn.params.schema import ParameterType
24
+
25
+ from .base import ParameterHandler
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ _DENSE_WEIGHT_NDIM = 2
30
+
31
+
32
+ class NumpyDenseHandler(ParameterHandler[NDArray[Any]]):
33
+ """Handler for 2D floating-point arrays in NumPy dictionaries."""
34
+
35
+ @property
36
+ def parameter_type(self) -> ParameterType:
37
+ """Return produced type for this handler (DENSE)."""
38
+ return ParameterType.DENSE
39
+
40
+ def can_handle(self, parameter: NDArray[Any], _param_name: str) -> bool:
41
+ """Return True for 2D floating-point arrays with more than one element.
42
+
43
+ Masked arrays are rejected: their mask would be silently discarded,
44
+ computing metrics over raw fill values.
45
+ """
46
+ return (
47
+ parameter.ndim == _DENSE_WEIGHT_NDIM
48
+ and parameter.size > 1
49
+ and np.issubdtype(parameter.dtype, np.floating)
50
+ and not isinstance(parameter, np.ma.MaskedArray)
51
+ )
52
+
53
+ def process(
54
+ self, parameter: NDArray[Any], _param_name: str
55
+ ) -> NDArray[np.floating[Any]]:
56
+ """Return the array as NumPy weights."""
57
+ return cast("NDArray[np.floating[Any]]", parameter)
58
+
59
+ def get_additional_metadata(
60
+ self, parameter: NDArray[Any], _param_name: str
61
+ ) -> dict[str, Any]:
62
+ """Return optional NumPy-specific metadata such as dtype."""
63
+ return {"numpy_dtype": str(parameter.dtype)}
@@ -0,0 +1,67 @@
1
+ """ONNX-specific parameter handlers for dense parameters.
2
+
3
+ This module implements handlers for extracting dense weights from ONNX models.
4
+ The extractor is responsible for selecting candidate weight arrays; this
5
+ handler validates and standardizes them.
6
+
7
+ Currently supports only DENSE weights by accepting 2D NumPy arrays.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from typing import TYPE_CHECKING, Any, Self, cast
14
+
15
+ import diffract.core.utils.imports as import_utils
16
+ from diffract.core.data.nn.params.schema import ParameterType
17
+
18
+ from .base import ParameterHandler
19
+
20
+ if TYPE_CHECKING:
21
+ import numpy as np
22
+ from numpy.typing import NDArray
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ if not import_utils.is_available("onnx"):
28
+ logger.debug("ONNX not available, disabling corresponding handlers")
29
+
30
+ class OnnxDenseHandler(ParameterHandler):
31
+ """Stub handler when ONNX is not available."""
32
+
33
+ def __new__( # noqa: D102
34
+ cls,
35
+ *_args: Any,
36
+ **_kwargs: Any, # type: ignore[name-defined]
37
+ ) -> Self:
38
+ msg = "onnx package not available"
39
+ raise ImportError(msg)
40
+
41
+ else:
42
+ import numpy as np
43
+
44
+ class OnnxDenseHandler(ParameterHandler[Any]):
45
+ """Handler for ONNX dense weights represented as 2D NumPy arrays."""
46
+
47
+ _EXPECTED_NDIM = 2
48
+
49
+ @property
50
+ def parameter_type(self) -> ParameterType: # noqa: D102
51
+ return ParameterType.DENSE
52
+
53
+ def can_handle(self, parameter: Any, _param_name: str) -> bool: # noqa: D102
54
+ arr = np.asarray(parameter)
55
+ return arr.ndim == self._EXPECTED_NDIM and arr.size > 1
56
+
57
+ def process( # noqa: D102
58
+ self, parameter: Any, _param_name: str
59
+ ) -> NDArray[np.floating[Any]]:
60
+ arr = np.asarray(parameter)
61
+ if arr.ndim != self._EXPECTED_NDIM:
62
+ msg = (
63
+ f"Expected {self._EXPECTED_NDIM}D ONNX weight array, "
64
+ f"got shape {arr.shape}"
65
+ )
66
+ raise ValueError(msg)
67
+ return cast("NDArray[np.floating[Any]]", arr)
@@ -0,0 +1,82 @@
1
+ """TensorFlow-specific parameter handlers for dense parameters.
2
+
3
+ This module implements handlers for extracting dense (fully-connected) layer
4
+ weights from TensorFlow/Keras models. Currently supports only DENSE weights
5
+ from `tf.keras.layers.Dense`.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ from typing import TYPE_CHECKING, Any, Self, cast
12
+
13
+ import diffract.core.utils.imports as import_utils
14
+ from diffract.core.data.nn.params.schema import ParameterType
15
+
16
+ from .base import ParameterHandler
17
+
18
+ if TYPE_CHECKING:
19
+ import numpy as np
20
+ from numpy.typing import NDArray
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ if not import_utils.is_available("tensorflow"):
26
+ logger.debug("TensorFlow not available, disabling corresponding handlers")
27
+
28
+ class TensorFlowDenseHandler(ParameterHandler):
29
+ """Stub handler when TensorFlow is not available."""
30
+
31
+ def __new__( # noqa: D102
32
+ cls,
33
+ *_args: Any,
34
+ **_kwargs: Any, # type: ignore[name-defined]
35
+ ) -> Self:
36
+ msg = "TensorFlow package not available"
37
+ raise ImportError(msg)
38
+
39
+ else:
40
+ tf = import_utils.require("tensorflow")
41
+
42
+ class TensorFlowDenseHandler(ParameterHandler[Any]):
43
+ """Handler for TensorFlow/Keras Dense layers (tf.keras.layers.Dense)."""
44
+
45
+ @property
46
+ def parameter_type(self) -> ParameterType: # noqa: D102
47
+ return ParameterType.DENSE
48
+
49
+ def can_handle(self, parameter: Any, _param_name: str) -> bool: # noqa: D102
50
+ return isinstance(parameter, tf.keras.layers.Dense)
51
+
52
+ def process( # noqa: D102
53
+ self, parameter: Any, _param_name: str
54
+ ) -> NDArray[np.floating[Any]]:
55
+ if not isinstance(parameter, tf.keras.layers.Dense):
56
+ msg = f"Expected tf.keras.layers.Dense, got {type(parameter)}"
57
+ raise TypeError(msg)
58
+
59
+ # Keras Dense exposes kernel as a Variable. Ensure it's created/built.
60
+ if not hasattr(parameter, "kernel"):
61
+ msg = "Dense layer has no 'kernel' attribute (model not built?)"
62
+ raise ValueError(msg)
63
+
64
+ kernel = parameter.kernel
65
+ # In eager mode `.numpy()` is available; otherwise Keras models passed
66
+ # for analysis are expected to be materialized.
67
+ arr = kernel.numpy()
68
+ return cast("NDArray[np.floating[Any]]", arr)
69
+
70
+ def get_additional_metadata( # noqa: D102
71
+ self, parameter: Any, _param_name: str
72
+ ) -> dict[str, Any]:
73
+ if not isinstance(parameter, tf.keras.layers.Dense):
74
+ return {}
75
+ meta: dict[str, Any] = {
76
+ "tf_dtype": getattr(parameter.kernel, "dtype", None),
77
+ }
78
+ # Useful for downstream analysis / sanity checks.
79
+ with_size = getattr(parameter, "units", None)
80
+ if with_size is not None:
81
+ meta["units"] = with_size
82
+ return meta
@@ -0,0 +1,124 @@
1
+ """PyTorch-specific parameter handlers for dense parameters.
2
+
3
+ This module implements handlers for extracting dense (fully-connected) layer
4
+ weights from PyTorch models and state dictionaries. Handlers declare their
5
+ produced ParameterType and provide optional metadata.
6
+
7
+ Behavior when PyTorch is unavailable:
8
+ - Stub handler classes are defined that raise ImportError on instantiation
9
+ to signal the missing dependency.
10
+
11
+ Example:
12
+ >>> handler = TorchDenseHandler()
13
+ >>> if handler.can_handle(module, name):
14
+ ... weights = handler.process(module, name)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ from typing import TYPE_CHECKING, Any, Self, cast
21
+
22
+ import diffract.core.utils.imports as import_utils
23
+ from diffract.core.data.nn.params.schema import ParameterType
24
+
25
+ from .base import ParameterHandler
26
+
27
+ if TYPE_CHECKING:
28
+ import numpy as np
29
+ from numpy.typing import NDArray
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+ _DENSE_WEIGHT_NDIM = 2
34
+
35
+
36
+ if not import_utils.is_available("torch"):
37
+ logger.debug("PyTorch not available, disabling corresponding handlers")
38
+
39
+ class TorchDenseHandler(ParameterHandler):
40
+ """Stub handler when PyTorch is not available."""
41
+
42
+ def __new__(
43
+ cls,
44
+ *_args: Any,
45
+ **_kwargs: Any, # type: ignore[name-defined]
46
+ ) -> Self:
47
+ """Raise ImportError since PyTorch is unavailable."""
48
+ msg = "PyTorch package not available"
49
+ raise ImportError(msg)
50
+
51
+ class TorchStateDictDenseHandler(ParameterHandler):
52
+ """Stub handler when PyTorch is not available."""
53
+
54
+ def __new__(
55
+ cls,
56
+ *_args: Any,
57
+ **_kwargs: Any, # type: ignore[name-defined]
58
+ ) -> Self:
59
+ """Raise ImportError since PyTorch is unavailable."""
60
+ msg = "PyTorch package not available"
61
+ raise ImportError(msg)
62
+
63
+ else:
64
+ torch = import_utils.require("torch")
65
+ nn = torch.nn
66
+
67
+ class TorchDenseHandler(ParameterHandler[nn.Module]):
68
+ """Handler for PyTorch Linear/Dense layers (nn.Linear)."""
69
+
70
+ @property
71
+ def parameter_type(self) -> ParameterType:
72
+ """Return produced type for this handler (DENSE)."""
73
+ return ParameterType.DENSE
74
+
75
+ def can_handle(self, parameter: nn.Module, _param_name: str) -> bool:
76
+ """Return True if parameter is a PyTorch nn.Linear module."""
77
+ return isinstance(parameter, nn.Linear)
78
+
79
+ def process(
80
+ self, parameter: nn.Module, _param_name: str
81
+ ) -> NDArray[np.floating[Any]]:
82
+ """Convert nn.Linear weights to NumPy array using CPU tensors."""
83
+ if not isinstance(parameter, nn.Linear):
84
+ msg = f"Expected nn.Linear, got {type(parameter)}"
85
+ raise TypeError(msg)
86
+ return cast(
87
+ "NDArray[np.floating[Any]]", parameter.weight.detach().cpu().numpy()
88
+ )
89
+
90
+ def get_additional_metadata(
91
+ self, parameter: nn.Module, _param_name: str
92
+ ) -> dict[str, Any]:
93
+ """Return optional torch-specific metadata such as dtype."""
94
+ if not isinstance(parameter, nn.Linear):
95
+ return {}
96
+ return {"torch_dtype": str(parameter.weight.dtype)}
97
+
98
+ class TorchStateDictDenseHandler(ParameterHandler[torch.Tensor]):
99
+ """Handler for 2D weight tensors in PyTorch state dicts."""
100
+
101
+ @property
102
+ def parameter_type(self) -> ParameterType:
103
+ """Return produced type for this handler (DENSE)."""
104
+ return ParameterType.DENSE
105
+
106
+ def can_handle(self, parameter: torch.Tensor, param_name: str) -> bool:
107
+ """Return True for 2D weight tensors with positive number of elements."""
108
+ return (
109
+ param_name.endswith("weight")
110
+ and len(parameter.shape) == _DENSE_WEIGHT_NDIM
111
+ and parameter.numel() > 1
112
+ )
113
+
114
+ def process(
115
+ self, parameter: torch.Tensor, _param_name: str
116
+ ) -> NDArray[np.floating[Any]]:
117
+ """Convert torch.Tensor to NumPy array using CPU tensor."""
118
+ return cast("NDArray[np.floating[Any]]", parameter.detach().cpu().numpy())
119
+
120
+ def get_additional_metadata(
121
+ self, parameter: torch.Tensor, _param_name: str
122
+ ) -> dict[str, Any]:
123
+ """Return optional torch-specific metadata such as dtype."""
124
+ return {"torch_dtype": str(parameter.dtype)}
@@ -0,0 +1,56 @@
1
+ """Parameter extractor interface and protocols.
2
+
3
+ This module defines the core protocol that all parameter extractor
4
+ implementations must follow. It provides a framework-agnostic contract
5
+ for parameter extraction operations from neural network models.
6
+
7
+ The interface ensures consistent parameter extraction behavior across
8
+ different deep learning frameworks while allowing framework-specific
9
+ optimizations and parameter handling strategies.
10
+
11
+ Example:
12
+ >>> extractor: IParameterExtractor = get_extractor(model)
13
+ >>> parameters = extractor.extract_parameters(storage, cache)
14
+ >>> print(f"Extracted {len(parameters)} parameters")
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import TYPE_CHECKING, Protocol, runtime_checkable
20
+
21
+ if TYPE_CHECKING:
22
+ from diffract.core.data.nn.params.interface import IParameterRepository
23
+
24
+
25
+ @runtime_checkable
26
+ class IParameterExtractor(Protocol):
27
+ """Protocol defining the interface for neural network parameter extractors.
28
+
29
+ Provides a unified interface for extracting parameters from neural network
30
+ models across different deep learning frameworks. All extractor implementations
31
+ must implement this protocol to ensure consistent behavior.
32
+
33
+ The extraction process should traverse the model structure and extract all
34
+ relevant parameters along with their metadata, returning a standardized
35
+ parameter collection that can be used across the diffract system.
36
+ """
37
+
38
+ def extract_parameters(
39
+ self,
40
+ parameter_repository: IParameterRepository,
41
+ ) -> None:
42
+ """Extract all parameters from the neural network model.
43
+
44
+ Traverses the model structure and extracts all relevant weight parameters
45
+ along with their metadata. The extraction should be framework-specific
46
+ and add standardized ParameterDataProxy objects to the repository.
47
+
48
+ Args:
49
+ parameter_repository: Repository to store extracted parameters.
50
+
51
+ Raises:
52
+ NotImplementedError: If parameter type is not supported.
53
+ ValueError: If model structure is invalid.
54
+ RuntimeError: If extraction process fails.
55
+ """
56
+ ...
@@ -0,0 +1,94 @@
1
+ """NumPy-specific parameter extractors.
2
+
3
+ This module provides a parameter extractor for plain dictionaries mapping
4
+ parameter names to NumPy arrays. It implements the BaseParameterExtractor
5
+ interface without requiring any deep learning framework, since NumPy arrays
6
+ already match the library's internal weight format.
7
+
8
+ Supported Objects:
9
+ - dict[str, numpy.ndarray]: Mappings of parameter names to arrays
10
+
11
+ Example:
12
+ >>> import numpy as np
13
+ >>> weights = {"encoder.weight": np.random.rand(10, 5)}
14
+ >>> extractor = NumpyDictExtractor(weights)
15
+ >>> parameters = extractor.extract_parameters(storage, cache)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ from typing import TYPE_CHECKING, Any
22
+
23
+ from .base import BaseParameterExtractor
24
+ from .handlers import NumpyDenseHandler
25
+
26
+ if TYPE_CHECKING:
27
+ from collections.abc import Generator
28
+
29
+ from numpy.typing import NDArray
30
+
31
+ from .base import ExtractorOverrides
32
+ from .handlers.base import ParameterHandler
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ class NumpyDictExtractor(BaseParameterExtractor):
38
+ """NumPy dictionary parameter extractor with handler-based processing.
39
+
40
+ Extracts parameters from dictionaries mapping names to NumPy arrays by
41
+ iterating through entries and processing them with registered handlers.
42
+ This is the framework-free entry point: weight matrices loaded from
43
+ ``.npy``/``.npz`` files or via ``safetensors.numpy`` can be analyzed
44
+ without installing any deep learning framework.
45
+
46
+ Example:
47
+ >>> import numpy as np
48
+ >>> weights = {"encoder.weight": np.random.rand(10, 5)}
49
+ >>> extractor = NumpyDictExtractor(weights)
50
+ >>> params = extractor.extract_parameters(storage, cache)
51
+
52
+ Attributes:
53
+ model: Dictionary mapping parameter names to arrays.
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ model: dict[str, NDArray[Any]],
59
+ overrides: ExtractorOverrides | None = None,
60
+ skip_not_implemented_types: bool = True,
61
+ custom_handlers: list[ParameterHandler] | None = None,
62
+ ) -> None:
63
+ """Initialize NumPy dictionary extractor.
64
+
65
+ Args:
66
+ model: Dictionary mapping parameter names to numpy.ndarray.
67
+ overrides: Optional extraction behavior overrides.
68
+ skip_not_implemented_types: Whether to skip unsupported arrays.
69
+ custom_handlers: Optional list of custom parameter handlers.
70
+ """
71
+ super().__init__(model, overrides, skip_not_implemented_types, custom_handlers)
72
+
73
+ def _register_default_handlers(self) -> None:
74
+ """Register default handlers for NumPy array dictionaries.
75
+
76
+ Registers the NumpyDenseHandler for processing dense weight matrices.
77
+ """
78
+ self.handler_registry.register_handler_class(NumpyDenseHandler)
79
+ logger.debug(
80
+ "Registered %d default handlers for NumPy arrays (DENSE only)",
81
+ len(self.handler_registry.list_handlers()),
82
+ )
83
+
84
+ def _iter_parameters(self) -> Generator[tuple[str, NDArray[Any]], None, None]:
85
+ """Iterate over arrays in the dictionary.
86
+
87
+ Yields all name-array pairs from the dictionary, allowing handlers
88
+ to process each array appropriately.
89
+
90
+ Yields:
91
+ Tuples of (parameter_name, array) for each entry in the dict.
92
+ """
93
+ arrays: dict[str, NDArray[Any]] = self.model
94
+ yield from arrays.items()