diffract-core 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. diffract/__init__.py +52 -0
  2. diffract/configs/fast_speed_without_disk.ini +48 -0
  3. diffract/configs/hybrid.ini +74 -0
  4. diffract/configs/sqlite.ini +62 -0
  5. diffract/containers.py +405 -0
  6. diffract/core/__init__.py +23 -0
  7. diffract/core/cache/__init__.py +24 -0
  8. diffract/core/cache/containers.py +93 -0
  9. diffract/core/cache/interface.py +117 -0
  10. diffract/core/cache/redis_manager.py +464 -0
  11. diffract/core/cache/simple_manager.py +478 -0
  12. diffract/core/compute/__init__.py +46 -0
  13. diffract/core/compute/config.py +48 -0
  14. diffract/core/compute/containers.py +81 -0
  15. diffract/core/compute/decorator.py +125 -0
  16. diffract/core/compute/exceptions.py +31 -0
  17. diffract/core/compute/execution/__init__.py +14 -0
  18. diffract/core/compute/execution/_types.py +70 -0
  19. diffract/core/compute/execution/aggregation.py +74 -0
  20. diffract/core/compute/execution/aggregation_runner.py +362 -0
  21. diffract/core/compute/execution/enums.py +38 -0
  22. diffract/core/compute/execution/executor.py +125 -0
  23. diffract/core/compute/execution/parameter_runner.py +125 -0
  24. diffract/core/compute/execution/restrictions.py +61 -0
  25. diffract/core/compute/execution/strategy.py +118 -0
  26. diffract/core/compute/execution/utils.py +112 -0
  27. diffract/core/compute/extensions/__init__.py +0 -0
  28. diffract/core/compute/extensions/power_law.py +1051 -0
  29. diffract/core/compute/extensions/rmt.py +150 -0
  30. diffract/core/compute/extensions/utils.py +36 -0
  31. diffract/core/compute/field_signature.py +183 -0
  32. diffract/core/compute/kernels/__init__.py +48 -0
  33. diffract/core/compute/kernels/alignment.py +106 -0
  34. diffract/core/compute/kernels/heavy_tailed.py +394 -0
  35. diffract/core/compute/kernels/marchenko_pastur.py +99 -0
  36. diffract/core/compute/kernels/mat_decomposition.py +101 -0
  37. diffract/core/compute/kernels/mat_properties.py +53 -0
  38. diffract/core/compute/kernels/model_quality.py +27 -0
  39. diffract/core/compute/kernels/norms.py +88 -0
  40. diffract/core/compute/kernels/ranks.py +35 -0
  41. diffract/core/compute/kernels/tracy_widom.py +36 -0
  42. diffract/core/compute/metadata.py +67 -0
  43. diffract/core/compute/registry.py +485 -0
  44. diffract/core/constants.py +147 -0
  45. diffract/core/data/__init__.py +32 -0
  46. diffract/core/data/interface.py +304 -0
  47. diffract/core/data/metadata/__init__.py +15 -0
  48. diffract/core/data/metadata/containers.py +60 -0
  49. diffract/core/data/metadata/interface.py +208 -0
  50. diffract/core/data/metadata/sqlite_index.py +604 -0
  51. diffract/core/data/nn/__init__.py +43 -0
  52. diffract/core/data/nn/aggregates/__init__.py +35 -0
  53. diffract/core/data/nn/aggregates/metadata.py +96 -0
  54. diffract/core/data/nn/aggregates/proxy.py +57 -0
  55. diffract/core/data/nn/aggregates/repository.py +87 -0
  56. diffract/core/data/nn/aggregates/view.py +307 -0
  57. diffract/core/data/nn/containers.py +86 -0
  58. diffract/core/data/nn/extractors/__init__.py +49 -0
  59. diffract/core/data/nn/extractors/base.py +300 -0
  60. diffract/core/data/nn/extractors/factory.py +234 -0
  61. diffract/core/data/nn/extractors/flax.py +112 -0
  62. diffract/core/data/nn/extractors/handlers/__init__.py +39 -0
  63. diffract/core/data/nn/extractors/handlers/base.py +110 -0
  64. diffract/core/data/nn/extractors/handlers/flax_handlers.py +71 -0
  65. diffract/core/data/nn/extractors/handlers/numpy_handlers.py +63 -0
  66. diffract/core/data/nn/extractors/handlers/onnx_handlers.py +67 -0
  67. diffract/core/data/nn/extractors/handlers/tensorflow_handlers.py +82 -0
  68. diffract/core/data/nn/extractors/handlers/torch_handlers.py +124 -0
  69. diffract/core/data/nn/extractors/interface.py +56 -0
  70. diffract/core/data/nn/extractors/numpy.py +94 -0
  71. diffract/core/data/nn/extractors/onnx.py +108 -0
  72. diffract/core/data/nn/extractors/tensorflow.py +99 -0
  73. diffract/core/data/nn/extractors/torch.py +204 -0
  74. diffract/core/data/nn/params/__init__.py +27 -0
  75. diffract/core/data/nn/params/interface.py +402 -0
  76. diffract/core/data/nn/params/metadata.py +110 -0
  77. diffract/core/data/nn/params/proxy.py +93 -0
  78. diffract/core/data/nn/params/repository.py +45 -0
  79. diffract/core/data/nn/params/schema.py +82 -0
  80. diffract/core/data/nn/params/view.py +393 -0
  81. diffract/core/data/proxy.py +246 -0
  82. diffract/core/data/repository.py +227 -0
  83. diffract/core/data/utils.py +33 -0
  84. diffract/core/data/view.py +389 -0
  85. diffract/core/export/__init__.py +27 -0
  86. diffract/core/export/containers.py +47 -0
  87. diffract/core/export/exporters.py +191 -0
  88. diffract/core/export/formatters/__init__.py +23 -0
  89. diffract/core/export/formatters/base.py +68 -0
  90. diffract/core/export/formatters/dict_formatter.py +38 -0
  91. diffract/core/export/formatters/json_formatter.py +58 -0
  92. diffract/core/export/formatters/list_formatter.py +41 -0
  93. diffract/core/export/formatters/pandas_formatter.py +86 -0
  94. diffract/core/export/formatters/polars_formatter.py +86 -0
  95. diffract/core/export/formatters/registry.py +84 -0
  96. diffract/core/export/interface.py +105 -0
  97. diffract/core/parallel/__init__.py +21 -0
  98. diffract/core/parallel/containers.py +57 -0
  99. diffract/core/parallel/execution.py +91 -0
  100. diffract/core/parallel/runtime.py +91 -0
  101. diffract/core/storage/__init__.py +35 -0
  102. diffract/core/storage/base_manager.py +330 -0
  103. diffract/core/storage/containers.py +122 -0
  104. diffract/core/storage/hdf5_manager.py +856 -0
  105. diffract/core/storage/hybrid_manager.py +218 -0
  106. diffract/core/storage/interface.py +222 -0
  107. diffract/core/storage/metadata.py +89 -0
  108. diffract/core/storage/ram_manager.py +159 -0
  109. diffract/core/storage/sqlite_manager.py +1062 -0
  110. diffract/core/storage/zarr_manager.py +992 -0
  111. diffract/core/utils/__init__.py +45 -0
  112. diffract/core/utils/build.py +46 -0
  113. diffract/core/utils/exceptions.py +11 -0
  114. diffract/core/utils/hashing.py +90 -0
  115. diffract/core/utils/imports.py +292 -0
  116. diffract/core/utils/math.py +15 -0
  117. diffract/py.typed +0 -0
  118. diffract/session/__init__.py +24 -0
  119. diffract/session/errors.py +17 -0
  120. diffract/session/field_cache.py +216 -0
  121. diffract/session/namespaces/__init__.py +15 -0
  122. diffract/session/namespaces/compute/__init__.py +219 -0
  123. diffract/session/namespaces/models/__init__.py +282 -0
  124. diffract/session/namespaces/models/parameters/__init__.py +133 -0
  125. diffract/session/namespaces/models/parameters/meta_patcher.py +167 -0
  126. diffract/session/namespaces/results/__init__.py +378 -0
  127. diffract/session/namespaces/results/eraser.py +129 -0
  128. diffract/session/namespaces/results/injester.py +249 -0
  129. diffract/session/namespaces/utils/__init__.py +141 -0
  130. diffract/session/namespaces/utils/merger.py +295 -0
  131. diffract/session/namespaces/viz/__init__.py +91 -0
  132. diffract/session/namespaces/viz/_utils.py +14 -0
  133. diffract/session/namespaces/viz/box.py +207 -0
  134. diffract/session/namespaces/viz/grid.py +160 -0
  135. diffract/session/namespaces/viz/heatmap.py +164 -0
  136. diffract/session/namespaces/viz/scatter.py +202 -0
  137. diffract/session/namespaces/viz/sparkline.py +270 -0
  138. diffract/session/namespaces/viz/violin.py +212 -0
  139. diffract/session/session.py +260 -0
  140. diffract/session/utils.py +81 -0
  141. diffract/viz/__init__.py +42 -0
  142. diffract/viz/data/__init__.py +34 -0
  143. diffract/viz/data/detection.py +57 -0
  144. diffract/viz/data/extraction.py +117 -0
  145. diffract/viz/data/filtering.py +57 -0
  146. diffract/viz/data/ordering.py +129 -0
  147. diffract/viz/data/provider.py +99 -0
  148. diffract/viz/data/types.py +98 -0
  149. diffract/viz/plots/__init__.py +37 -0
  150. diffract/viz/plots/base/__init__.py +20 -0
  151. diffract/viz/plots/base/axis.py +219 -0
  152. diffract/viz/plots/base/coloraxis.py +133 -0
  153. diffract/viz/plots/base/configurator.py +18 -0
  154. diffract/viz/plots/base/jitter.py +368 -0
  155. diffract/viz/plots/base/line.py +197 -0
  156. diffract/viz/plots/base/marker.py +278 -0
  157. diffract/viz/plots/base/overlay.py +14 -0
  158. diffract/viz/plots/base/plot.py +110 -0
  159. diffract/viz/plots/base/update.py +50 -0
  160. diffract/viz/plots/boxplot.py +283 -0
  161. diffract/viz/plots/cluster.py +374 -0
  162. diffract/viz/plots/heatmap.py +168 -0
  163. diffract/viz/plots/scatter.py +233 -0
  164. diffract/viz/plots/sparkline.py +405 -0
  165. diffract/viz/plots/subplots/__init__.py +32 -0
  166. diffract/viz/plots/subplots/coloraxis.py +339 -0
  167. diffract/viz/plots/subplots/factory.py +713 -0
  168. diffract/viz/plots/subplots/grid.py +214 -0
  169. diffract/viz/plots/subplots/layout.py +370 -0
  170. diffract/viz/plots/subplots/spec.py +39 -0
  171. diffract/viz/plots/violin.py +298 -0
  172. diffract/viz/renderer.py +513 -0
  173. diffract/viz/styling/__init__.py +78 -0
  174. diffract/viz/styling/palettes/__init__.py +20 -0
  175. diffract/viz/styling/palettes/bundle.py +16 -0
  176. diffract/viz/styling/palettes/color.py +83 -0
  177. diffract/viz/styling/palettes/dashes.py +27 -0
  178. diffract/viz/styling/palettes/symbols.py +42 -0
  179. diffract/viz/styling/resolvers/__init__.py +11 -0
  180. diffract/viz/styling/resolvers/categorical.py +68 -0
  181. diffract/viz/styling/resolvers/color.py +77 -0
  182. diffract/viz/styling/resolvers/numeric.py +108 -0
  183. diffract/viz/styling/sources.py +27 -0
  184. diffract/viz/styling/theme/__init__.py +29 -0
  185. diffract/viz/styling/theme/applier.py +114 -0
  186. diffract/viz/styling/theme/components.py +66 -0
  187. diffract/viz/styling/theme/presets.py +58 -0
  188. diffract/viz/styling/theme/theme.py +36 -0
  189. diffract_core-0.2.1.dist-info/METADATA +530 -0
  190. diffract_core-0.2.1.dist-info/RECORD +193 -0
  191. diffract_core-0.2.1.dist-info/WHEEL +4 -0
  192. diffract_core-0.2.1.dist-info/licenses/LICENSE +201 -0
  193. diffract_core-0.2.1.dist-info/licenses/NOTICE +2 -0
@@ -0,0 +1,82 @@
1
+ """Domain schema definitions for diffract.core.data.nn.
2
+
3
+ This module centralizes NN-domain type aliases and enumerations to keep
4
+ interfaces explicit and avoid ambiguous bare primitives like str/int in
5
+ public APIs. It defines the core domain schema including parameter types,
6
+ identifiers, and field names.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from enum import IntFlag, auto
12
+
13
+ # Domain primitives.
14
+ type ParameterUID = str
15
+ """Unique identifier of a parameter in storage."""
16
+
17
+ type ParameterIndex = int
18
+ """Zero-based positional index within a view or list-like ordering."""
19
+
20
+ type FieldName = str
21
+ """Field name for parameter-associated data stored in backends (e.g., "weights")."""
22
+
23
+ type ModelID = str
24
+ """Unique identifier of a model containing parameters."""
25
+
26
+
27
+ class ParameterType(IntFlag):
28
+ """Parameter type enumeration with extensible design.
29
+
30
+ This enumeration supports bitwise operations for flexible parameter
31
+ filtering and type combination. New parameter types can be added
32
+ dynamically through string conversion, making it extensible for
33
+ future parameter classifications.
34
+
35
+ The IntFlag base allows combining types using bitwise OR operations
36
+ and checking membership using bitwise AND operations.
37
+
38
+ Attributes:
39
+ UNKNOWN: Default type for unclassified parameters.
40
+ DENSE: Dense/fully-connected layer parameters.
41
+ """
42
+
43
+ UNKNOWN = 0
44
+ DENSE = auto()
45
+
46
+ @classmethod
47
+ def from_string(cls, type_str: str) -> ParameterType:
48
+ """Create parameter type from string representation.
49
+
50
+ Attempts to match existing parameter types first, then creates
51
+ new dynamic types for unknown strings. This allows the system
52
+ to adapt to new parameter types without code changes.
53
+
54
+ Args:
55
+ type_str: String representation of parameter type.
56
+
57
+ Returns:
58
+ Corresponding ParameterType enum value.
59
+ """
60
+ try:
61
+ return cls[type_str.upper()]
62
+ except KeyError:
63
+ # Create dynamic parameter type for new types
64
+ current_max = max(cls._value2member_map_.values())
65
+ new_value = 1 << (current_max.bit_length())
66
+ return cls._create_custom_type(type_str.upper(), new_value)
67
+
68
+ @classmethod
69
+ def _create_custom_type(cls, name: str, value: int) -> ParameterType:
70
+ """Create a custom parameter type dynamically."""
71
+ new_member = int.__new__(cls, value)
72
+ new_member._name_ = name
73
+ new_member._value_ = value
74
+
75
+ cls._member_map_[name] = new_member
76
+ cls._value2member_map_[value] = new_member
77
+
78
+ return new_member
79
+
80
+ def __repr__(self) -> str:
81
+ """Return string representation of parameter type."""
82
+ return self.name
@@ -0,0 +1,393 @@
1
+ """Parameter view for batch operations on filtered parameter collections.
2
+
3
+ This module provides the ParameterView class that extends the generic DataView
4
+ with parameter-specific filtering methods.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import re
11
+ from collections.abc import Iterator
12
+ from typing import TYPE_CHECKING
13
+
14
+ import numpy as np
15
+
16
+ from diffract.core.constants import REGEX_PREFIX, TABLE_PARAMETERS
17
+ from diffract.core.data.utils import build_matcher
18
+ from diffract.core.data.view import DataView
19
+ from diffract.core.parallel import ParallelContext, map_maybe_parallel
20
+
21
+ from .metadata import ParameterMetadata
22
+ from .proxy import ParameterDataProxy
23
+ from .schema import FieldName, ModelID, ParameterType, ParameterUID
24
+
25
+ if TYPE_CHECKING:
26
+ from .interface import IParameterProxy, IParameterRepository
27
+
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ class ParameterView(DataView[ParameterMetadata, ParameterDataProxy]):
33
+ """A numpy-like view over a subset of parameters owned by a repository.
34
+
35
+ Extends the generic DataView with parameter-specific filtering methods
36
+ such as filter_by_name, filter_by_ptype, and filter_by_model_id.
37
+ Uses SQL-based filtering via MetadataIndex for efficiency.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ *,
43
+ repository: IParameterRepository,
44
+ uids: list[ParameterUID] | None = None,
45
+ ) -> None:
46
+ super().__init__(repository=repository, uids=uids)
47
+
48
+ def __iter__(self) -> Iterator[IParameterProxy]:
49
+ """Iterate parameter proxies in view order."""
50
+ return super().__iter__()
51
+
52
+ def _sort_in_place(self) -> None:
53
+ """Sort UIDs by model_id, name, then uid for deterministic ordering."""
54
+ uids = self._ensure_uids()
55
+ uids.sort(
56
+ key=lambda uid: (
57
+ self._repository.get_proxy(uid).meta.model_id,
58
+ self._repository.get_proxy(uid).meta.name,
59
+ uid,
60
+ )
61
+ )
62
+ self._sorted = True
63
+
64
+ def filter_by_name(self, *names: str, inverse_mask: bool = False) -> ParameterView:
65
+ """Filter parameters by name.
66
+
67
+ Args:
68
+ *names: Parameter names to match. Use "re:" prefix for regex patterns.
69
+ inverse_mask: If True, return parameters NOT matching the names.
70
+
71
+ Returns:
72
+ New view with filtered parameters.
73
+ """
74
+ # Separate exact matches from regex patterns
75
+ exact_names: list[str] = []
76
+ regex_patterns: list[re.Pattern[str]] = []
77
+ for name in names:
78
+ if name.startswith(REGEX_PREFIX):
79
+ regex_patterns.append(re.compile(name.removeprefix(REGEX_PREFIX)))
80
+ else:
81
+ exact_names.append(name)
82
+
83
+ # If only exact matches and no inverse, use SQL
84
+ if exact_names and not regex_patterns and not inverse_mask:
85
+ current_uids = self._uids
86
+ if current_uids is None:
87
+ # Query directly from index
88
+ filtered = self._repository.metadata_index.query(
89
+ TABLE_PARAMETERS,
90
+ where_in={"name": exact_names},
91
+ )
92
+ else:
93
+ # Filter within current UIDs
94
+ filtered = self._repository.metadata_index.query(
95
+ TABLE_PARAMETERS,
96
+ where_in={"name": exact_names, "uid": current_uids},
97
+ )
98
+ return ParameterView(repository=self._repository, uids=filtered)
99
+
100
+ # Fall back to in-memory filtering for regex or inverse
101
+ if not self._sorted:
102
+ self._sort_in_place()
103
+
104
+ uids = self._ensure_uids()
105
+ matches = build_matcher(names)
106
+
107
+ if not inverse_mask:
108
+ filtered = [
109
+ uid
110
+ for uid in uids
111
+ if matches(self._repository.get_proxy(uid).meta.name)
112
+ ]
113
+ else:
114
+ filtered = [
115
+ uid
116
+ for uid in uids
117
+ if not matches(self._repository.get_proxy(uid).meta.name)
118
+ ]
119
+
120
+ return ParameterView(repository=self._repository, uids=filtered)
121
+
122
+ def filter_by_ptype(self, *ptypes: str | ParameterType) -> ParameterView:
123
+ """Filter parameters by parameter type.
124
+
125
+ Args:
126
+ *ptypes: Parameter types to include (strings or ParameterType values).
127
+
128
+ Returns:
129
+ New view with filtered parameters.
130
+ """
131
+ # Convert to string names for SQL query
132
+ ptype_names: list[str] = []
133
+ for ptype in ptypes:
134
+ if isinstance(ptype, ParameterType):
135
+ ptype_names.append(ptype.name)
136
+ else:
137
+ ptype_names.append(ptype.upper())
138
+
139
+ current_uids = self._uids
140
+ if current_uids is None:
141
+ # Query directly from index
142
+ filtered = self._repository.metadata_index.query(
143
+ TABLE_PARAMETERS,
144
+ where_in={"ptype": ptype_names},
145
+ )
146
+ else:
147
+ # Filter within current UIDs
148
+ filtered = self._repository.metadata_index.query(
149
+ TABLE_PARAMETERS,
150
+ where_in={"ptype": ptype_names, "uid": current_uids},
151
+ )
152
+
153
+ return ParameterView(repository=self._repository, uids=filtered)
154
+
155
+ def filter_by_model_id(
156
+ self, *model_ids: ModelID, inverse_mask: bool = False
157
+ ) -> ParameterView:
158
+ """Filter parameters by model id.
159
+
160
+ Args:
161
+ *model_ids: Model IDs to match. Use "re:" prefix for regex patterns.
162
+ inverse_mask: If True, return parameters NOT matching the model IDs.
163
+
164
+ Returns:
165
+ New view with filtered parameters.
166
+ """
167
+ # Separate exact matches from regex patterns
168
+ exact_ids: list[str] = []
169
+ regex_patterns: list[re.Pattern[str]] = []
170
+ for model_id in model_ids:
171
+ if model_id.startswith(REGEX_PREFIX):
172
+ regex_patterns.append(re.compile(model_id.removeprefix(REGEX_PREFIX)))
173
+ else:
174
+ exact_ids.append(model_id)
175
+
176
+ # If only exact matches and no inverse, use SQL
177
+ if exact_ids and not regex_patterns and not inverse_mask:
178
+ current_uids = self._uids
179
+ if current_uids is None:
180
+ # Query directly from index
181
+ filtered = self._repository.metadata_index.query(
182
+ TABLE_PARAMETERS,
183
+ where_in={"model_id": exact_ids},
184
+ )
185
+ else:
186
+ # Filter within current UIDs
187
+ filtered = self._repository.metadata_index.query(
188
+ TABLE_PARAMETERS,
189
+ where_in={"model_id": exact_ids, "uid": current_uids},
190
+ )
191
+ return ParameterView(repository=self._repository, uids=filtered)
192
+
193
+ # Fall back to in-memory filtering for regex or inverse
194
+ if not self._sorted:
195
+ self._sort_in_place()
196
+
197
+ uids = self._ensure_uids()
198
+ matches = build_matcher(model_ids)
199
+
200
+ if not inverse_mask:
201
+ filtered = [
202
+ uid
203
+ for uid in uids
204
+ if matches(self._repository.get_proxy(uid).meta.model_id)
205
+ ]
206
+ else:
207
+ filtered = [
208
+ uid
209
+ for uid in uids
210
+ if not matches(self._repository.get_proxy(uid).meta.model_id)
211
+ ]
212
+
213
+ return ParameterView(repository=self._repository, uids=filtered)
214
+
215
+ def filter_by_fields(
216
+ self,
217
+ *fields: FieldName,
218
+ inverse_mask: bool = False,
219
+ parallel: ParallelContext | None = None,
220
+ ) -> ParameterView:
221
+ """Filter parameters by the presence of one or more fields.
222
+
223
+ Args:
224
+ *fields: Field names that must be present.
225
+ inverse_mask: If True, return parameters WITHOUT the specified fields.
226
+ parallel: Optional parallel context for concurrent checks.
227
+
228
+ Returns:
229
+ New view with filtered parameters.
230
+ """
231
+ result = super().filter_by_fields(
232
+ *fields, inverse_mask=inverse_mask, parallel=parallel
233
+ )
234
+ return ParameterView(repository=self._repository, uids=result._uids)
235
+
236
+ def iter_chunks_by_read_budget(
237
+ self,
238
+ *,
239
+ budget_bytes: int | None = None,
240
+ required_fields_by_uid: dict[ParameterUID, list[FieldName]],
241
+ default_field_bytes: int = 1024 * 1024,
242
+ parallel: ParallelContext | None = None,
243
+ ) -> Iterator[ParameterView]:
244
+ """Yield sub-views chunked by approximate read budget.
245
+
246
+ Args:
247
+ budget_bytes: Maximum bytes per chunk. If None, auto-detects from
248
+ cache_manager.get_available_bytes() with 40% headroom factor.
249
+ required_fields_by_uid: Mapping of uid -> required fields to include
250
+ in the estimate.
251
+ default_field_bytes: Fallback size estimate when metadata is missing.
252
+ parallel: Optional parallel context for metadata estimation.
253
+ """
254
+ if not self._sorted:
255
+ self._sort_in_place()
256
+
257
+ uids = self._ensure_uids()
258
+ if not uids:
259
+ return
260
+
261
+ if budget_bytes is None:
262
+ budget_bytes = self._get_cache_budget_bytes()
263
+
264
+ if budget_bytes <= 0:
265
+ yield self
266
+ return
267
+
268
+ def _estimate_field_bytes(uid: ParameterUID, field: FieldName) -> int:
269
+ meta = self._repository.get_proxy(uid).get_field_metadata(field)
270
+ if not meta:
271
+ return default_field_bytes
272
+
273
+ shape = meta.get("shape")
274
+ dtype = meta.get("dtype")
275
+ if not shape or not dtype:
276
+ return default_field_bytes
277
+
278
+ try:
279
+ itemsize = int(np.dtype(dtype).itemsize)
280
+ n = 1
281
+ for x in shape:
282
+ n *= int(x)
283
+ return max(1, n * itemsize)
284
+ except Exception: # noqa: BLE001
285
+ return default_field_bytes
286
+
287
+ def _estimate_uid(uid: ParameterUID) -> int:
288
+ fields = required_fields_by_uid.get(uid, [])
289
+ if not fields:
290
+ return 0
291
+ return sum(_estimate_field_bytes(uid, f) for f in fields)
292
+
293
+ estimates = map_maybe_parallel(uids, _estimate_uid, parallel=parallel)
294
+ uid_to_est = dict(zip(uids, estimates, strict=True))
295
+
296
+ chunk: list[ParameterUID] = []
297
+ used = 0
298
+ for uid in uids:
299
+ estimated_bytes = uid_to_est[uid]
300
+ if chunk and (used + estimated_bytes > budget_bytes):
301
+ yield ParameterView(repository=self._repository, uids=list(chunk))
302
+ chunk.clear()
303
+ used = 0
304
+ chunk.append(uid)
305
+ used += estimated_bytes
306
+ if chunk:
307
+ yield ParameterView(repository=self._repository, uids=chunk)
308
+
309
+ def erase_fields(self, *fields: FieldName) -> None:
310
+ """Erase fields from all parameters in this view."""
311
+ uids = self._ensure_uids()
312
+ if not uids:
313
+ return
314
+
315
+ with self:
316
+ for uid in uids:
317
+ for field in fields:
318
+ self._repository.storage_manager.erase_field(
319
+ uid, field, table=TABLE_PARAMETERS
320
+ )
321
+ if self._repository.cache_manager is not None:
322
+ self._repository.cache_manager.erase_field(uid, field)
323
+
324
+ def erase_fields_with_regexp(self, *patterns: str) -> None:
325
+ """Erase fields matching regex patterns from all parameters in this view."""
326
+ uids = self._ensure_uids()
327
+ if not uids:
328
+ return
329
+
330
+ all_fields = set(
331
+ self._repository.storage_manager.list_fields(table=TABLE_PARAMETERS)
332
+ )
333
+
334
+ with self:
335
+ for uid in uids:
336
+ for pattern in patterns:
337
+ for field_name in all_fields:
338
+ if re.fullmatch(pattern, field_name):
339
+ self._repository.storage_manager.erase_field(
340
+ uid, field_name, table=TABLE_PARAMETERS
341
+ )
342
+ if self._repository.cache_manager is not None:
343
+ self._repository.cache_manager.erase_field(
344
+ uid, field_name
345
+ )
346
+
347
+ def clear(self, erase: bool = False) -> None:
348
+ """Clear this view and optionally erase corresponding data.
349
+
350
+ Args:
351
+ erase: If True, also erase underlying storage data.
352
+ If False, only clear membership (metadata index).
353
+ """
354
+ uids = self._ensure_uids()
355
+
356
+ if erase and self._repository.cache_manager is not None:
357
+ self._repository.cache_manager.clear()
358
+
359
+ with self._repository:
360
+ for uid in list(uids):
361
+ self._repository._proxy_cache.pop(uid, None)
362
+ self._repository.metadata_index.delete(TABLE_PARAMETERS, uid)
363
+ if erase:
364
+ self._repository.storage_manager.erase_obj(
365
+ uid, table=TABLE_PARAMETERS
366
+ )
367
+
368
+ if self._uids is not None:
369
+ self._uids.clear()
370
+ else:
371
+ self._uids = []
372
+ self._sorted = True
373
+
374
+ def _get_cache_budget_bytes(self, headroom_factor: float = 0.4) -> int:
375
+ """Get read budget based on available cache capacity.
376
+
377
+ Args:
378
+ headroom_factor: Fraction of available cache to use (default 40%).
379
+
380
+ Returns:
381
+ Budget in bytes, or fallback value if cache unavailable.
382
+ """
383
+ fallback = 256 * 1024 * 1024 # 256MB
384
+
385
+ cache = self._repository.cache_manager
386
+ if cache is None:
387
+ return fallback
388
+
389
+ available = cache.get_available_bytes()
390
+ if available is None:
391
+ return fallback
392
+
393
+ return max(1024 * 1024, int(available * headroom_factor))