power-grid-model 1.10.17__py3-none-win_amd64.whl → 1.12.119__py3-none-win_amd64.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.

Potentially problematic release.


This version of power-grid-model might be problematic. Click here for more details.

Files changed (67) hide show
  1. power_grid_model/__init__.py +54 -29
  2. power_grid_model/_core/__init__.py +3 -3
  3. power_grid_model/_core/buffer_handling.py +507 -478
  4. power_grid_model/_core/data_handling.py +195 -141
  5. power_grid_model/_core/data_types.py +142 -0
  6. power_grid_model/_core/dataset_definitions.py +109 -109
  7. power_grid_model/_core/enum.py +226 -0
  8. power_grid_model/_core/error_handling.py +215 -198
  9. power_grid_model/_core/errors.py +134 -0
  10. power_grid_model/_core/index_integer.py +17 -17
  11. power_grid_model/_core/options.py +71 -69
  12. power_grid_model/_core/power_grid_core.py +577 -562
  13. power_grid_model/_core/power_grid_dataset.py +545 -490
  14. power_grid_model/_core/power_grid_meta.py +262 -244
  15. power_grid_model/_core/power_grid_model.py +1025 -687
  16. power_grid_model/_core/power_grid_model_c/__init__.py +3 -0
  17. power_grid_model/_core/power_grid_model_c/bin/power_grid_model_c.dll +0 -0
  18. power_grid_model/_core/power_grid_model_c/get_pgm_dll_path.py +63 -0
  19. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/basics.h +251 -0
  20. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/buffer.h +108 -0
  21. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/dataset.h +332 -0
  22. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/dataset_definitions.h +1060 -0
  23. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/handle.h +111 -0
  24. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/meta_data.h +189 -0
  25. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/model.h +130 -0
  26. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/options.h +142 -0
  27. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c/serialization.h +118 -0
  28. power_grid_model/_core/power_grid_model_c/include/power_grid_model_c.h +36 -0
  29. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/basics.hpp +65 -0
  30. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/buffer.hpp +61 -0
  31. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/dataset.hpp +224 -0
  32. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/handle.hpp +108 -0
  33. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/meta_data.hpp +84 -0
  34. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/model.hpp +63 -0
  35. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/options.hpp +52 -0
  36. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/serialization.hpp +124 -0
  37. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp/utils.hpp +81 -0
  38. power_grid_model/_core/power_grid_model_c/include/power_grid_model_cpp.hpp +19 -0
  39. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelConfig.cmake +37 -0
  40. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelConfigVersion.cmake +65 -0
  41. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelTargets-release.cmake +19 -0
  42. power_grid_model/_core/power_grid_model_c/lib/cmake/power_grid_model/power_grid_modelTargets.cmake +144 -0
  43. power_grid_model/_core/power_grid_model_c/lib/power_grid_model_c.lib +0 -0
  44. power_grid_model/_core/power_grid_model_c/share/LICENSE +292 -0
  45. power_grid_model/_core/power_grid_model_c/share/README.md +15 -0
  46. power_grid_model/_core/serialization.py +319 -317
  47. power_grid_model/_core/typing.py +20 -0
  48. power_grid_model/{_utils.py → _core/utils.py} +798 -783
  49. power_grid_model/data_types.py +321 -319
  50. power_grid_model/enum.py +27 -214
  51. power_grid_model/errors.py +37 -119
  52. power_grid_model/typing.py +43 -48
  53. power_grid_model/utils.py +529 -400
  54. power_grid_model/validation/__init__.py +25 -10
  55. power_grid_model/validation/{rules.py → _rules.py} +1167 -962
  56. power_grid_model/validation/{validation.py → _validation.py} +1172 -1015
  57. power_grid_model/validation/assertions.py +93 -92
  58. power_grid_model/validation/errors.py +602 -524
  59. power_grid_model/validation/utils.py +313 -318
  60. {power_grid_model-1.10.17.dist-info → power_grid_model-1.12.119.dist-info}/METADATA +162 -165
  61. power_grid_model-1.12.119.dist-info/RECORD +65 -0
  62. {power_grid_model-1.10.17.dist-info → power_grid_model-1.12.119.dist-info}/WHEEL +1 -1
  63. power_grid_model-1.12.119.dist-info/entry_points.txt +3 -0
  64. power_grid_model/_core/_power_grid_core.dll +0 -0
  65. power_grid_model-1.10.17.dist-info/RECORD +0 -32
  66. power_grid_model-1.10.17.dist-info/top_level.txt +0 -1
  67. {power_grid_model-1.10.17.dist-info → power_grid_model-1.12.119.dist-info/licenses}/LICENSE +0 -0
@@ -1,478 +1,507 @@
1
- # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
- #
3
- # SPDX-License-Identifier: MPL-2.0
4
-
5
- """
6
- Power grid model buffer handler
7
- """
8
-
9
- from dataclasses import dataclass
10
- from typing import cast
11
-
12
- import numpy as np
13
-
14
- from power_grid_model._core.error_handling import VALIDATOR_MSG
15
- from power_grid_model._core.index_integer import IdxC, IdxNp
16
- from power_grid_model._core.power_grid_core import IdxPtr, VoidPtr
17
- from power_grid_model._core.power_grid_meta import ComponentMetaData
18
- from power_grid_model._utils import (
19
- _extract_data_from_component_data,
20
- _extract_indptr,
21
- check_indptr_consistency,
22
- is_columnar,
23
- is_sparse,
24
- )
25
- from power_grid_model.data_types import (
26
- AttributeType,
27
- ComponentData,
28
- DenseBatchData,
29
- IndexPointer,
30
- SingleComponentData,
31
- SparseBatchArray,
32
- SparseBatchData,
33
- )
34
-
35
-
36
- @dataclass
37
- class BufferProperties:
38
- """
39
- Helper class to collect info on the dataset.
40
- """
41
-
42
- is_sparse: bool
43
- is_batch: bool
44
- batch_size: int
45
- n_elements_per_scenario: int
46
- n_total_elements: int
47
- columns: list[AttributeType] | None
48
-
49
-
50
- # prepared attribute data for c api
51
- @dataclass
52
- class CAttributeBuffer:
53
- """
54
- Buffer for a single attribute.
55
- """
56
-
57
- data: VoidPtr # type: ignore
58
-
59
-
60
- # prepared component data for c api
61
- @dataclass
62
- class CBuffer:
63
- """
64
- Buffer for a single component.
65
- """
66
-
67
- data: VoidPtr | None
68
- indptr: IdxPtr | None # type: ignore
69
- n_elements_per_scenario: int
70
- batch_size: int
71
- total_elements: int
72
- attribute_data: dict[AttributeType, CAttributeBuffer]
73
-
74
-
75
- def _get_raw_data_view(data: np.ndarray, dtype: np.dtype) -> VoidPtr:
76
- """
77
- Get a raw view on the data.
78
-
79
- Args:
80
- data: the data.
81
- dtype: the dtype the raw buffer should obey.
82
-
83
- Returns:
84
- a raw view on the data set.
85
- """
86
- if data.dtype != dtype:
87
- raise ValueError(f"Data type does not match schema. {VALIDATOR_MSG}")
88
- return np.ascontiguousarray(data, dtype=dtype).ctypes.data_as(VoidPtr)
89
-
90
-
91
- def _get_raw_component_data_view(
92
- data: np.ndarray | dict[AttributeType, np.ndarray], schema: ComponentMetaData
93
- ) -> VoidPtr | None:
94
- """
95
- Get a raw view on the data.
96
-
97
- Args:
98
- data: the data.
99
- schema: the schema the raw buffer should obey.
100
-
101
- Returns:
102
- a raw view on the data set.
103
- """
104
- if isinstance(data, np.ndarray):
105
- return _get_raw_data_view(data, dtype=schema.dtype)
106
- return None
107
-
108
-
109
- def _get_raw_attribute_data_view(data: np.ndarray, schema: ComponentMetaData, attribute: AttributeType) -> VoidPtr:
110
- """
111
- Get a raw view on the data.
112
-
113
- Args:
114
- data: the data.
115
- schema: the schema the raw buffer should obey.
116
-
117
- Returns:
118
- a raw view on the data set.
119
- """
120
- if schema.dtype[attribute].shape == (3,) and data.shape[-1] != 3:
121
- raise ValueError("Given data has a different schema than supported.")
122
- return _get_raw_data_view(data, dtype=schema.dtype[attribute].base)
123
-
124
-
125
- def _get_indptr_view(indptr: np.ndarray) -> IdxPtr: # type: ignore[valid-type]
126
- """
127
- Get a raw view on the index pointer.
128
-
129
- Args:
130
- indptr: the index pointer.
131
-
132
- Returns:
133
- a raw view on the index pointer.
134
- """
135
- return np.ascontiguousarray(indptr, dtype=IdxNp).ctypes.data_as(IdxPtr)
136
-
137
-
138
- def _get_dense_buffer_properties(
139
- data: ComponentData,
140
- schema: ComponentMetaData,
141
- is_batch: bool | None,
142
- batch_size: int | None,
143
- ) -> BufferProperties:
144
- """
145
- Extract the properties of the uniform batch dataset component.
146
-
147
- Args:
148
- data (ComponentData): the dataset component.
149
- schema (ComponentMetaData): the dataset type.
150
- is_batch (bool | None): whether the data is a batch dataset.
151
- batch_size (int | None): the batch size.
152
-
153
- Raises:
154
- KeyError: if the dataset component is not sparse.
155
- ValueError: if the dataset component contains conflicting or bad data.
156
-
157
- Returns:
158
- the properties of the dataset component.
159
- """
160
- if is_batch is not None and batch_size is not None and batch_size != 1 and not is_batch:
161
- raise ValueError(f"Inconsistent 'is batch' and 'batch size'. {VALIDATOR_MSG}")
162
-
163
- is_sparse_property = False
164
-
165
- sub_data = _extract_data_from_component_data(data)
166
- if not is_columnar(data):
167
- actual_ndim = sub_data.ndim
168
- shape: tuple[int] = sub_data.shape
169
- columns = None
170
- else:
171
- if not sub_data:
172
- raise ValueError(f"Empty columnar buffer is ambiguous. {VALIDATOR_MSG}")
173
- attribute, attribute_data = next(iter(sub_data.items()))
174
- actual_ndim = attribute_data.ndim - schema.dtype[attribute].ndim
175
- shape = attribute_data.shape[:actual_ndim]
176
- columns = list(sub_data)
177
-
178
- for attribute, attribute_data in sub_data.items():
179
- if (
180
- attribute_data.ndim != actual_ndim + schema.dtype[attribute].ndim
181
- or attribute_data.shape[:actual_ndim] != shape
182
- ):
183
- raise ValueError(f"Data buffers must be consistent. {VALIDATOR_MSG}")
184
-
185
- if actual_ndim not in (1, 2):
186
- raise ValueError(f"Array can only be 1D or 2D. {VALIDATOR_MSG}")
187
-
188
- actual_is_batch = actual_ndim == 2
189
- actual_batch_size = shape[0] if actual_is_batch else 1
190
- n_elements_per_scenario = shape[-1]
191
- n_total_elements = actual_batch_size * n_elements_per_scenario
192
-
193
- if is_batch is not None and is_batch != actual_is_batch:
194
- raise ValueError(f"Provided 'is batch' is incorrect for the provided data. {VALIDATOR_MSG}")
195
- if batch_size is not None and batch_size != actual_batch_size:
196
- raise ValueError(f"Provided 'batch size' is incorrect for the provided data. {VALIDATOR_MSG}")
197
-
198
- return BufferProperties(
199
- is_sparse=is_sparse_property,
200
- is_batch=actual_is_batch,
201
- batch_size=actual_batch_size,
202
- n_elements_per_scenario=n_elements_per_scenario,
203
- n_total_elements=n_total_elements,
204
- columns=columns,
205
- )
206
-
207
-
208
- def _get_sparse_buffer_properties(
209
- data: ComponentData,
210
- schema: ComponentMetaData,
211
- batch_size: int | None,
212
- ) -> BufferProperties:
213
- """
214
- Extract the properties of the sparse batch dataset component.
215
-
216
- Args:
217
- data (ComponentData): the sparse dataset component.
218
- schema (ComponentMetaData | None): the dataset type.
219
- batch_size (int | None): the batch size.
220
-
221
- Raises:
222
- KeyError: if the dataset component is not sparse.
223
- ValueError: if the dataset component contains conflicting or bad data.
224
-
225
- Returns:
226
- the properties of the dataset component.
227
- """
228
- is_sparse_property = True
229
-
230
- contents = _extract_data_from_component_data(data)
231
- indptr = _extract_indptr(data)
232
-
233
- ndim = 1
234
- columns: list[AttributeType] | None = None
235
- if not is_columnar(data):
236
- shape: tuple[int, ...] = contents.shape
237
- else:
238
- if not contents:
239
- raise ValueError(f"Empty columnar buffer is ambiguous. {VALIDATOR_MSG}")
240
- attribute_data = next(iter(contents.values()))
241
- shape = attribute_data.shape[:ndim]
242
- columns = list(contents)
243
- for attribute, attribute_data in contents.items():
244
- if attribute_data.ndim != ndim + schema.dtype[attribute].ndim or attribute_data.shape[:ndim] != shape:
245
- raise ValueError(f"Data buffers must be consistent. {VALIDATOR_MSG}")
246
-
247
- contents_size = shape[0]
248
- check_indptr_consistency(indptr, batch_size, contents_size)
249
-
250
- is_batch = True
251
- n_elements_per_scenario = -1
252
- n_total_elements = contents_size
253
-
254
- return BufferProperties(
255
- is_sparse=is_sparse_property,
256
- is_batch=is_batch,
257
- batch_size=indptr.size - 1,
258
- n_elements_per_scenario=n_elements_per_scenario,
259
- n_total_elements=n_total_elements,
260
- columns=columns,
261
- )
262
-
263
-
264
- def get_buffer_properties(
265
- data: ComponentData,
266
- schema: ComponentMetaData,
267
- is_batch: bool | None = None,
268
- batch_size: int | None = None,
269
- ) -> BufferProperties:
270
- """
271
- Extract the properties of the dataset component
272
-
273
- Args:
274
- data (ComponentData): the dataset component.
275
- schema (ComponentMetaData | None): the dataset type [optional if data is not columnar]
276
- is_batch (bool | None): whether the data is a batch dataset. [optional]
277
- batch_size (int | None): the batch size. [optional]
278
-
279
- Raises:
280
- ValueError: if the dataset component contains conflicting or bad data.
281
-
282
- Returns:
283
- the properties of the dataset component.
284
- """
285
- if not is_sparse(data):
286
- return _get_dense_buffer_properties(data=data, schema=schema, is_batch=is_batch, batch_size=batch_size)
287
-
288
- if is_batch is not None and not is_batch:
289
- raise ValueError("Sparse data must be batch data")
290
-
291
- return _get_sparse_buffer_properties(data=cast(SparseBatchArray, data), schema=schema, batch_size=batch_size)
292
-
293
-
294
- def _get_attribute_buffer_views(
295
- data: np.ndarray | dict[AttributeType, np.ndarray], schema: ComponentMetaData
296
- ) -> dict[AttributeType, CAttributeBuffer]:
297
- """
298
- Get C API compatible views on attribute buffers.
299
-
300
- Args:
301
- data (dict[AttributeType, np.ndarray]): the data.
302
- schema (ComponentMetaData): the schema that the data should obey.
303
-
304
- Returns:
305
- dict[AttributeType, CAttributeBuffer]: the C API attribute buffer view per attribute.
306
- """
307
- if isinstance(data, np.ndarray):
308
- return {}
309
-
310
- return {
311
- attribute: CAttributeBuffer(
312
- data=_get_raw_attribute_data_view(data=attribute_data, schema=schema, attribute=attribute)
313
- )
314
- for attribute, attribute_data in data.items()
315
- }
316
-
317
-
318
- def _get_uniform_buffer_view(
319
- data: DenseBatchData,
320
- schema: ComponentMetaData,
321
- is_batch: bool | None,
322
- batch_size: int | None,
323
- ) -> CBuffer:
324
- """
325
- Get a C API compatible view on a uniform buffer.
326
-
327
- Args:
328
- data: the data.
329
- schema: the schema that the data should obey.
330
- is_batch (bool | None): whether the data is a batch dataset.
331
- batch_size (int | None): the batch size.
332
-
333
- Returns:
334
- the C API buffer view.
335
- """
336
- properties = _get_dense_buffer_properties(data, schema=schema, is_batch=is_batch, batch_size=batch_size)
337
-
338
- return CBuffer(
339
- data=_get_raw_component_data_view(data=data, schema=schema),
340
- indptr=IdxPtr(),
341
- n_elements_per_scenario=properties.n_elements_per_scenario,
342
- batch_size=properties.batch_size,
343
- total_elements=properties.n_total_elements,
344
- attribute_data=_get_attribute_buffer_views(data=data, schema=schema),
345
- )
346
-
347
-
348
- def _get_sparse_buffer_view(
349
- data: SparseBatchArray,
350
- schema: ComponentMetaData,
351
- batch_size: int | None,
352
- ) -> CBuffer:
353
- """
354
- Get a C API compatible view on a sparse buffer.
355
-
356
- Args:
357
- data: the data.
358
- schema: the schema that the data should obey.
359
- batch_size (int | None): the batch size.
360
-
361
- Returns:
362
- the C API buffer view.
363
- """
364
- contents = data["data"]
365
- indptr = data["indptr"]
366
-
367
- properties = _get_sparse_buffer_properties(data, schema=schema, batch_size=batch_size)
368
-
369
- return CBuffer(
370
- data=_get_raw_component_data_view(data=contents, schema=schema),
371
- indptr=_get_indptr_view(indptr),
372
- n_elements_per_scenario=properties.n_elements_per_scenario,
373
- batch_size=properties.batch_size,
374
- total_elements=properties.n_total_elements,
375
- attribute_data=_get_attribute_buffer_views(data=contents, schema=schema),
376
- )
377
-
378
-
379
- def get_buffer_view(
380
- data: ComponentData,
381
- schema: ComponentMetaData,
382
- is_batch: bool | None = None,
383
- batch_size: int | None = None,
384
- ) -> CBuffer:
385
- """
386
- Get a C API compatible view on a buffer.
387
-
388
- Args:
389
- data: the data.
390
- schema: the schema that the data should obey.
391
- is_batch (bool | None): whether the data is a batch dataset. [optional]
392
- batch_size (int | None): the batch size. [optional]
393
-
394
- Returns:
395
- the C API buffer view.
396
- """
397
- if not is_sparse(data):
398
- return _get_uniform_buffer_view(cast(DenseBatchData, data), schema, is_batch, batch_size)
399
-
400
- if is_batch is not None and not is_batch:
401
- raise ValueError("Sparse data must be batch data")
402
-
403
- return _get_sparse_buffer_view(cast(SparseBatchArray, data), schema, batch_size)
404
-
405
-
406
- def create_buffer(properties: BufferProperties, schema: ComponentMetaData) -> ComponentData:
407
- """
408
- Create a buffer with the provided properties and type.
409
-
410
- Args:
411
- properties: the desired buffer properties.
412
- schema: the data type of the buffer.
413
-
414
- Raises:
415
- ValueError: if the buffer properties are not consistent.
416
-
417
- Returns:
418
- np.ndarray | dict[[str, np.ndarray]: a buffer with the correct properties.
419
- """
420
- if properties.is_sparse:
421
- return _create_sparse_buffer(properties=properties, schema=schema)
422
-
423
- return _create_uniform_buffer(properties=properties, schema=schema)
424
-
425
-
426
- def _create_uniform_buffer(properties: BufferProperties, schema: ComponentMetaData) -> DenseBatchData:
427
- """
428
- Create a uniform buffer with the provided properties and type.
429
-
430
- Args:
431
- properties: the desired buffer properties.
432
- schema: the data type of the buffer.
433
-
434
- Raises:
435
- ValueError: if the buffer properties are not uniform.
436
-
437
- Returns:
438
- A uniform buffer with the correct properties.
439
- """
440
- if properties.is_sparse:
441
- raise ValueError(f"A uniform buffer cannot be sparse. {VALIDATOR_MSG}")
442
-
443
- shape: int | tuple[int, int] = (
444
- (properties.batch_size, properties.n_elements_per_scenario)
445
- if properties.is_batch
446
- else properties.n_elements_per_scenario
447
- )
448
- return _create_contents_buffer(shape=shape, dtype=schema.dtype, columns=properties.columns)
449
-
450
-
451
- def _create_sparse_buffer(properties: BufferProperties, schema: ComponentMetaData) -> SparseBatchData:
452
- """
453
- Create a sparse buffer with the provided properties and type.
454
-
455
- Args:
456
- properties: the desired buffer properties.
457
- schema: the data type of the buffer.
458
-
459
- Raises:
460
- ValueError: if the buffer properties are not sparse.
461
-
462
- Returns:
463
- A sparse buffer with the correct properties.
464
- """
465
- data: SingleComponentData = _create_contents_buffer(
466
- shape=properties.n_total_elements,
467
- dtype=schema.dtype,
468
- columns=properties.columns,
469
- )
470
- indptr: IndexPointer = np.array([0] * properties.batch_size + [properties.n_total_elements], dtype=IdxC)
471
- return cast(SparseBatchData, {"data": data, "indptr": indptr})
472
-
473
-
474
- def _create_contents_buffer(shape, dtype, columns: list[AttributeType] | None) -> SingleComponentData | DenseBatchData:
475
- if columns is None:
476
- return np.empty(shape=shape, dtype=dtype)
477
-
478
- return {attribute: np.empty(shape=shape, dtype=dtype[attribute]) for attribute in columns}
1
+ # SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+
5
+ """
6
+ Power grid model buffer handler
7
+ """
8
+
9
+ from dataclasses import dataclass
10
+ from typing import cast, overload
11
+
12
+ import numpy as np
13
+
14
+ from power_grid_model._core.data_types import (
15
+ AttributeType,
16
+ ComponentData,
17
+ DenseBatchArray,
18
+ DenseBatchColumnarData,
19
+ DenseBatchData,
20
+ IndexPointer,
21
+ SingleArray,
22
+ SingleColumnarData,
23
+ SingleComponentData,
24
+ SparseBatchArray,
25
+ SparseBatchData,
26
+ )
27
+ from power_grid_model._core.error_handling import VALIDATOR_MSG
28
+ from power_grid_model._core.index_integer import IdxC, IdxNp
29
+ from power_grid_model._core.power_grid_core import IdxPtr, VoidPtr
30
+ from power_grid_model._core.power_grid_meta import ComponentMetaData
31
+ from power_grid_model._core.utils import (
32
+ _extract_data_from_component_data,
33
+ _extract_indptr,
34
+ check_indptr_consistency,
35
+ is_columnar,
36
+ is_sparse,
37
+ )
38
+
39
+
40
+ @dataclass
41
+ class BufferProperties:
42
+ """
43
+ Helper class to collect info on the dataset.
44
+ """
45
+
46
+ is_sparse: bool
47
+ is_batch: bool
48
+ batch_size: int
49
+ n_elements_per_scenario: int
50
+ n_total_elements: int
51
+ columns: list[AttributeType] | None
52
+
53
+
54
+ # prepared attribute data for c api
55
+ @dataclass
56
+ class CAttributeBuffer:
57
+ """
58
+ Buffer for a single attribute.
59
+ """
60
+
61
+ data: VoidPtr # type: ignore
62
+
63
+
64
+ # prepared component data for c api
65
+ @dataclass
66
+ class CBuffer:
67
+ """
68
+ Buffer for a single component.
69
+ """
70
+
71
+ data: VoidPtr | None
72
+ indptr: IdxPtr | None # type: ignore
73
+ n_elements_per_scenario: int
74
+ batch_size: int
75
+ total_elements: int
76
+ attribute_data: dict[AttributeType, CAttributeBuffer]
77
+
78
+
79
+ def _get_raw_data_view(data: np.ndarray, dtype: np.dtype) -> VoidPtr:
80
+ """
81
+ Get a raw view on the data.
82
+
83
+ Args:
84
+ data: the data.
85
+ dtype: the dtype the raw buffer should obey.
86
+
87
+ Returns:
88
+ a raw view on the data set.
89
+ """
90
+ if data.dtype != dtype:
91
+ raise ValueError(f"Data type does not match schema. {VALIDATOR_MSG}")
92
+ return np.ascontiguousarray(data, dtype=dtype).ctypes.data_as(VoidPtr)
93
+
94
+
95
+ @overload
96
+ def _get_raw_component_data_view(data: np.ndarray, schema: ComponentMetaData) -> VoidPtr: ...
97
+ @overload
98
+ def _get_raw_component_data_view(data: dict[AttributeType, np.ndarray], schema: ComponentMetaData) -> None: ...
99
+ def _get_raw_component_data_view(
100
+ data: np.ndarray | dict[AttributeType, np.ndarray], schema: ComponentMetaData
101
+ ) -> VoidPtr | None:
102
+ """
103
+ Get a raw view on the data.
104
+
105
+ Args:
106
+ data: the data.
107
+ schema: the schema the raw buffer should obey.
108
+
109
+ Returns:
110
+ a raw view on the data set.
111
+ """
112
+ if isinstance(data, np.ndarray):
113
+ return _get_raw_data_view(data, dtype=schema.dtype)
114
+ return None
115
+
116
+
117
+ def _get_raw_attribute_data_view(data: np.ndarray, schema: ComponentMetaData, attribute: AttributeType) -> VoidPtr:
118
+ """
119
+ Get a raw view on the data.
120
+
121
+ Args:
122
+ data: the data.
123
+ schema: the schema the raw buffer should obey.
124
+
125
+ Returns:
126
+ a raw view on the data set.
127
+ """
128
+ dense_batch_ndim = 2
129
+
130
+ attr_schema = schema.dtype[attribute]
131
+ attr_shape_start = data.ndim - attr_schema.ndim
132
+ dataset_shape = data.shape[:attr_shape_start]
133
+ attr_shape = data.shape[attr_shape_start:]
134
+ if len(dataset_shape) <= dense_batch_ndim and attr_shape == attr_schema.shape:
135
+ return _get_raw_data_view(data, dtype=schema.dtype[attribute].base)
136
+ raise ValueError("Given data has a different schema than supported.")
137
+
138
+
139
+ def _get_indptr_view(indptr: np.ndarray) -> IdxPtr: # type: ignore[valid-type]
140
+ """
141
+ Get a raw view on the index pointer.
142
+
143
+ Args:
144
+ indptr: the index pointer.
145
+
146
+ Returns:
147
+ a raw view on the index pointer.
148
+ """
149
+ return np.ascontiguousarray(indptr, dtype=IdxNp).ctypes.data_as(IdxPtr)
150
+
151
+
152
+ def _get_dense_buffer_properties(
153
+ data: ComponentData,
154
+ schema: ComponentMetaData,
155
+ is_batch: bool | None,
156
+ batch_size: int | None,
157
+ ) -> BufferProperties:
158
+ """
159
+ Extract the properties of the uniform batch dataset component.
160
+
161
+ Args:
162
+ data (ComponentData): the dataset component.
163
+ schema (ComponentMetaData): the dataset type.
164
+ is_batch (bool | None): whether the data is a batch dataset.
165
+ batch_size (int | None): the batch size.
166
+
167
+ Raises:
168
+ KeyError: if the dataset component is not sparse.
169
+ ValueError: if the dataset component contains conflicting or bad data.
170
+
171
+ Returns:
172
+ the properties of the dataset component.
173
+ """
174
+ if is_batch is not None and batch_size is not None and batch_size != 1 and not is_batch:
175
+ raise ValueError(f"Inconsistent 'is batch' and 'batch size'. {VALIDATOR_MSG}")
176
+
177
+ is_sparse_property = False
178
+
179
+ sub_data = _extract_data_from_component_data(data)
180
+ if not is_columnar(data):
181
+ actual_ndim = sub_data.ndim
182
+ shape: tuple[int] = sub_data.shape
183
+ columns = None
184
+ else:
185
+ if not sub_data:
186
+ raise ValueError(f"Empty columnar buffer is ambiguous. {VALIDATOR_MSG}")
187
+ attribute, attribute_data = next(iter(sub_data.items()))
188
+ actual_ndim = attribute_data.ndim - schema.dtype[attribute].ndim
189
+ shape = attribute_data.shape[:actual_ndim]
190
+ columns = list(sub_data)
191
+
192
+ for attribute, attribute_data in sub_data.items():
193
+ if (
194
+ attribute_data.ndim != actual_ndim + schema.dtype[attribute].ndim
195
+ or attribute_data.shape[:actual_ndim] != shape
196
+ ):
197
+ raise ValueError(f"Data buffers must be consistent. {VALIDATOR_MSG}")
198
+
199
+ if actual_ndim not in (1, 2):
200
+ raise ValueError(f"Array can only be 1D or 2D. {VALIDATOR_MSG}")
201
+
202
+ single_dataset_ndim = 1
203
+ batch_dataset_ndim = 2
204
+
205
+ actual_is_batch = actual_ndim == batch_dataset_ndim
206
+ actual_batch_size = shape[0] if actual_is_batch else single_dataset_ndim
207
+ n_elements_per_scenario = shape[-1]
208
+ n_total_elements = actual_batch_size * n_elements_per_scenario
209
+
210
+ if is_batch is not None and is_batch != actual_is_batch:
211
+ raise ValueError(
212
+ f"Incorrect/inconsistent data provided: {'batch' if actual_is_batch else 'single'} "
213
+ f"data provided but {'batch' if is_batch else 'single'} data expected. {VALIDATOR_MSG}"
214
+ )
215
+ if batch_size is not None and batch_size != actual_batch_size:
216
+ raise ValueError(
217
+ f"Incorrect/inconsistent batch size provided: {actual_batch_size} scenarios provided "
218
+ f"but {batch_size} scenarios expected. {VALIDATOR_MSG}"
219
+ )
220
+
221
+ return BufferProperties(
222
+ is_sparse=is_sparse_property,
223
+ is_batch=actual_is_batch,
224
+ batch_size=actual_batch_size,
225
+ n_elements_per_scenario=n_elements_per_scenario,
226
+ n_total_elements=n_total_elements,
227
+ columns=columns,
228
+ )
229
+
230
+
231
+ def _get_sparse_buffer_properties(
232
+ data: ComponentData,
233
+ schema: ComponentMetaData,
234
+ batch_size: int | None,
235
+ ) -> BufferProperties:
236
+ """
237
+ Extract the properties of the sparse batch dataset component.
238
+
239
+ Args:
240
+ data (ComponentData): the sparse dataset component.
241
+ schema (ComponentMetaData | None): the dataset type.
242
+ batch_size (int | None): the batch size.
243
+
244
+ Raises:
245
+ KeyError: if the dataset component is not sparse.
246
+ ValueError: if the dataset component contains conflicting or bad data.
247
+
248
+ Returns:
249
+ the properties of the dataset component.
250
+ """
251
+ is_sparse_property = True
252
+
253
+ contents = _extract_data_from_component_data(data)
254
+ indptr = _extract_indptr(data)
255
+
256
+ ndim = 1
257
+ columns: list[AttributeType] | None = None
258
+ if not is_columnar(data):
259
+ shape: tuple[int, ...] = contents.shape
260
+ else:
261
+ if not contents:
262
+ raise ValueError(f"Empty columnar buffer is ambiguous. {VALIDATOR_MSG}")
263
+ attribute_data = next(iter(contents.values()))
264
+ shape = attribute_data.shape[:ndim]
265
+ columns = list(contents)
266
+ for attribute, attribute_data in contents.items():
267
+ if attribute_data.ndim != ndim + schema.dtype[attribute].ndim or attribute_data.shape[:ndim] != shape:
268
+ raise ValueError(f"Data buffers must be consistent. {VALIDATOR_MSG}")
269
+
270
+ contents_size = shape[0]
271
+ check_indptr_consistency(indptr, batch_size, contents_size)
272
+
273
+ is_batch = True
274
+ n_elements_per_scenario = -1
275
+ n_total_elements = contents_size
276
+
277
+ return BufferProperties(
278
+ is_sparse=is_sparse_property,
279
+ is_batch=is_batch,
280
+ batch_size=indptr.size - 1,
281
+ n_elements_per_scenario=n_elements_per_scenario,
282
+ n_total_elements=n_total_elements,
283
+ columns=columns,
284
+ )
285
+
286
+
287
+ def get_buffer_properties(
288
+ data: ComponentData,
289
+ schema: ComponentMetaData,
290
+ is_batch: bool | None = None,
291
+ batch_size: int | None = None,
292
+ ) -> BufferProperties:
293
+ """
294
+ Extract the properties of the dataset component
295
+
296
+ Args:
297
+ data (ComponentData): the dataset component.
298
+ schema (ComponentMetaData | None): the dataset type [optional if data is not columnar]
299
+ is_batch (bool | None): whether the data is a batch dataset. [optional]
300
+ batch_size (int | None): the batch size. [optional]
301
+
302
+ Raises:
303
+ ValueError: if the dataset component contains conflicting or bad data.
304
+
305
+ Returns:
306
+ the properties of the dataset component.
307
+ """
308
+ if not is_sparse(data):
309
+ return _get_dense_buffer_properties(data=data, schema=schema, is_batch=is_batch, batch_size=batch_size)
310
+
311
+ if is_batch is not None and not is_batch:
312
+ raise ValueError("Sparse data must be batch data")
313
+
314
+ return _get_sparse_buffer_properties(data=cast(SparseBatchArray, data), schema=schema, batch_size=batch_size)
315
+
316
+
317
+ def _get_attribute_buffer_views(
318
+ data: np.ndarray | dict[AttributeType, np.ndarray], schema: ComponentMetaData
319
+ ) -> dict[AttributeType, CAttributeBuffer]:
320
+ """
321
+ Get C API compatible views on attribute buffers.
322
+
323
+ Args:
324
+ data (dict[AttributeType, np.ndarray]): the data.
325
+ schema (ComponentMetaData): the schema that the data should obey.
326
+
327
+ Returns:
328
+ dict[AttributeType, CAttributeBuffer]: the C API attribute buffer view per attribute.
329
+ """
330
+ if isinstance(data, np.ndarray):
331
+ return {}
332
+
333
+ return {
334
+ attribute: CAttributeBuffer(
335
+ data=_get_raw_attribute_data_view(data=attribute_data, schema=schema, attribute=attribute)
336
+ )
337
+ for attribute, attribute_data in data.items()
338
+ }
339
+
340
+
341
+ def _get_uniform_buffer_view(
342
+ data: DenseBatchData,
343
+ schema: ComponentMetaData,
344
+ is_batch: bool | None,
345
+ batch_size: int | None,
346
+ ) -> CBuffer:
347
+ """
348
+ Get a C API compatible view on a uniform buffer.
349
+
350
+ Args:
351
+ data: the data.
352
+ schema: the schema that the data should obey.
353
+ is_batch (bool | None): whether the data is a batch dataset.
354
+ batch_size (int | None): the batch size.
355
+
356
+ Returns:
357
+ the C API buffer view.
358
+ """
359
+ properties = _get_dense_buffer_properties(data, schema=schema, is_batch=is_batch, batch_size=batch_size)
360
+
361
+ return CBuffer(
362
+ data=_get_raw_component_data_view(data=data, schema=schema),
363
+ indptr=IdxPtr(),
364
+ n_elements_per_scenario=properties.n_elements_per_scenario,
365
+ batch_size=properties.batch_size,
366
+ total_elements=properties.n_total_elements,
367
+ attribute_data=_get_attribute_buffer_views(data=data, schema=schema),
368
+ )
369
+
370
+
371
+ def _get_sparse_buffer_view(
372
+ data: SparseBatchArray,
373
+ schema: ComponentMetaData,
374
+ batch_size: int | None,
375
+ ) -> CBuffer:
376
+ """
377
+ Get a C API compatible view on a sparse buffer.
378
+
379
+ Args:
380
+ data: the data.
381
+ schema: the schema that the data should obey.
382
+ batch_size (int | None): the batch size.
383
+
384
+ Returns:
385
+ the C API buffer view.
386
+ """
387
+ contents = data["data"]
388
+ indptr = data["indptr"]
389
+
390
+ properties = _get_sparse_buffer_properties(data, schema=schema, batch_size=batch_size)
391
+
392
+ return CBuffer(
393
+ data=_get_raw_component_data_view(data=contents, schema=schema),
394
+ indptr=_get_indptr_view(indptr),
395
+ n_elements_per_scenario=properties.n_elements_per_scenario,
396
+ batch_size=properties.batch_size,
397
+ total_elements=properties.n_total_elements,
398
+ attribute_data=_get_attribute_buffer_views(data=contents, schema=schema),
399
+ )
400
+
401
+
402
+ def get_buffer_view(
403
+ data: ComponentData,
404
+ schema: ComponentMetaData,
405
+ is_batch: bool | None = None,
406
+ batch_size: int | None = None,
407
+ ) -> CBuffer:
408
+ """
409
+ Get a C API compatible view on a buffer.
410
+
411
+ Args:
412
+ data: the data.
413
+ schema: the schema that the data should obey.
414
+ is_batch (bool | None): whether the data is a batch dataset. [optional]
415
+ batch_size (int | None): the batch size. [optional]
416
+
417
+ Returns:
418
+ the C API buffer view.
419
+ """
420
+ if not is_sparse(data):
421
+ return _get_uniform_buffer_view(cast(DenseBatchData, data), schema, is_batch, batch_size)
422
+
423
+ if is_batch is not None and not is_batch:
424
+ raise ValueError("Sparse data must be batch data")
425
+
426
+ return _get_sparse_buffer_view(cast(SparseBatchArray, data), schema, batch_size)
427
+
428
+
429
+ def create_buffer(properties: BufferProperties, schema: ComponentMetaData) -> ComponentData:
430
+ """
431
+ Create a buffer with the provided properties and type.
432
+
433
+ Args:
434
+ properties: the desired buffer properties.
435
+ schema: the data type of the buffer.
436
+
437
+ Raises:
438
+ ValueError: if the buffer properties are not consistent.
439
+
440
+ Returns:
441
+ np.ndarray | dict[[str, np.ndarray]: a buffer with the correct properties.
442
+ """
443
+ if properties.is_sparse:
444
+ return _create_sparse_buffer(properties=properties, schema=schema)
445
+
446
+ return _create_uniform_buffer(properties=properties, schema=schema)
447
+
448
+
449
+ def _create_uniform_buffer(properties: BufferProperties, schema: ComponentMetaData) -> DenseBatchData:
450
+ """
451
+ Create a uniform buffer with the provided properties and type.
452
+
453
+ Args:
454
+ properties: the desired buffer properties.
455
+ schema: the data type of the buffer.
456
+
457
+ Raises:
458
+ ValueError: if the buffer properties are not uniform.
459
+
460
+ Returns:
461
+ A uniform buffer with the correct properties.
462
+ """
463
+ if properties.is_sparse:
464
+ raise ValueError(f"A uniform buffer cannot be sparse. {VALIDATOR_MSG}")
465
+
466
+ shape: int | tuple[int, int] = (
467
+ (properties.batch_size, properties.n_elements_per_scenario)
468
+ if properties.is_batch
469
+ else properties.n_elements_per_scenario
470
+ )
471
+ return _create_contents_buffer(shape=shape, dtype=schema.dtype, columns=properties.columns)
472
+
473
+
474
+ def _create_sparse_buffer(properties: BufferProperties, schema: ComponentMetaData) -> SparseBatchData:
475
+ """
476
+ Create a sparse buffer with the provided properties and type.
477
+
478
+ Args:
479
+ properties: the desired buffer properties.
480
+ schema: the data type of the buffer.
481
+
482
+ Raises:
483
+ ValueError: if the buffer properties are not sparse.
484
+
485
+ Returns:
486
+ A sparse buffer with the correct properties.
487
+ """
488
+ data: SingleComponentData = _create_contents_buffer(
489
+ shape=properties.n_total_elements,
490
+ dtype=schema.dtype,
491
+ columns=properties.columns,
492
+ )
493
+ indptr: IndexPointer = np.array([0] * properties.batch_size + [properties.n_total_elements], dtype=IdxC)
494
+ return cast(SparseBatchData, {"data": data, "indptr": indptr})
495
+
496
+
497
+ @overload
498
+ def _create_contents_buffer(shape, dtype, columns: None) -> SingleArray | DenseBatchArray: ...
499
+ @overload
500
+ def _create_contents_buffer(
501
+ shape, dtype, columns: list[AttributeType]
502
+ ) -> SingleColumnarData | DenseBatchColumnarData: ...
503
+ def _create_contents_buffer(shape, dtype, columns):
504
+ if columns is None:
505
+ return np.empty(shape=shape, dtype=dtype)
506
+
507
+ return {attribute: np.empty(shape=shape, dtype=dtype[attribute]) for attribute in columns}