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,68 @@
|
|
|
1
|
+
"""Base utilities for DataFrame formatters.
|
|
2
|
+
|
|
3
|
+
Provides shared logic for building scalar and aggregate records.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from diffract.core.export.interface import AggregateData, ResultData
|
|
11
|
+
|
|
12
|
+
SCALAR_COLUMNS = ["model_id", "parameter_name", "parameter_uid", "parameter_type"]
|
|
13
|
+
AGGREGATE_COLUMNS = [
|
|
14
|
+
"field",
|
|
15
|
+
"context_models",
|
|
16
|
+
"context_params",
|
|
17
|
+
"value",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_scalar_records(results: ResultData) -> list[dict[str, Any]]:
|
|
22
|
+
"""Build scalar records from parameter results.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
results: Parameter results dictionary.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
List of scalar records for DataFrame creation.
|
|
29
|
+
"""
|
|
30
|
+
scalar_records: list[dict[str, Any]] = []
|
|
31
|
+
|
|
32
|
+
for param_uid, param_data in results.items():
|
|
33
|
+
metadata = param_data["metadata"]
|
|
34
|
+
field_values = param_data["fields"]
|
|
35
|
+
|
|
36
|
+
base_meta = {
|
|
37
|
+
"parameter_uid": param_uid,
|
|
38
|
+
"model_id": metadata["model_id"],
|
|
39
|
+
"parameter_name": metadata["name"],
|
|
40
|
+
"parameter_type": metadata["parameter_type"],
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
scalar_record: dict[str, Any] = {**base_meta}
|
|
44
|
+
|
|
45
|
+
for key, value in metadata.items():
|
|
46
|
+
if key not in ["model_id", "name", "parameter_type"]:
|
|
47
|
+
scalar_record[f"meta_{key}"] = value
|
|
48
|
+
|
|
49
|
+
scalar_record.update(field_values)
|
|
50
|
+
|
|
51
|
+
# Only add record if it has data beyond base metadata
|
|
52
|
+
if len(scalar_record) > len(base_meta):
|
|
53
|
+
scalar_records.append(scalar_record)
|
|
54
|
+
|
|
55
|
+
return scalar_records
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_aggregate_records(aggregates: AggregateData) -> list[dict[str, Any]]:
|
|
59
|
+
"""Build aggregate records from aggregate results.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
aggregates: Aggregate results list.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List of aggregate records for DataFrame creation.
|
|
66
|
+
"""
|
|
67
|
+
# Aggregates are already in the right format, just return them
|
|
68
|
+
return list(aggregates)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Dictionary formatter for result export.
|
|
2
|
+
|
|
3
|
+
Returns structured results with separate scalars and aggregates dictionaries.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from diffract.core.export.interface import (
|
|
9
|
+
AggregateData,
|
|
10
|
+
IResultFormatter,
|
|
11
|
+
ResultData,
|
|
12
|
+
StructuredExportResult,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DictFormatter(IResultFormatter):
|
|
17
|
+
"""Return results as structured dictionaries."""
|
|
18
|
+
|
|
19
|
+
def format_results(
|
|
20
|
+
self,
|
|
21
|
+
param_results: ResultData,
|
|
22
|
+
aggregate_results: AggregateData,
|
|
23
|
+
_fields: tuple[str, ...],
|
|
24
|
+
) -> StructuredExportResult[dict]:
|
|
25
|
+
"""Return results as structured dict with scalars and aggregates.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
param_results: Parameter results dictionary with scalar fields.
|
|
29
|
+
aggregate_results: Aggregate results list with contextual fields.
|
|
30
|
+
_fields: Field names (unused for dict format).
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
StructuredExportResult with scalars dict and aggregates list.
|
|
34
|
+
"""
|
|
35
|
+
return StructuredExportResult(
|
|
36
|
+
scalars=param_results,
|
|
37
|
+
aggregates=aggregate_results,
|
|
38
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""JSON formatter for result export.
|
|
2
|
+
|
|
3
|
+
Serializes structured results to a JSON string with a custom serializer that
|
|
4
|
+
converts array-like objects (e.g., numpy arrays, tensors) via tolist()
|
|
5
|
+
when available, and falls back to string representation otherwise.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
from diffract.core.export.interface import (
|
|
13
|
+
AggregateData,
|
|
14
|
+
IResultFormatter,
|
|
15
|
+
ResultData,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class JsonFormatter(IResultFormatter):
|
|
20
|
+
"""Convert results to a pretty-printed JSON string."""
|
|
21
|
+
|
|
22
|
+
def format_results(
|
|
23
|
+
self,
|
|
24
|
+
param_results: ResultData,
|
|
25
|
+
aggregate_results: AggregateData,
|
|
26
|
+
_fields: tuple[str, ...],
|
|
27
|
+
) -> str:
|
|
28
|
+
"""Convert results to formatted JSON string.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
param_results: Parameter results dictionary with scalar fields.
|
|
32
|
+
aggregate_results: Aggregate results list with contextual fields.
|
|
33
|
+
_fields: Field names (unused for JSON format).
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
JSON string representation with scalars and aggregates.
|
|
37
|
+
"""
|
|
38
|
+
combined = {
|
|
39
|
+
"scalars": param_results,
|
|
40
|
+
"aggregates": aggregate_results,
|
|
41
|
+
}
|
|
42
|
+
return json.dumps(combined, indent=2, default=self._json_serializer)
|
|
43
|
+
|
|
44
|
+
def _json_serializer(self, obj: object) -> object:
|
|
45
|
+
"""Custom JSON serializer for non-native types.
|
|
46
|
+
|
|
47
|
+
Uses tolist() for array-like structures to preserve value structure.
|
|
48
|
+
Falls back to str() for other objects.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
obj: Object to serialize.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
JSON-serializable representation of the object.
|
|
55
|
+
"""
|
|
56
|
+
if hasattr(obj, "tolist"):
|
|
57
|
+
return obj.tolist()
|
|
58
|
+
return str(obj)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""List formatter for result export.
|
|
2
|
+
|
|
3
|
+
Returns structured results with scalar records as list-of-dicts and
|
|
4
|
+
aggregates as list-of-dicts.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from diffract.core.export.interface import (
|
|
10
|
+
AggregateData,
|
|
11
|
+
IResultFormatter,
|
|
12
|
+
ResultData,
|
|
13
|
+
StructuredExportResult,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .base import build_aggregate_records, build_scalar_records
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ListFormatter(IResultFormatter):
|
|
20
|
+
"""Return results as list-based records."""
|
|
21
|
+
|
|
22
|
+
def format_results(
|
|
23
|
+
self,
|
|
24
|
+
param_results: ResultData,
|
|
25
|
+
aggregate_results: AggregateData,
|
|
26
|
+
_fields: tuple[str, ...],
|
|
27
|
+
) -> StructuredExportResult[list[dict]]:
|
|
28
|
+
"""Return results as lists of records.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
param_results: Parameter results dictionary with scalar fields.
|
|
32
|
+
aggregate_results: Aggregate results list with contextual fields.
|
|
33
|
+
_fields: Field names (unused for list format).
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
StructuredExportResult with scalars list and aggregates list.
|
|
37
|
+
"""
|
|
38
|
+
return StructuredExportResult(
|
|
39
|
+
scalars=build_scalar_records(param_results),
|
|
40
|
+
aggregates=build_aggregate_records(aggregate_results),
|
|
41
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Pandas DataFrame formatter for result export.
|
|
2
|
+
|
|
3
|
+
Creates structured DataFrames separating scalar per-parameter fields from
|
|
4
|
+
contextual/aggregation fields. If pandas is unavailable, raises ImportError.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Self
|
|
11
|
+
|
|
12
|
+
import diffract.core.utils.imports as import_utils
|
|
13
|
+
from diffract.core.export.interface import (
|
|
14
|
+
AggregateData,
|
|
15
|
+
IResultFormatter,
|
|
16
|
+
ResultData,
|
|
17
|
+
StructuredExportResult,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .base import (
|
|
21
|
+
AGGREGATE_COLUMNS,
|
|
22
|
+
SCALAR_COLUMNS,
|
|
23
|
+
build_aggregate_records,
|
|
24
|
+
build_scalar_records,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if not import_utils.is_available("pandas"):
|
|
31
|
+
logger.debug("Failed to import pandas, disabling corresponding formatters")
|
|
32
|
+
|
|
33
|
+
class PandasFormatter(IResultFormatter):
|
|
34
|
+
"""Stub formatter that raises when pandas is unavailable."""
|
|
35
|
+
|
|
36
|
+
def __new__(cls, *_args: Any, **_kwargs: Any) -> Self:
|
|
37
|
+
"""Raise ImportError because pandas is not installed."""
|
|
38
|
+
msg = "pandas package not available"
|
|
39
|
+
raise ImportError(msg)
|
|
40
|
+
|
|
41
|
+
else:
|
|
42
|
+
pd = import_utils.require("pandas")
|
|
43
|
+
|
|
44
|
+
class PandasFormatter(IResultFormatter):
|
|
45
|
+
"""Convert results to structured DataFrames separating scalars/aggregates."""
|
|
46
|
+
|
|
47
|
+
def format_results(
|
|
48
|
+
self,
|
|
49
|
+
param_results: ResultData,
|
|
50
|
+
aggregate_results: AggregateData,
|
|
51
|
+
fields: tuple[str, ...],
|
|
52
|
+
) -> StructuredExportResult[pd.DataFrame]:
|
|
53
|
+
"""Convert results to structured pandas DataFrames.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
param_results: Parameter results dictionary with scalar fields.
|
|
57
|
+
aggregate_results: Aggregate results list with contextual fields.
|
|
58
|
+
fields: Field names requested for export.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
StructuredExportResult with scalars and aggregates DataFrames.
|
|
62
|
+
"""
|
|
63
|
+
if not param_results and not aggregate_results:
|
|
64
|
+
return StructuredExportResult(
|
|
65
|
+
scalars=pd.DataFrame(columns=[*SCALAR_COLUMNS, *fields]),
|
|
66
|
+
aggregates=pd.DataFrame(columns=AGGREGATE_COLUMNS),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
scalar_records = build_scalar_records(param_results)
|
|
70
|
+
aggregate_records = build_aggregate_records(aggregate_results)
|
|
71
|
+
|
|
72
|
+
scalars_df = (
|
|
73
|
+
pd.DataFrame(scalar_records)
|
|
74
|
+
if scalar_records
|
|
75
|
+
else pd.DataFrame(columns=[*SCALAR_COLUMNS, *fields])
|
|
76
|
+
)
|
|
77
|
+
aggregates_df = (
|
|
78
|
+
pd.DataFrame(aggregate_records)
|
|
79
|
+
if aggregate_records
|
|
80
|
+
else pd.DataFrame(columns=AGGREGATE_COLUMNS)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return StructuredExportResult(
|
|
84
|
+
scalars=scalars_df,
|
|
85
|
+
aggregates=aggregates_df,
|
|
86
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Polars DataFrame formatter for result export.
|
|
2
|
+
|
|
3
|
+
Creates structured DataFrames separating scalar per-parameter fields from
|
|
4
|
+
contextual/aggregation fields. If polars is unavailable, raises ImportError.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Self
|
|
11
|
+
|
|
12
|
+
import diffract.core.utils.imports as import_utils
|
|
13
|
+
from diffract.core.export.interface import (
|
|
14
|
+
AggregateData,
|
|
15
|
+
IResultFormatter,
|
|
16
|
+
ResultData,
|
|
17
|
+
StructuredExportResult,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .base import (
|
|
21
|
+
AGGREGATE_COLUMNS,
|
|
22
|
+
SCALAR_COLUMNS,
|
|
23
|
+
build_aggregate_records,
|
|
24
|
+
build_scalar_records,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if not import_utils.is_available("polars"):
|
|
31
|
+
logger.debug("Failed to import polars, disabling corresponding formatters")
|
|
32
|
+
|
|
33
|
+
class PolarsFormatter(IResultFormatter):
|
|
34
|
+
"""Stub formatter that raises when polars is unavailable."""
|
|
35
|
+
|
|
36
|
+
def __new__(cls, *_args: Any, **_kwargs: Any) -> Self:
|
|
37
|
+
"""Raise ImportError because polars is not installed."""
|
|
38
|
+
msg = "polars package not available"
|
|
39
|
+
raise ImportError(msg)
|
|
40
|
+
|
|
41
|
+
else:
|
|
42
|
+
pl = import_utils.require("polars")
|
|
43
|
+
|
|
44
|
+
class PolarsFormatter(IResultFormatter):
|
|
45
|
+
"""Convert results to structured DataFrames separating scalars/aggregates."""
|
|
46
|
+
|
|
47
|
+
def format_results(
|
|
48
|
+
self,
|
|
49
|
+
param_results: ResultData,
|
|
50
|
+
aggregate_results: AggregateData,
|
|
51
|
+
fields: tuple[str, ...],
|
|
52
|
+
) -> StructuredExportResult[pl.DataFrame]:
|
|
53
|
+
"""Convert results to structured polars DataFrames.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
param_results: Parameter results dictionary with scalar fields.
|
|
57
|
+
aggregate_results: Aggregate results list with contextual fields.
|
|
58
|
+
fields: Field names requested for export.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
StructuredExportResult with scalars and aggregates DataFrames.
|
|
62
|
+
"""
|
|
63
|
+
if not param_results and not aggregate_results:
|
|
64
|
+
return StructuredExportResult(
|
|
65
|
+
scalars=pl.DataFrame({c: [] for c in [*SCALAR_COLUMNS, *fields]}),
|
|
66
|
+
aggregates=pl.DataFrame({c: [] for c in AGGREGATE_COLUMNS}),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
scalar_records = build_scalar_records(param_results)
|
|
70
|
+
aggregate_records = build_aggregate_records(aggregate_results)
|
|
71
|
+
|
|
72
|
+
scalars_df = (
|
|
73
|
+
pl.DataFrame(scalar_records)
|
|
74
|
+
if scalar_records
|
|
75
|
+
else pl.DataFrame({c: [] for c in [*SCALAR_COLUMNS, *fields]})
|
|
76
|
+
)
|
|
77
|
+
aggregates_df = (
|
|
78
|
+
pl.DataFrame(aggregate_records)
|
|
79
|
+
if aggregate_records
|
|
80
|
+
else pl.DataFrame({c: [] for c in AGGREGATE_COLUMNS})
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return StructuredExportResult(
|
|
84
|
+
scalars=scalars_df,
|
|
85
|
+
aggregates=aggregates_df,
|
|
86
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Formatter registry and factory for result export.
|
|
2
|
+
|
|
3
|
+
Maintains a mapping from format names to formatter instances and provides
|
|
4
|
+
helpers to register custom formatters and retrieve formatters by name.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import difflib
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
import diffract.core.utils.imports as import_utils
|
|
13
|
+
|
|
14
|
+
from .dict_formatter import DictFormatter
|
|
15
|
+
from .json_formatter import JsonFormatter
|
|
16
|
+
from .list_formatter import ListFormatter
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from diffract.core.export.interface import IResultFormatter
|
|
20
|
+
|
|
21
|
+
# Formats backed by optional dependencies, mapped to the extra providing them
|
|
22
|
+
_OPTIONAL_FORMAT_EXTRAS: dict[str, str] = {
|
|
23
|
+
"pandas": "pandas",
|
|
24
|
+
"polars": "polars",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Built-in formatter instances
|
|
28
|
+
FORMATTERS: dict[str, IResultFormatter] = {
|
|
29
|
+
"dict": DictFormatter(),
|
|
30
|
+
"json": JsonFormatter(),
|
|
31
|
+
"list": ListFormatter(),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if import_utils.is_available("pandas"):
|
|
35
|
+
from .pandas_formatter import PandasFormatter
|
|
36
|
+
|
|
37
|
+
FORMATTERS["pandas"] = PandasFormatter() # type: ignore[arg-type]
|
|
38
|
+
|
|
39
|
+
if import_utils.is_available("polars"):
|
|
40
|
+
from .polars_formatter import PolarsFormatter
|
|
41
|
+
|
|
42
|
+
FORMATTERS["polars"] = PolarsFormatter() # type: ignore[arg-type]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def register_formatter(name: str, formatter: IResultFormatter) -> None:
|
|
46
|
+
"""Register a custom formatter under a given name.
|
|
47
|
+
|
|
48
|
+
Overwrites an existing formatter if the name is already used.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
name: Unique format name.
|
|
52
|
+
formatter: Formatter instance to register.
|
|
53
|
+
"""
|
|
54
|
+
FORMATTERS[name] = formatter
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_formatter(name: str) -> IResultFormatter:
|
|
58
|
+
"""Return a formatter by name or raise ValueError if unknown.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
name: Format name to lookup.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Formatter instance associated with the name.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
ValueError: If name is not registered.
|
|
68
|
+
"""
|
|
69
|
+
try:
|
|
70
|
+
return FORMATTERS[name]
|
|
71
|
+
except KeyError as e:
|
|
72
|
+
if name in _OPTIONAL_FORMAT_EXTRAS:
|
|
73
|
+
extra = _OPTIONAL_FORMAT_EXTRAS[name]
|
|
74
|
+
msg = (
|
|
75
|
+
f"Export format '{name}' requires the optional dependency "
|
|
76
|
+
f"'{extra}'. Install it with: "
|
|
77
|
+
f'pip install "diffract-core[{extra}]".'
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
known = ", ".join(sorted(FORMATTERS))
|
|
81
|
+
close = difflib.get_close_matches(name, FORMATTERS, n=3, cutoff=0.6)
|
|
82
|
+
hint = f" Did you mean: {', '.join(close)}?" if close else ""
|
|
83
|
+
msg = f"Unsupported format '{name}'. Known: {known}.{hint}"
|
|
84
|
+
raise ValueError(msg) from e
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Interfaces (protocols) for result export and formatting.
|
|
2
|
+
|
|
3
|
+
Defines:
|
|
4
|
+
- IResultFormatter: Converts structured results to a target format.
|
|
5
|
+
- IResultExporter: Extracts requested fields from parameters and aggregates,
|
|
6
|
+
then delegates formatting to an IResultFormatter.
|
|
7
|
+
|
|
8
|
+
ResultData structure (parameters):
|
|
9
|
+
{
|
|
10
|
+
"<param_uid>": {
|
|
11
|
+
"metadata": { ... },
|
|
12
|
+
"fields": { "field": value, ... }
|
|
13
|
+
},
|
|
14
|
+
...
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
AggregateData structure:
|
|
18
|
+
[
|
|
19
|
+
{
|
|
20
|
+
"field": "field_name",
|
|
21
|
+
"context_models": ("model1", "model2"),
|
|
22
|
+
"context_params": ("param1",),
|
|
23
|
+
"value": ...,
|
|
24
|
+
},
|
|
25
|
+
...
|
|
26
|
+
]
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, runtime_checkable
|
|
33
|
+
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from diffract.core.data.nn.aggregates.view import AggregateView
|
|
36
|
+
from diffract.core.data.nn.params.interface import IParameterView
|
|
37
|
+
|
|
38
|
+
ResultData = dict[str, dict[str, Any]]
|
|
39
|
+
AggregateData = list[dict[str, Any]]
|
|
40
|
+
|
|
41
|
+
T = TypeVar("T")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class StructuredExportResult[T]:
|
|
46
|
+
"""Container for structured export with separate scalar and aggregate data.
|
|
47
|
+
|
|
48
|
+
Scalars are per-parameter fields (e.g., frob_norm, stable_rank).
|
|
49
|
+
Aggregates are cross-entity fields from aggregation kernels (e.g., l_overlap).
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
scalars: DataFrame/dict with per-parameter metrics indexed by parameter.
|
|
53
|
+
aggregates: DataFrame/dict with aggregation results including context info.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
scalars: T
|
|
57
|
+
aggregates: T
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@runtime_checkable
|
|
61
|
+
class IResultFormatter(Protocol):
|
|
62
|
+
"""Protocol for converting raw results to specific formats."""
|
|
63
|
+
|
|
64
|
+
def format_results(
|
|
65
|
+
self,
|
|
66
|
+
param_results: ResultData,
|
|
67
|
+
aggregate_results: AggregateData,
|
|
68
|
+
fields: tuple[str, ...],
|
|
69
|
+
) -> Any:
|
|
70
|
+
"""Convert raw results to the target format.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
param_results: Parameter results dictionary with scalar fields.
|
|
74
|
+
aggregate_results: Aggregate results list with contextual fields.
|
|
75
|
+
fields: Field names requested for export.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Formatted results in the target format (StructuredExportResult, dict, etc.).
|
|
79
|
+
"""
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@runtime_checkable
|
|
84
|
+
class IResultExporter(Protocol):
|
|
85
|
+
"""Protocol for exporting results from parameter and aggregate repositories."""
|
|
86
|
+
|
|
87
|
+
def export_results(
|
|
88
|
+
self,
|
|
89
|
+
*fields: tuple[str, ...],
|
|
90
|
+
parameters: IParameterView,
|
|
91
|
+
aggregates: AggregateView | None,
|
|
92
|
+
formatter: IResultFormatter,
|
|
93
|
+
) -> Any:
|
|
94
|
+
"""Export results from parameters and aggregates using the specified formatter.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
*fields: Field names to export.
|
|
98
|
+
parameters: Parameter collection to export scalar fields from.
|
|
99
|
+
aggregates: Aggregate view to export contextual fields from.
|
|
100
|
+
formatter: Formatter to use for output conversion.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Formatted results from the formatter.
|
|
104
|
+
"""
|
|
105
|
+
...
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Parallel execution utilities for compute subsystem."""
|
|
2
|
+
|
|
3
|
+
from .containers import ParallelSingletonContainer
|
|
4
|
+
from .execution import map_maybe_parallel
|
|
5
|
+
from .runtime import (
|
|
6
|
+
ParallelCalibration,
|
|
7
|
+
ParallelContext,
|
|
8
|
+
calibrate_thread_pool_overhead,
|
|
9
|
+
get_thread_pool_calibration,
|
|
10
|
+
should_parallelize,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"ParallelCalibration",
|
|
15
|
+
"ParallelContext",
|
|
16
|
+
"ParallelSingletonContainer",
|
|
17
|
+
"calibrate_thread_pool_overhead",
|
|
18
|
+
"get_thread_pool_calibration",
|
|
19
|
+
"map_maybe_parallel",
|
|
20
|
+
"should_parallelize",
|
|
21
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Dependency injection container for pool-per-method executors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import concurrent.futures
|
|
6
|
+
import os
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from dependency_injector import containers, providers
|
|
10
|
+
|
|
11
|
+
from .runtime import ParallelContext, get_thread_pool_calibration
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _normalize_workers(max_workers: int | None) -> int:
|
|
15
|
+
if max_workers is None:
|
|
16
|
+
return max(1, os.cpu_count() or 1)
|
|
17
|
+
return max(1, int(max_workers))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _thread_pool_context_resource(*, workers: int) -> Any:
|
|
21
|
+
executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)
|
|
22
|
+
try:
|
|
23
|
+
calibration = get_thread_pool_calibration(workers)
|
|
24
|
+
yield ParallelContext(
|
|
25
|
+
executor=executor, calibration=calibration, workers=workers
|
|
26
|
+
)
|
|
27
|
+
finally:
|
|
28
|
+
executor.shutdown(wait=True, cancel_futures=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _process_pool_context_resource(*, workers: int) -> Any:
|
|
32
|
+
executor = concurrent.futures.ProcessPoolExecutor(max_workers=workers)
|
|
33
|
+
try:
|
|
34
|
+
yield executor
|
|
35
|
+
finally:
|
|
36
|
+
executor.shutdown(wait=True, cancel_futures=True)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ParallelSingletonContainer(containers.DeclarativeContainer):
|
|
40
|
+
"""Singleton container for project-wide parallelism resources."""
|
|
41
|
+
|
|
42
|
+
config = providers.Configuration()
|
|
43
|
+
|
|
44
|
+
max_workers = providers.Callable(_normalize_workers, config.thread_pool.max_workers)
|
|
45
|
+
process_max_workers = providers.Callable(
|
|
46
|
+
_normalize_workers, config.process_pool.max_workers
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
thread_pool_context = providers.Resource(
|
|
50
|
+
_thread_pool_context_resource,
|
|
51
|
+
workers=max_workers,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
process_pool_context = providers.Resource(
|
|
55
|
+
_process_pool_context_resource,
|
|
56
|
+
workers=process_max_workers,
|
|
57
|
+
)
|