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
diffract/__init__.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Diffract: neural network weight analysis and evolution tracking.
|
|
2
|
+
|
|
3
|
+
Quick start::
|
|
4
|
+
|
|
5
|
+
from diffract import Session
|
|
6
|
+
|
|
7
|
+
# In-memory session (no persistence)
|
|
8
|
+
session = Session(profile="ram")
|
|
9
|
+
|
|
10
|
+
# Persistent local session (SQLite in .diffract/)
|
|
11
|
+
session = Session(profile="local")
|
|
12
|
+
|
|
13
|
+
# Or with a custom config file
|
|
14
|
+
session = Session(config_path="my_config.ini")
|
|
15
|
+
|
|
16
|
+
Available profiles: "ram", "local", "hybrid".
|
|
17
|
+
Use ``diffract.list_profiles()`` to see all options.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import importlib
|
|
23
|
+
from typing import TYPE_CHECKING, Any
|
|
24
|
+
|
|
25
|
+
__version__ = "0.2.1"
|
|
26
|
+
|
|
27
|
+
from .containers import list_profiles
|
|
28
|
+
from .core.data.nn.params.schema import ParameterType
|
|
29
|
+
from .session import ParameterOverrides, Session
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from . import viz as viz
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"ParameterOverrides",
|
|
36
|
+
"ParameterType",
|
|
37
|
+
"Session",
|
|
38
|
+
"__version__",
|
|
39
|
+
"list_profiles",
|
|
40
|
+
"viz",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __getattr__(name: str) -> Any:
|
|
45
|
+
if name == "viz":
|
|
46
|
+
return importlib.import_module(".viz", __name__)
|
|
47
|
+
msg = f"module {__name__!r} has no attribute {name!r}"
|
|
48
|
+
raise AttributeError(msg)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def __dir__() -> list[str]:
|
|
52
|
+
return sorted(__all__)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
; Fast configuration: RAM storage, no cache
|
|
2
|
+
|
|
3
|
+
[logging]
|
|
4
|
+
version = 1
|
|
5
|
+
disable_existing_loggers = false
|
|
6
|
+
|
|
7
|
+
[logging.formatters.standard]
|
|
8
|
+
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
9
|
+
datefmt = "%Y-%m-%d %H:%M:%S"
|
|
10
|
+
|
|
11
|
+
[logging.handlers.console]
|
|
12
|
+
class = logging.StreamHandler
|
|
13
|
+
level = WARNING
|
|
14
|
+
formatter = standard
|
|
15
|
+
stream = ext://sys.stdout
|
|
16
|
+
|
|
17
|
+
[logging.loggers.diffract]
|
|
18
|
+
level = INFO
|
|
19
|
+
handlers = ["console"]
|
|
20
|
+
propagate = false
|
|
21
|
+
|
|
22
|
+
[logging.root]
|
|
23
|
+
level = WARNING
|
|
24
|
+
handlers = ["console"]
|
|
25
|
+
|
|
26
|
+
[storage]
|
|
27
|
+
backend = "ram"
|
|
28
|
+
|
|
29
|
+
[metadata]
|
|
30
|
+
backend = "sqlite"
|
|
31
|
+
|
|
32
|
+
[metadata.sqlite]
|
|
33
|
+
path = ":memory:"
|
|
34
|
+
|
|
35
|
+
[cache]
|
|
36
|
+
backend = "none"
|
|
37
|
+
|
|
38
|
+
[parallel.thread_pool]
|
|
39
|
+
max_workers = 8
|
|
40
|
+
|
|
41
|
+
[parallel.process_pool]
|
|
42
|
+
max_workers = 8
|
|
43
|
+
|
|
44
|
+
[nn.extractor]
|
|
45
|
+
skip_not_implemented_types = true
|
|
46
|
+
|
|
47
|
+
[export]
|
|
48
|
+
default_export_format = "dict"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[logging]
|
|
2
|
+
version = 1
|
|
3
|
+
disable_existing_loggers = false
|
|
4
|
+
|
|
5
|
+
[logging.formatters.standard]
|
|
6
|
+
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
7
|
+
datefmt = "%Y-%m-%d %H:%M:%S"
|
|
8
|
+
|
|
9
|
+
[logging.handlers.console]
|
|
10
|
+
class = logging.StreamHandler
|
|
11
|
+
level = INFO
|
|
12
|
+
formatter = standard
|
|
13
|
+
stream = ext://sys.stdout
|
|
14
|
+
|
|
15
|
+
[logging.handlers.file]
|
|
16
|
+
class = logging.handlers.RotatingFileHandler
|
|
17
|
+
level = INFO
|
|
18
|
+
formatter = standard
|
|
19
|
+
filename = ".diffract/hybrid.log"
|
|
20
|
+
maxBytes = 1048576
|
|
21
|
+
backupCount = 1
|
|
22
|
+
|
|
23
|
+
[logging.loggers.diffract]
|
|
24
|
+
level = INFO
|
|
25
|
+
handlers = ["console", "file"]
|
|
26
|
+
propagate = false
|
|
27
|
+
|
|
28
|
+
[logging.root]
|
|
29
|
+
level = INFO
|
|
30
|
+
handlers = ["console", "file"]
|
|
31
|
+
|
|
32
|
+
[storage]
|
|
33
|
+
backend = "hybrid"
|
|
34
|
+
|
|
35
|
+
[storage.hybrid]
|
|
36
|
+
light = "sqlite"
|
|
37
|
+
heavy = "hdf5"
|
|
38
|
+
array_threshold = 1048576
|
|
39
|
+
|
|
40
|
+
[storage.sqlite]
|
|
41
|
+
path = ".diffract/hybrid/sqlite_storage.db"
|
|
42
|
+
blob_write_workers = 8
|
|
43
|
+
array_threshold = 1048576
|
|
44
|
+
read_mmap_size = 4294967296
|
|
45
|
+
write_mmap_size = 4294967296
|
|
46
|
+
batch_size_limit_bytes = 4294967296
|
|
47
|
+
|
|
48
|
+
[storage.hdf5]
|
|
49
|
+
path = ".diffract/hybrid/hdf5_storage.h5"
|
|
50
|
+
batch_size_limit_bytes = 4294967296
|
|
51
|
+
|
|
52
|
+
[metadata]
|
|
53
|
+
backend = "sqlite"
|
|
54
|
+
|
|
55
|
+
[metadata.sqlite]
|
|
56
|
+
path = ".diffract/hybrid/metadata_index.db"
|
|
57
|
+
|
|
58
|
+
[cache]
|
|
59
|
+
backend = "simple"
|
|
60
|
+
|
|
61
|
+
[cache.simple]
|
|
62
|
+
max_memory_mb = 128_000
|
|
63
|
+
|
|
64
|
+
[parallel.thread_pool]
|
|
65
|
+
max_workers = 8
|
|
66
|
+
|
|
67
|
+
[parallel.process_pool]
|
|
68
|
+
max_workers = 8
|
|
69
|
+
|
|
70
|
+
[nn.extractor]
|
|
71
|
+
skip_not_implemented_types = true
|
|
72
|
+
|
|
73
|
+
[export]
|
|
74
|
+
default_export_format = "pandas"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
; SQLite-backed storage configuration
|
|
2
|
+
|
|
3
|
+
[logging]
|
|
4
|
+
version = 1
|
|
5
|
+
disable_existing_loggers = false
|
|
6
|
+
|
|
7
|
+
[logging.formatters.standard]
|
|
8
|
+
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
9
|
+
datefmt = "%Y-%m-%d %H:%M:%S"
|
|
10
|
+
|
|
11
|
+
[logging.handlers.console]
|
|
12
|
+
class = logging.StreamHandler
|
|
13
|
+
level = WARNING
|
|
14
|
+
formatter = standard
|
|
15
|
+
stream = ext://sys.stdout
|
|
16
|
+
|
|
17
|
+
[logging.handlers.file]
|
|
18
|
+
class = logging.handlers.RotatingFileHandler
|
|
19
|
+
level = INFO
|
|
20
|
+
formatter = standard
|
|
21
|
+
filename = ".diffract/sqlite.log"
|
|
22
|
+
maxBytes = 1048576
|
|
23
|
+
backupCount = 1
|
|
24
|
+
|
|
25
|
+
[logging.loggers.diffract]
|
|
26
|
+
level = DEBUG
|
|
27
|
+
handlers = ["console", "file"]
|
|
28
|
+
propagate = false
|
|
29
|
+
|
|
30
|
+
[logging.root]
|
|
31
|
+
level = DEBUG
|
|
32
|
+
handlers = ["console", "file"]
|
|
33
|
+
|
|
34
|
+
[storage]
|
|
35
|
+
backend = "sqlite"
|
|
36
|
+
|
|
37
|
+
[storage.sqlite]
|
|
38
|
+
path = ".diffract/sqlite/sqlite_storage.db"
|
|
39
|
+
|
|
40
|
+
[metadata]
|
|
41
|
+
backend = "sqlite"
|
|
42
|
+
|
|
43
|
+
[metadata.sqlite]
|
|
44
|
+
path = ".diffract/sqlite/metadata_index.db"
|
|
45
|
+
|
|
46
|
+
[cache]
|
|
47
|
+
backend = "simple"
|
|
48
|
+
|
|
49
|
+
[cache.simple]
|
|
50
|
+
max_memory_mb = 4096
|
|
51
|
+
|
|
52
|
+
[parallel.thread_pool]
|
|
53
|
+
max_workers = 8
|
|
54
|
+
|
|
55
|
+
[parallel.process_pool]
|
|
56
|
+
max_workers = 8
|
|
57
|
+
|
|
58
|
+
[nn.extractor]
|
|
59
|
+
skip_not_implemented_types = true
|
|
60
|
+
|
|
61
|
+
[export]
|
|
62
|
+
default_export_format = "pandas"
|
diffract/containers.py
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
"""Main application dependency injection container.
|
|
2
|
+
|
|
3
|
+
This module provides the MainContainer class that orchestrates all subsystem
|
|
4
|
+
containers for the diffract application, including storage, cache, compute,
|
|
5
|
+
model parameters, and export functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import configparser
|
|
11
|
+
import contextlib
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from dependency_injector import containers, providers
|
|
20
|
+
|
|
21
|
+
from .core.cache.containers import CacheContainer
|
|
22
|
+
from .core.compute.containers import (
|
|
23
|
+
ComputeContainer,
|
|
24
|
+
ComputeSingletonContainer,
|
|
25
|
+
ComputeSingletonContainerWiringConfig,
|
|
26
|
+
)
|
|
27
|
+
from .core.data.metadata.containers import MetadataContainer
|
|
28
|
+
from .core.data.nn.containers import ModelParametersContainer
|
|
29
|
+
from .core.export.containers import ExportContainer
|
|
30
|
+
from .core.parallel import ParallelSingletonContainer
|
|
31
|
+
from .core.storage.containers import StorageContainer
|
|
32
|
+
from .core.utils.exceptions import format_exception_message
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
# Built-in configuration profiles for common use cases.
|
|
37
|
+
#
|
|
38
|
+
# Values are relative paths to INI configs shipped with the repo/package.
|
|
39
|
+
PROFILES: dict[str, str] = {
|
|
40
|
+
"ram": "configs/fast_speed_without_disk.ini",
|
|
41
|
+
"local": "configs/sqlite.ini",
|
|
42
|
+
"hybrid": "configs/hybrid.ini",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def list_profiles() -> list[str]:
|
|
47
|
+
"""Return available profile names."""
|
|
48
|
+
return list(PROFILES.keys())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _resolve_packaged_or_repo_path(relative_path: str) -> Path:
|
|
52
|
+
"""Resolve a config path within package or repository layout."""
|
|
53
|
+
candidates = [
|
|
54
|
+
# Packaged layout: <site-packages>/diffract/<relative_path>
|
|
55
|
+
Path(__file__).resolve().parent / relative_path,
|
|
56
|
+
# Repo layout: <root>/<relative_path>
|
|
57
|
+
Path(__file__).resolve().parents[2] / relative_path,
|
|
58
|
+
]
|
|
59
|
+
for candidate in candidates:
|
|
60
|
+
if candidate.exists():
|
|
61
|
+
return candidate
|
|
62
|
+
msg = (
|
|
63
|
+
f"Config file not found for relative path {relative_path!r}. "
|
|
64
|
+
f"Tried: {', '.join(str(c) for c in candidates)}"
|
|
65
|
+
)
|
|
66
|
+
raise FileNotFoundError(msg)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_profile_config(profile: str) -> dict[str, Any]:
|
|
70
|
+
"""Get configuration dictionary for a profile."""
|
|
71
|
+
if profile not in PROFILES:
|
|
72
|
+
available = ", ".join(PROFILES.keys())
|
|
73
|
+
msg = f"Unknown profile '{profile}'. Available: {available}"
|
|
74
|
+
raise ValueError(msg)
|
|
75
|
+
config_path = _resolve_packaged_or_repo_path(PROFILES[profile])
|
|
76
|
+
return _parse_ini_config(config_path)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
_FALLBACK_LOGGING_CONFIG: dict[str, Any] = {
|
|
80
|
+
"version": 1,
|
|
81
|
+
"disable_existing_loggers": False,
|
|
82
|
+
"formatters": {
|
|
83
|
+
"standard": {"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"}
|
|
84
|
+
},
|
|
85
|
+
"handlers": {
|
|
86
|
+
"console": {
|
|
87
|
+
"class": "logging.StreamHandler",
|
|
88
|
+
"level": "INFO",
|
|
89
|
+
"formatter": "standard",
|
|
90
|
+
"stream": "ext://sys.stdout",
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"root": {"level": "INFO", "handlers": ["console"]},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _expand_env_vars(value: str) -> str:
|
|
98
|
+
"""Expand ${VAR} and $VAR patterns with environment variables."""
|
|
99
|
+
|
|
100
|
+
def replacer(match: re.Match[str]) -> str:
|
|
101
|
+
var_name = match.group(1) or match.group(2)
|
|
102
|
+
return os.environ.get(var_name, match.group(0))
|
|
103
|
+
|
|
104
|
+
return re.sub(r"\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)", replacer, value)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _coerce_ini_value(value: str) -> Any:
|
|
108
|
+
"""Convert an INI string value into a reasonable Python type.
|
|
109
|
+
|
|
110
|
+
INI files store values as strings; for service initialization configs we
|
|
111
|
+
accept basic scalar types and JSON literals for lists/dicts.
|
|
112
|
+
Supports ${VAR} syntax for environment variable expansion.
|
|
113
|
+
"""
|
|
114
|
+
raw = _expand_env_vars(value.strip())
|
|
115
|
+
lowered = raw.lower()
|
|
116
|
+
|
|
117
|
+
if lowered in {"none", "null"}:
|
|
118
|
+
return None
|
|
119
|
+
if lowered in {"true", "false"}:
|
|
120
|
+
return lowered == "true"
|
|
121
|
+
|
|
122
|
+
# Try JSON first for explicit literals like lists/dicts/quoted strings.
|
|
123
|
+
if raw.startswith(("{", "[", '"')) or raw in {"0", "1"}:
|
|
124
|
+
with contextlib.suppress(json.JSONDecodeError):
|
|
125
|
+
return json.loads(raw)
|
|
126
|
+
|
|
127
|
+
# Numeric scalars (allow underscores).
|
|
128
|
+
int_candidate = raw.replace("_", "")
|
|
129
|
+
with contextlib.suppress(ValueError):
|
|
130
|
+
return int(int_candidate)
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
return float(int_candidate)
|
|
134
|
+
except ValueError:
|
|
135
|
+
return raw
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _parse_ini_config(path: Path) -> dict[str, Any]:
|
|
139
|
+
"""Parse an INI file into a nested dictionary.
|
|
140
|
+
|
|
141
|
+
Section names can use dot-notation to represent nesting, e.g.:
|
|
142
|
+
[storage.hdf5]
|
|
143
|
+
path=/tmp/main.h5
|
|
144
|
+
"""
|
|
145
|
+
# Disable interpolation because logging formatters use "%(name)s" syntax.
|
|
146
|
+
parser = configparser.ConfigParser(interpolation=None)
|
|
147
|
+
# Preserve case for keys like "maxBytes" used by logging.config.dictConfig.
|
|
148
|
+
parser.optionxform = str
|
|
149
|
+
parser.read(path)
|
|
150
|
+
|
|
151
|
+
data: dict[str, Any] = {}
|
|
152
|
+
for section in parser.sections():
|
|
153
|
+
cursor: dict[str, Any] = data
|
|
154
|
+
for part in section.split("."):
|
|
155
|
+
cursor = cursor.setdefault(part, {})
|
|
156
|
+
for key, val in parser.items(section):
|
|
157
|
+
cursor[key] = _coerce_ini_value(val)
|
|
158
|
+
return data
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _default_config_path() -> Path | None:
|
|
162
|
+
"""Return the default launch config path if present."""
|
|
163
|
+
with contextlib.suppress(FileNotFoundError):
|
|
164
|
+
return _resolve_packaged_or_repo_path(PROFILES["ram"])
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class MainContainer(containers.DeclarativeContainer):
|
|
169
|
+
"""Main application container that orchestrates all subsystem containers.
|
|
170
|
+
|
|
171
|
+
This container manages the dependency injection for the entire application,
|
|
172
|
+
providing centralized configuration and wiring of all subsystem components
|
|
173
|
+
including storage, caching, compute kernels, model parameters, and export.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
# Configuration
|
|
177
|
+
config = providers.Configuration()
|
|
178
|
+
|
|
179
|
+
# Logging configuration
|
|
180
|
+
logging_config = providers.Resource(
|
|
181
|
+
"logging.config.dictConfig",
|
|
182
|
+
config=config.logging,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Subsystem containers
|
|
186
|
+
storage = providers.Container(
|
|
187
|
+
StorageContainer,
|
|
188
|
+
config=config.storage,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
cache = providers.Container(
|
|
192
|
+
CacheContainer,
|
|
193
|
+
config=config.cache,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
metadata = providers.Container(
|
|
197
|
+
MetadataContainer,
|
|
198
|
+
config=config.metadata,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
compute_singleton = providers.Container(ComputeSingletonContainer)
|
|
202
|
+
|
|
203
|
+
parallel_singleton = providers.Container(
|
|
204
|
+
ParallelSingletonContainer,
|
|
205
|
+
config=config.parallel,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
nn = providers.Container(
|
|
209
|
+
ModelParametersContainer,
|
|
210
|
+
storage_manager=storage.storage_manager_resource,
|
|
211
|
+
cache_manager=cache.cache_manager_resource,
|
|
212
|
+
metadata_index=metadata.metadata_index_resource,
|
|
213
|
+
parallel=parallel_singleton.thread_pool_context,
|
|
214
|
+
config=config.nn,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
compute = providers.Container(
|
|
218
|
+
ComputeContainer,
|
|
219
|
+
config=config.compute,
|
|
220
|
+
compute_singleton=compute_singleton,
|
|
221
|
+
parallel=parallel_singleton.thread_pool_context,
|
|
222
|
+
process_pool=parallel_singleton.process_pool_context,
|
|
223
|
+
aggregate_repository=nn.aggregate_repository,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
export = providers.Container(
|
|
227
|
+
ExportContainer,
|
|
228
|
+
config=config.export,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class WiringConfiguration:
|
|
233
|
+
"""Complete application wiring configuration.
|
|
234
|
+
|
|
235
|
+
Manages the dependency injection wiring for all containers in the application,
|
|
236
|
+
ensuring proper module and package registration for dependency resolution.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
@classmethod
|
|
240
|
+
def get_wiring_configs(cls) -> list[dict[str, Any]]:
|
|
241
|
+
"""Get all wiring configurations for the application.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
List of dictionaries containing container, modules, and packages
|
|
245
|
+
configuration for each subsystem that requires wiring.
|
|
246
|
+
"""
|
|
247
|
+
return [
|
|
248
|
+
{
|
|
249
|
+
"container": ComputeSingletonContainer,
|
|
250
|
+
"modules": ComputeSingletonContainerWiringConfig.modules,
|
|
251
|
+
"packages": ComputeSingletonContainerWiringConfig.packages,
|
|
252
|
+
},
|
|
253
|
+
]
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
def wire(cls, container: MainContainer) -> None:
|
|
257
|
+
"""Wire the entire application with all subsystem containers.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
container: The main application container to wire.
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
Exception: If wiring fails for any container or module.
|
|
264
|
+
"""
|
|
265
|
+
try:
|
|
266
|
+
# Wire main container
|
|
267
|
+
container.wire(
|
|
268
|
+
modules=[
|
|
269
|
+
__name__,
|
|
270
|
+
"diffract.session",
|
|
271
|
+
"diffract.session.namespaces",
|
|
272
|
+
"diffract.session.namespaces.models",
|
|
273
|
+
],
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Wire subsystem containers
|
|
277
|
+
for config in cls.get_wiring_configs():
|
|
278
|
+
container_cls = config["container"]
|
|
279
|
+
parts = re.findall(r"[A-Z][^A-Z]*", container_cls.__name__)
|
|
280
|
+
parts = [p.lower() for p in parts]
|
|
281
|
+
parts.remove("container")
|
|
282
|
+
container_name = "_".join(parts)
|
|
283
|
+
container_instance = getattr(container, container_name)
|
|
284
|
+
|
|
285
|
+
if config.get("modules"):
|
|
286
|
+
container_instance.wire(modules=config["modules"])
|
|
287
|
+
|
|
288
|
+
if config.get("packages"):
|
|
289
|
+
container_instance.wire(packages=config["packages"])
|
|
290
|
+
|
|
291
|
+
logger.debug("Successfully wired all application containers")
|
|
292
|
+
|
|
293
|
+
except Exception:
|
|
294
|
+
logger.exception("Failed to wire application containers")
|
|
295
|
+
raise
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
compute_singleton_container = ComputeSingletonContainer()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def create_main_container(
|
|
302
|
+
config_path: str | Path | None = None,
|
|
303
|
+
profile: str | None = None,
|
|
304
|
+
) -> MainContainer:
|
|
305
|
+
"""Create and configure the main application container.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
config_path: Path to configuration file (YAML/JSON/INI). Overrides
|
|
309
|
+
profile settings if both are provided.
|
|
310
|
+
profile: Built-in profile name ("ram", "local", "hybrid").
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Configured MainContainer instance with all subsystems wired
|
|
314
|
+
and default kernels registered.
|
|
315
|
+
|
|
316
|
+
Raises:
|
|
317
|
+
ValueError: If profile name is not recognized.
|
|
318
|
+
Exception: If container creation or wiring fails.
|
|
319
|
+
"""
|
|
320
|
+
container = MainContainer(
|
|
321
|
+
compute_singleton=compute_singleton_container,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
repo_root = Path(__file__).resolve().parents[2]
|
|
325
|
+
cwd = Path.cwd()
|
|
326
|
+
|
|
327
|
+
config_loaded = False
|
|
328
|
+
|
|
329
|
+
if config_path is not None:
|
|
330
|
+
config_path = Path(config_path)
|
|
331
|
+
if config_path.exists():
|
|
332
|
+
suffix = config_path.suffix.lower()
|
|
333
|
+
match suffix:
|
|
334
|
+
case ".yaml" | ".yml":
|
|
335
|
+
container.config.from_yaml(str(config_path))
|
|
336
|
+
case ".json":
|
|
337
|
+
container.config.from_json(str(config_path))
|
|
338
|
+
case ".ini":
|
|
339
|
+
container.config.from_dict(_parse_ini_config(config_path))
|
|
340
|
+
case _:
|
|
341
|
+
msg = f"Unsupported config file extension: '{suffix}'"
|
|
342
|
+
raise ValueError(msg)
|
|
343
|
+
logger.info("Using config file: %s", config_path)
|
|
344
|
+
config_loaded = True
|
|
345
|
+
else:
|
|
346
|
+
logger.warning("Configuration file not found: %s", config_path)
|
|
347
|
+
|
|
348
|
+
if not config_loaded and profile is not None:
|
|
349
|
+
profile_config = get_profile_config(profile)
|
|
350
|
+
container.config.from_dict(profile_config)
|
|
351
|
+
logger.info("Using profile: %s", profile)
|
|
352
|
+
config_loaded = True
|
|
353
|
+
|
|
354
|
+
if not config_loaded:
|
|
355
|
+
default_path = _default_config_path()
|
|
356
|
+
if default_path is not None:
|
|
357
|
+
container.config.from_dict(_parse_ini_config(default_path))
|
|
358
|
+
logger.debug("Loaded default configuration from %s", default_path)
|
|
359
|
+
|
|
360
|
+
# Resolve relative file paths to absolute paths.
|
|
361
|
+
base_path = cwd if profile is not None else repo_root
|
|
362
|
+
resolved = container.config()
|
|
363
|
+
for key in ("path", "filename"):
|
|
364
|
+
stack: list[dict[str, Any]] = [resolved]
|
|
365
|
+
while stack:
|
|
366
|
+
cur = stack.pop()
|
|
367
|
+
for k, v in list(cur.items()):
|
|
368
|
+
if isinstance(v, dict):
|
|
369
|
+
stack.append(v)
|
|
370
|
+
continue
|
|
371
|
+
if k != key or not isinstance(v, str):
|
|
372
|
+
continue
|
|
373
|
+
value = v.strip().strip('"')
|
|
374
|
+
if value.startswith(("ext://", "/", "~")):
|
|
375
|
+
continue
|
|
376
|
+
cur[k] = str(base_path / value)
|
|
377
|
+
container.config.from_dict(resolved)
|
|
378
|
+
|
|
379
|
+
# Configs without a [logging] section get the fallback silently.
|
|
380
|
+
if not resolved.get("logging"):
|
|
381
|
+
container.config.logging.override(_FALLBACK_LOGGING_CONFIG)
|
|
382
|
+
|
|
383
|
+
# logging.config.dictConfig cannot create missing log directories itself.
|
|
384
|
+
for handler in resolved.get("logging", {}).get("handlers", {}).values():
|
|
385
|
+
filename = handler.get("filename") if isinstance(handler, dict) else None
|
|
386
|
+
if isinstance(filename, str):
|
|
387
|
+
Path(filename.strip().strip('"')).parent.mkdir(parents=True, exist_ok=True)
|
|
388
|
+
|
|
389
|
+
try:
|
|
390
|
+
container.logging_config()
|
|
391
|
+
except Exception as e: # noqa: BLE001
|
|
392
|
+
logger.warning(
|
|
393
|
+
"Failed to configure logging; using fallback config: %s",
|
|
394
|
+
format_exception_message(e),
|
|
395
|
+
)
|
|
396
|
+
logger.debug("Logging configuration failure details", exc_info=True)
|
|
397
|
+
container.config.logging.override(_FALLBACK_LOGGING_CONFIG)
|
|
398
|
+
container.logging_config()
|
|
399
|
+
|
|
400
|
+
# Wire all containers
|
|
401
|
+
WiringConfiguration.wire(container)
|
|
402
|
+
|
|
403
|
+
container.compute_singleton.register_default_kernels()
|
|
404
|
+
|
|
405
|
+
return container
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Core module for diffract."""
|
|
2
|
+
|
|
3
|
+
from diffract.core.constants import (
|
|
4
|
+
CONTEXT_SEPARATOR,
|
|
5
|
+
HDF5_INDEX_DATASET,
|
|
6
|
+
HDF5_INDEX_GROUP,
|
|
7
|
+
HDF5_INDEX_TOMBSTONE,
|
|
8
|
+
MODELS_CONTEXT_PREFIX,
|
|
9
|
+
PARAMS_CONTEXT_PREFIX,
|
|
10
|
+
REGEX_PREFIX,
|
|
11
|
+
WEIGHTS_FIELD,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CONTEXT_SEPARATOR",
|
|
16
|
+
"HDF5_INDEX_DATASET",
|
|
17
|
+
"HDF5_INDEX_GROUP",
|
|
18
|
+
"HDF5_INDEX_TOMBSTONE",
|
|
19
|
+
"MODELS_CONTEXT_PREFIX",
|
|
20
|
+
"PARAMS_CONTEXT_PREFIX",
|
|
21
|
+
"REGEX_PREFIX",
|
|
22
|
+
"WEIGHTS_FIELD",
|
|
23
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Cache management module providing flexible caching backends.
|
|
2
|
+
|
|
3
|
+
This module offers a unified interface for caching with support for multiple
|
|
4
|
+
backends including Redis and in-memory implementations. It uses dependency
|
|
5
|
+
injection for flexible configuration and backend selection.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from diffract.core.cache import CacheContainer
|
|
9
|
+
>>> container = CacheContainer()
|
|
10
|
+
>>> cache_manager = container.cache_manager()
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .containers import CacheContainer
|
|
14
|
+
from .interface import UID, ICacheManager
|
|
15
|
+
from .redis_manager import RedisLRUCacheManager
|
|
16
|
+
from .simple_manager import SimpleLRUCacheManager
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"UID",
|
|
20
|
+
"CacheContainer",
|
|
21
|
+
"ICacheManager",
|
|
22
|
+
"RedisLRUCacheManager",
|
|
23
|
+
"SimpleLRUCacheManager",
|
|
24
|
+
]
|