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,298 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, replace
4
+ from typing import Any, Literal
5
+
6
+ import numpy as np
7
+ import plotly.graph_objects as go
8
+
9
+ from diffract.viz.data import (
10
+ DataType,
11
+ Entry,
12
+ FieldRef,
13
+ )
14
+ from diffract.viz.plots.base.axis import AxisType, SupportsAxis
15
+ from diffract.viz.plots.base.jitter import SupportsJitter
16
+ from diffract.viz.plots.base.marker import SupportsMarker
17
+ from diffract.viz.plots.base.plot import Plot
18
+ from diffract.viz.styling import (
19
+ CategoricalPropertyResolver,
20
+ NumericPropertyResolver,
21
+ ResolvedColor,
22
+ )
23
+
24
+
25
+ @dataclass(kw_only=True)
26
+ class ViolinPlot(
27
+ Plot,
28
+ SupportsJitter,
29
+ SupportsAxis("x", AxisType.CATEGORICAL),
30
+ SupportsAxis("y", AxisType.NUMERIC),
31
+ SupportsMarker,
32
+ ):
33
+ """Violin plot for a scalar or vector numeric field.
34
+
35
+ Supports grouping by categorical x-axis and color customization via marker
36
+ properties. Violin-specific options include box/meanline visibility, side
37
+ rendering, and bandwidth.
38
+
39
+ When ``y`` is a vector field (e.g., an eigenvalue spectrum per entry), the array
40
+ is automatically expanded into individual observations for the violin.
41
+ """
42
+
43
+ y: FieldRef
44
+ y_rescale_range: tuple[float, float] | None = None
45
+ y_rescale_traces_separately: bool = False
46
+
47
+ x: FieldRef
48
+
49
+ points: Literal["all", "outliers", False] = "outliers"
50
+ box_visible: bool = True
51
+ meanline_visible: bool = True
52
+ side: Literal["positive", "negative", "both"] = "positive"
53
+ bandwidth: float | None = None
54
+
55
+ def _build_traces_data(
56
+ self, entries: dict[str, Entry]
57
+ ) -> dict[str, dict[str, Any]]:
58
+ """Build trace data dict for each group (category on x-axis)."""
59
+ x_resolver = CategoricalPropertyResolver()
60
+ y_resolver = NumericPropertyResolver(self.y_rescale_range)
61
+
62
+ # The x axis is categorical by design; without an explicit
63
+ # data_type digit-like string fields (layer_id, head_id) would be
64
+ # detected as numeric and rejected.
65
+ x_ref = replace(self.x, data_type=self.x.data_type or DataType.CATEGORICAL)
66
+ x_values = x_resolver.resolve(x_ref, entries)
67
+ y_values = y_resolver.resolve(self.y, entries)
68
+
69
+ sorted_categories = self._get_sorted_categories(x_values)
70
+ category_order = self._get_effective_category_order(sorted_categories)
71
+ groups = self._group_by_category(x_values, y_values, category_order)
72
+
73
+ # 1) Resolve globally for all entries
74
+ resolved_size = self._resolve_marker_size(entries, self._theme)
75
+ resolved_opacity = self._resolve_marker_opacity(entries, self._theme)
76
+ resolved_symbol = self._resolve_marker_symbol(entries, self._theme)
77
+ resolved_color = self._resolve_marker_color(entries, self._theme)
78
+ resolved_jitter_color = self._resolve_jitter_color(entries, self._theme)
79
+
80
+ # 2) Build per-category traces
81
+ traces_data: dict[str, dict[str, Any]] = {}
82
+
83
+ for idx, category in enumerate(category_order):
84
+ trace_id = f"violin_{category}"
85
+
86
+ trace_y = groups[category]
87
+ if self.y_rescale_traces_separately:
88
+ trace_y = y_resolver._normalize(trace_y)
89
+
90
+ trace_data: dict[str, Any] = {
91
+ "category": category,
92
+ "category_idx": idx,
93
+ "jitter_x_center": float(idx),
94
+ "jitter_xaxis": "x2",
95
+ "y_values": trace_y,
96
+ }
97
+
98
+ # 3) Slice resolved values for this category and add to trace
99
+ self._add_marker_size_to_trace(
100
+ trace_data,
101
+ self._pick_category_values(x_values, y_values, category, resolved_size),
102
+ )
103
+ self._add_marker_opacity_to_trace(
104
+ trace_data,
105
+ self._pick_category_values(
106
+ x_values, y_values, category, resolved_opacity
107
+ ),
108
+ )
109
+ self._add_marker_symbol_to_trace(
110
+ trace_data,
111
+ self._pick_category_scalar(x_values, category, resolved_symbol),
112
+ )
113
+ self._add_marker_color_to_trace(
114
+ trace_data,
115
+ self._pick_category_color(x_values, y_values, category, resolved_color),
116
+ )
117
+
118
+ if resolved_jitter_color is not None:
119
+ self._add_jitter_color_to_trace(
120
+ trace_data,
121
+ self._pick_category_color(
122
+ x_values, y_values, category, resolved_jitter_color
123
+ ),
124
+ )
125
+
126
+ traces_data[trace_id] = trace_data
127
+
128
+ return traces_data
129
+
130
+ def _get_sorted_categories(self, x_values: list[Any]) -> list[str]:
131
+ """Get unique categories sorted according to x ordering."""
132
+ unique_categories = list(dict.fromkeys(x_values))
133
+ order_indices = self.x.ordering.argsort(unique_categories)
134
+ return [str(unique_categories[i]) for i in order_indices]
135
+
136
+ def _get_effective_category_order(self, sorted_categories: list[str]) -> list[str]:
137
+ """Resolve category order used for both traces and jitter centers."""
138
+ if self.x_categoryorder != "array" or self.x_categoryarray is None:
139
+ return sorted_categories
140
+
141
+ preferred = [str(category) for category in self.x_categoryarray]
142
+ preferred_existing = [
143
+ category for category in preferred if category in sorted_categories
144
+ ]
145
+ remaining = [
146
+ category
147
+ for category in sorted_categories
148
+ if category not in preferred_existing
149
+ ]
150
+ return preferred_existing + remaining
151
+
152
+ def _group_by_category(
153
+ self, x_values: list[Any], y_values: list[Any], categories: list[str]
154
+ ) -> dict[str, list[float]]:
155
+ """Group y-values by their x category.
156
+
157
+ Scalar y-values are appended as single floats.
158
+ Array y-values (``np.ndarray``) are exploded into individual floats,
159
+ so that each element of the vector becomes a separate observation
160
+ in the violin (useful for per-parameter spectra like ESD).
161
+ """
162
+ groups: dict[str, list[float]] = {cat: [] for cat in categories}
163
+
164
+ for x_val, y_val in zip(x_values, y_values, strict=False):
165
+ if y_val is None:
166
+ continue
167
+ cat = str(x_val)
168
+ if isinstance(y_val, np.ndarray):
169
+ groups[cat].extend(y_val.ravel().tolist())
170
+ else:
171
+ groups[cat].append(float(y_val))
172
+
173
+ return groups
174
+
175
+ def _build_figure(self) -> go.Figure:
176
+ """Build the plotly figure with violin traces."""
177
+ fig = go.Figure()
178
+
179
+ if self._traces_data is None:
180
+ return fig
181
+
182
+ for trace_id, trace_data in self._traces_data.items():
183
+ category = trace_data["category"]
184
+ y_values = trace_data["y_values"]
185
+
186
+ if len(y_values) == 0:
187
+ continue
188
+
189
+ fig.add_trace(
190
+ go.Violin(
191
+ name=category,
192
+ x=[category] * len(y_values),
193
+ y=y_values,
194
+ points=self.points,
195
+ box_visible=self.box_visible,
196
+ meanline_visible=self.meanline_visible,
197
+ width=0.5,
198
+ side=self.side,
199
+ bandwidth=self.bandwidth,
200
+ meta={"trace_id": trace_id},
201
+ )
202
+ )
203
+
204
+ # Set title
205
+ if self.title:
206
+ fig.update_layout(title=self.title)
207
+ else:
208
+ fig.update_layout(title=f"{self.y.field} by {self.x.field}")
209
+
210
+ categoryorder = self.x_categoryorder or "array"
211
+
212
+ if categoryorder == "array":
213
+ categoryarray = self.x_categoryarray or [
214
+ trace_data["category"]
215
+ for trace_data in sorted(
216
+ self._traces_data.values(),
217
+ key=lambda data: data["category_idx"],
218
+ )
219
+ ]
220
+ else:
221
+ categoryarray = None
222
+
223
+ half_span = max(0.5, abs(self.jitter_offset) + self.jitter_width + 0.05)
224
+ fig.update_layout(
225
+ xaxis=dict(
226
+ categoryorder=categoryorder,
227
+ categoryarray=categoryarray,
228
+ range=[-half_span, len(self._traces_data) - 1 + 0.5 * half_span],
229
+ )
230
+ )
231
+
232
+ return fig
233
+
234
+ @staticmethod
235
+ def _pick_category_values(
236
+ x_values: list[Any],
237
+ y_values: list[Any],
238
+ category: str,
239
+ resolved: float | list[float] | None,
240
+ ) -> float | list[float] | None:
241
+ """Slice a resolved numeric value for a specific category.
242
+
243
+ If resolved is scalar or None — return as-is.
244
+ If resolved is a per-entry list — filter to entries matching this category.
245
+ """
246
+ if resolved is None or not isinstance(resolved, list):
247
+ return resolved
248
+ return [
249
+ val
250
+ for x_val, y_val, val in zip(x_values, y_values, resolved, strict=False)
251
+ if str(x_val) == category and y_val is not None
252
+ ]
253
+
254
+ @staticmethod
255
+ def _pick_category_scalar(
256
+ x_values: list[Any],
257
+ category: str,
258
+ resolved: str | list[str] | None,
259
+ ) -> str | None:
260
+ """Pick a single categorical value for a category (first match).
261
+
262
+ If resolved is scalar or None — return as-is.
263
+ If resolved is a per-entry list — return the first matching value.
264
+ """
265
+ if resolved is None or not isinstance(resolved, list):
266
+ return resolved
267
+ for x_val, val in zip(x_values, resolved, strict=False):
268
+ if str(x_val) == category:
269
+ return val
270
+ return None
271
+
272
+ @staticmethod
273
+ def _pick_category_color(
274
+ x_values: list[Any],
275
+ y_values: list[Any],
276
+ category: str,
277
+ resolved_color: ResolvedColor,
278
+ ) -> ResolvedColor:
279
+ """Build a per-category ResolvedColor from a global ResolvedColor."""
280
+ if resolved_color.values is not None:
281
+ filtered = [
282
+ c_val
283
+ for x_val, y_val, c_val in zip(
284
+ x_values, y_values, resolved_color.values, strict=False
285
+ )
286
+ if str(x_val) == category and y_val is not None
287
+ ]
288
+ return ResolvedColor(values=filtered)
289
+
290
+ if resolved_color.color is not None:
291
+ if isinstance(resolved_color.color, list):
292
+ for x_val, color in zip(x_values, resolved_color.color, strict=False):
293
+ if str(x_val) == category:
294
+ return ResolvedColor(color=color)
295
+ return ResolvedColor()
296
+ return resolved_color
297
+
298
+ return resolved_color