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,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()
|