mlarray 0.0.47__tar.gz → 0.0.48__tar.gz

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 (46) hide show
  1. {mlarray-0.0.47 → mlarray-0.0.48}/PKG-INFO +1 -1
  2. mlarray-0.0.48/examples/example_compressed_vs_uncompressed.py +42 -0
  3. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/meta.py +48 -0
  4. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/mlarray.py +254 -67
  5. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/PKG-INFO +1 -1
  6. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/SOURCES.txt +1 -0
  7. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_asarray.py +16 -0
  8. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_usage.py +22 -0
  9. {mlarray-0.0.47 → mlarray-0.0.48}/.github/workflows/workflow.yml +0 -0
  10. {mlarray-0.0.47 → mlarray-0.0.48}/.gitignore +0 -0
  11. {mlarray-0.0.47 → mlarray-0.0.48}/LICENSE +0 -0
  12. {mlarray-0.0.47 → mlarray-0.0.48}/MANIFEST.in +0 -0
  13. {mlarray-0.0.47 → mlarray-0.0.48}/README.md +0 -0
  14. {mlarray-0.0.47 → mlarray-0.0.48}/assets/banner.png +0 -0
  15. {mlarray-0.0.47 → mlarray-0.0.48}/assets/banner.png~ +0 -0
  16. {mlarray-0.0.47 → mlarray-0.0.48}/docs/api.md +0 -0
  17. {mlarray-0.0.47 → mlarray-0.0.48}/docs/cli.md +0 -0
  18. {mlarray-0.0.47 → mlarray-0.0.48}/docs/index.md +0 -0
  19. {mlarray-0.0.47 → mlarray-0.0.48}/docs/optimization.md +0 -0
  20. {mlarray-0.0.47 → mlarray-0.0.48}/docs/schema.md +0 -0
  21. {mlarray-0.0.47 → mlarray-0.0.48}/docs/usage.md +0 -0
  22. {mlarray-0.0.47 → mlarray-0.0.48}/docs/why.md +0 -0
  23. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_asarray.py +0 -0
  24. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_bboxes_only.py +0 -0
  25. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_channel.py +0 -0
  26. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_in_memory_constructors.py +0 -0
  27. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_metadata_only.py +0 -0
  28. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_non_spatial.py +0 -0
  29. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_open.py +0 -0
  30. {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_save_load.py +0 -0
  31. {mlarray-0.0.47 → mlarray-0.0.48}/mkdocs.yml +0 -0
  32. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/__init__.py +0 -0
  33. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/cli.py +0 -0
  34. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/utils.py +0 -0
  35. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/dependency_links.txt +0 -0
  36. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/entry_points.txt +0 -0
  37. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/requires.txt +0 -0
  38. {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/top_level.txt +0 -0
  39. {mlarray-0.0.47 → mlarray-0.0.48}/pyproject.toml +0 -0
  40. {mlarray-0.0.47 → mlarray-0.0.48}/setup.cfg +0 -0
  41. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_bboxes.py +0 -0
  42. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_constructors.py +0 -0
  43. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_create.py +0 -0
  44. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_metadata.py +0 -0
  45. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_open.py +0 -0
  46. {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_optimization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mlarray
3
- Version: 0.0.47
3
+ Version: 0.0.48
4
4
  Summary: Array format specialized for Machine Learning with Blosc2 backend and standardized metadata.
5
5
  Author-email: Karol Gotkowski <karol.gotkowski@dkfz.de>
6
6
  License: MIT
@@ -0,0 +1,42 @@
1
+ from pathlib import Path
2
+
3
+ import numpy as np
4
+
5
+ from mlarray import MLArray
6
+
7
+
8
+ def main():
9
+ array = np.arange(2 * 4 * 4, dtype=np.float32).reshape(2, 4, 4)
10
+
11
+ compressed_img = MLArray.asarray(
12
+ array,
13
+ compressed=True,
14
+ patch_size=None,
15
+ chunk_size=(1, 4, 4),
16
+ block_size=(1, 2, 2),
17
+ )
18
+ uncompressed_img = MLArray.asarray(array, compressed=False)
19
+
20
+ compressed_path = Path("example_compressed_output.mla")
21
+ uncompressed_path = Path("example_uncompressed_output.mla")
22
+
23
+ print("compressed in-memory backend (before save):", type(compressed_img._store))
24
+ print(
25
+ "uncompressed in-memory backend (before save):",
26
+ type(uncompressed_img._store),
27
+ )
28
+
29
+ compressed_img.save(compressed_path)
30
+ uncompressed_img.save(uncompressed_path)
31
+
32
+ print("compressed in-memory backend (after save):", type(compressed_img._store))
33
+ print(
34
+ "uncompressed in-memory backend (after save):",
35
+ type(uncompressed_img._store),
36
+ )
37
+ print("saved compressed file:", compressed_path)
38
+ print("saved uncompressed->compressed file:", uncompressed_path)
39
+
40
+
41
+ if __name__ == "__main__":
42
+ main()
@@ -393,6 +393,44 @@ def _cast_to_list(value: Any, label: str):
393
393
  return out
394
394
 
395
395
 
396
+ def _to_jsonable(value: Any) -> Any:
397
+ """Recursively convert values to JSON-serializable plain Python objects."""
398
+ if isinstance(value, Enum):
399
+ return value.value
400
+
401
+ if isinstance(value, Mapping):
402
+ return {str(k): _to_jsonable(v) for k, v in value.items()}
403
+
404
+ if isinstance(value, (list, tuple)):
405
+ return [_to_jsonable(v) for v in value]
406
+
407
+ if isinstance(value, np.generic):
408
+ return value.item()
409
+
410
+ return value
411
+
412
+
413
+ def _cast_to_jsonable_mapping(value: Any, label: str) -> dict[str, Any]:
414
+ """Cast a value to a JSON-serializable mapping.
415
+
416
+ Accepts mappings directly or objects exposing ``__dict__`` (for example
417
+ Blosc2 ``CParams`` / ``DParams`` objects).
418
+ """
419
+ if isinstance(value, Mapping):
420
+ out = dict(value)
421
+ elif hasattr(value, "__dict__"):
422
+ out = dict(vars(value))
423
+ else:
424
+ raise TypeError(f"{label} must be a mapping or object with __dict__")
425
+
426
+ out = _to_jsonable(out)
427
+ if not isinstance(out, dict):
428
+ raise TypeError(f"{label} could not be converted to a mapping")
429
+ if not is_serializable(out):
430
+ raise TypeError(f"{label} is not JSON-serializable")
431
+ return out
432
+
433
+
396
434
  def _validate_int(value: Any, label: str) -> None:
397
435
  """Validate that value is an int.
398
436
 
@@ -565,10 +603,14 @@ class MetaBlosc2(BaseMeta):
565
603
  chunk_size: List of per-dimension chunk sizes. Length must match ndims.
566
604
  block_size: List of per-dimension block sizes. Length must match ndims.
567
605
  patch_size: List of per-dimension patch sizes. Length must match spatial ndims.
606
+ cparams: Blosc2 compression parameters as a JSON-serializable dict.
607
+ dparams: Blosc2 decompression parameters as a JSON-serializable dict.
568
608
  """
569
609
  chunk_size: Optional[list] = None
570
610
  block_size: Optional[list] = None
571
611
  patch_size: Optional[list] = None
612
+ cparams: Optional[dict[str, Any]] = None
613
+ dparams: Optional[dict[str, Any]] = None
572
614
 
573
615
  def _validate_and_cast(self, *, ndims: Optional[int] = None, spatial_ndims: Optional[int] = None, **_: Any) -> None:
574
616
  """Validate and normalize tiling sizes.
@@ -591,6 +633,12 @@ class MetaBlosc2(BaseMeta):
591
633
  self.patch_size = _cast_to_list(self.patch_size, "meta.blosc2.patch_size")
592
634
  _validate_float_int_list(self.patch_size, "meta.blosc2.patch_size", spatial_ndims)
593
635
 
636
+ if self.cparams is not None:
637
+ self.cparams = _cast_to_jsonable_mapping(self.cparams, "meta.blosc2.cparams")
638
+
639
+ if self.dparams is not None:
640
+ self.dparams = _cast_to_jsonable_mapping(self.dparams, "meta.blosc2.dparams")
641
+
594
642
 
595
643
  class AxisLabelEnum(str, Enum):
596
644
  """Axis label/role identifiers used for spatial metadata.
@@ -2,7 +2,7 @@ from copy import deepcopy
2
2
  import numpy as np
3
3
  import blosc2
4
4
  import math
5
- from typing import Dict, Optional, Union, List, Tuple
5
+ from typing import Any, Dict, Optional, Union, List, Tuple
6
6
  from pathlib import Path
7
7
  import os
8
8
  from mlarray.meta import Meta, MetaBlosc2, AxisLabel, _spatial_axis_mask
@@ -28,6 +28,7 @@ class MLArray:
28
28
  block_size: Optional[Union[int, List, Tuple]] = None,
29
29
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
30
30
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
31
+ compressed: bool = True,
31
32
  ) -> None:
32
33
  """Initializes a MLArray instance.
33
34
 
@@ -75,6 +76,7 @@ class MLArray:
75
76
  self.mmap_mode = None
76
77
  self.meta = None
77
78
  self._store = None
79
+ self._backend = None
78
80
  if isinstance(array, (str, Path)) and (
79
81
  spacing is not None
80
82
  or origin is not None
@@ -94,7 +96,7 @@ class MLArray:
94
96
  "array is a filepath."
95
97
  )
96
98
  if isinstance(array, (str, Path)):
97
- self._load(array)
99
+ self._load(array, compressed=compressed)
98
100
  else:
99
101
  self._validate_and_add_meta(meta, spacing, origin, direction, axis_labels, False, validate=False)
100
102
  if array is not None:
@@ -106,10 +108,16 @@ class MLArray:
106
108
  block_size=block_size,
107
109
  cparams=cparams,
108
110
  dparams=dparams,
111
+ compressed=compressed,
109
112
  )
110
113
  has_array = True
111
114
  else:
112
- self._store = blosc2.empty((0,))
115
+ if compressed:
116
+ self._store = blosc2.empty((0,))
117
+ self._backend = "blosc2"
118
+ else:
119
+ self._store = np.empty((0,))
120
+ self._backend = "numpy"
113
121
  has_array = False
114
122
  if copy is not None:
115
123
  self.meta.copy_from(copy.meta)
@@ -224,6 +232,7 @@ class MLArray:
224
232
  cls,
225
233
  filepath: Union[str, Path],
226
234
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
235
+ compressed: bool = True,
227
236
  ):
228
237
  """Loads a MLArray file as a whole. Does not use memory-mapping. Both MLArray ('.mla') and Blosc2 ('.b2nd') files are supported.
229
238
 
@@ -243,7 +252,7 @@ class MLArray:
243
252
  RuntimeError: If the file extension is not ".b2nd" or ".mla".
244
253
  """
245
254
  class_instance = cls()
246
- class_instance._load(filepath, dparams)
255
+ class_instance._load(filepath, dparams, compressed=compressed)
247
256
  return class_instance
248
257
 
249
258
  @classmethod
@@ -257,6 +266,7 @@ class MLArray:
257
266
  block_size: Optional[Union[int, List, Tuple]] = None,
258
267
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
259
268
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
269
+ compressed: bool = True,
260
270
  ):
261
271
  """Create an in-memory MLArray with uninitialized values.
262
272
 
@@ -284,6 +294,7 @@ class MLArray:
284
294
  """
285
295
  class_instance = cls()
286
296
  class_instance._construct_in_memory(
297
+ constructor="empty",
287
298
  shape=shape,
288
299
  dtype=dtype,
289
300
  meta=meta,
@@ -292,7 +303,7 @@ class MLArray:
292
303
  block_size=block_size,
293
304
  cparams=cparams,
294
305
  dparams=dparams,
295
- store_builder=lambda **kwargs: blosc2.empty(**kwargs),
306
+ compressed=compressed,
296
307
  )
297
308
  return class_instance
298
309
 
@@ -307,6 +318,7 @@ class MLArray:
307
318
  block_size: Optional[Union[int, List, Tuple]] = None,
308
319
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
309
320
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
321
+ compressed: bool = True,
310
322
  ):
311
323
  """Create an in-memory MLArray filled with zeros.
312
324
 
@@ -332,6 +344,7 @@ class MLArray:
332
344
  """
333
345
  class_instance = cls()
334
346
  class_instance._construct_in_memory(
347
+ constructor="zeros",
335
348
  shape=shape,
336
349
  dtype=dtype,
337
350
  meta=meta,
@@ -340,7 +353,7 @@ class MLArray:
340
353
  block_size=block_size,
341
354
  cparams=cparams,
342
355
  dparams=dparams,
343
- store_builder=lambda **kwargs: blosc2.zeros(**kwargs),
356
+ compressed=compressed,
344
357
  )
345
358
  return class_instance
346
359
 
@@ -355,6 +368,7 @@ class MLArray:
355
368
  block_size: Optional[Union[int, List, Tuple]] = None,
356
369
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
357
370
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
371
+ compressed: bool = True,
358
372
  ):
359
373
  """Create an in-memory MLArray filled with ones.
360
374
 
@@ -382,6 +396,7 @@ class MLArray:
382
396
  dtype = blosc2.DEFAULT_FLOAT if dtype is None else dtype
383
397
  class_instance = cls()
384
398
  class_instance._construct_in_memory(
399
+ constructor="ones",
385
400
  shape=shape,
386
401
  dtype=dtype,
387
402
  meta=meta,
@@ -390,7 +405,7 @@ class MLArray:
390
405
  block_size=block_size,
391
406
  cparams=cparams,
392
407
  dparams=dparams,
393
- store_builder=lambda **kwargs: blosc2.ones(**kwargs),
408
+ compressed=compressed,
394
409
  )
395
410
  return class_instance
396
411
 
@@ -406,6 +421,7 @@ class MLArray:
406
421
  block_size: Optional[Union[int, List, Tuple]] = None,
407
422
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
408
423
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
424
+ compressed: bool = True,
409
425
  ):
410
426
  """Create an in-memory MLArray filled with ``fill_value``.
411
427
 
@@ -439,6 +455,7 @@ class MLArray:
439
455
  dtype = np.dtype(type(fill_value))
440
456
  class_instance = cls()
441
457
  class_instance._construct_in_memory(
458
+ constructor="full",
442
459
  shape=shape,
443
460
  dtype=dtype,
444
461
  meta=meta,
@@ -447,7 +464,8 @@ class MLArray:
447
464
  block_size=block_size,
448
465
  cparams=cparams,
449
466
  dparams=dparams,
450
- store_builder=lambda **kwargs: blosc2.full(fill_value=fill_value, **kwargs),
467
+ compressed=compressed,
468
+ constructor_kwargs={"fill_value": fill_value},
451
469
  )
452
470
  return class_instance
453
471
 
@@ -466,6 +484,7 @@ class MLArray:
466
484
  block_size: Optional[Union[int, List, Tuple]] = None,
467
485
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
468
486
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
487
+ compressed: bool = True,
469
488
  ):
470
489
  """Create an in-memory MLArray with evenly spaced values.
471
490
 
@@ -525,6 +544,7 @@ class MLArray:
525
544
 
526
545
  class_instance = cls()
527
546
  class_instance._construct_in_memory(
547
+ constructor="arange",
528
548
  shape=shape,
529
549
  dtype=dtype,
530
550
  meta=meta,
@@ -533,13 +553,13 @@ class MLArray:
533
553
  block_size=block_size,
534
554
  cparams=cparams,
535
555
  dparams=dparams,
536
- store_builder=lambda **kwargs: blosc2.arange(
537
- start=start,
538
- stop=stop,
539
- step=step,
540
- c_order=c_order,
541
- **kwargs,
542
- ),
556
+ compressed=compressed,
557
+ constructor_kwargs={
558
+ "start": start,
559
+ "stop": stop,
560
+ "step": step,
561
+ "c_order": c_order,
562
+ },
543
563
  )
544
564
  return class_instance
545
565
 
@@ -559,6 +579,7 @@ class MLArray:
559
579
  block_size: Optional[Union[int, List, Tuple]] = None,
560
580
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
561
581
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
582
+ compressed: bool = True,
562
583
  ):
563
584
  """Create an in-memory MLArray with evenly spaced samples.
564
585
 
@@ -617,6 +638,7 @@ class MLArray:
617
638
 
618
639
  class_instance = cls()
619
640
  class_instance._construct_in_memory(
641
+ constructor="linspace",
620
642
  shape=shape,
621
643
  dtype=dtype,
622
644
  meta=meta,
@@ -625,14 +647,14 @@ class MLArray:
625
647
  block_size=block_size,
626
648
  cparams=cparams,
627
649
  dparams=dparams,
628
- store_builder=lambda **kwargs: blosc2.linspace(
629
- start=start,
630
- stop=stop,
631
- num=num,
632
- endpoint=endpoint,
633
- c_order=c_order,
634
- **kwargs,
635
- ),
650
+ compressed=compressed,
651
+ constructor_kwargs={
652
+ "start": start,
653
+ "stop": stop,
654
+ "num": num,
655
+ "endpoint": endpoint,
656
+ "c_order": c_order,
657
+ },
636
658
  )
637
659
  return class_instance
638
660
 
@@ -645,7 +667,8 @@ class MLArray:
645
667
  chunk_size: Optional[Union[int, List, Tuple]]= None,
646
668
  block_size: Optional[Union[int, List, Tuple]] = None,
647
669
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
648
- dparams: Optional[Union[Dict, blosc2.DParams]] = None
670
+ dparams: Optional[Union[Dict, blosc2.DParams]] = None,
671
+ compressed: bool = True,
649
672
  ):
650
673
  """Convert a NumPy array into an in-memory Blosc2-backed MLArray.
651
674
 
@@ -688,7 +711,16 @@ class MLArray:
688
711
  implemented for the provided dimensionality.
689
712
  """
690
713
  class_instance = cls()
691
- class_instance._asarray(array, meta, patch_size, chunk_size, block_size, cparams, dparams)
714
+ class_instance._asarray(
715
+ array,
716
+ meta,
717
+ patch_size,
718
+ chunk_size,
719
+ block_size,
720
+ cparams,
721
+ dparams,
722
+ compressed=compressed,
723
+ )
692
724
  return class_instance
693
725
 
694
726
  @classmethod
@@ -702,6 +734,7 @@ class MLArray:
702
734
  block_size: Optional[Union[int, List, Tuple]] = None,
703
735
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
704
736
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
737
+ compressed: bool = True,
705
738
  ):
706
739
  """Create an in-memory MLArray with the same shape as ``x``.
707
740
 
@@ -730,6 +763,7 @@ class MLArray:
730
763
  class_instance = cls()
731
764
  shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
732
765
  class_instance._construct_in_memory(
766
+ constructor="empty",
733
767
  shape=shape,
734
768
  dtype=dtype,
735
769
  meta=meta,
@@ -738,7 +772,7 @@ class MLArray:
738
772
  block_size=block_size,
739
773
  cparams=cparams,
740
774
  dparams=dparams,
741
- store_builder=lambda **kwargs: blosc2.empty(**kwargs),
775
+ compressed=compressed,
742
776
  )
743
777
  return class_instance
744
778
 
@@ -753,6 +787,7 @@ class MLArray:
753
787
  block_size: Optional[Union[int, List, Tuple]] = None,
754
788
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
755
789
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
790
+ compressed: bool = True,
756
791
  ):
757
792
  """Create an in-memory MLArray of zeros with the same shape as ``x``.
758
793
 
@@ -781,6 +816,7 @@ class MLArray:
781
816
  class_instance = cls()
782
817
  shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
783
818
  class_instance._construct_in_memory(
819
+ constructor="zeros",
784
820
  shape=shape,
785
821
  dtype=dtype,
786
822
  meta=meta,
@@ -789,7 +825,7 @@ class MLArray:
789
825
  block_size=block_size,
790
826
  cparams=cparams,
791
827
  dparams=dparams,
792
- store_builder=lambda **kwargs: blosc2.zeros(**kwargs),
828
+ compressed=compressed,
793
829
  )
794
830
  return class_instance
795
831
 
@@ -804,6 +840,7 @@ class MLArray:
804
840
  block_size: Optional[Union[int, List, Tuple]] = None,
805
841
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
806
842
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
843
+ compressed: bool = True,
807
844
  ):
808
845
  """Create an in-memory MLArray of ones with the same shape as ``x``.
809
846
 
@@ -832,6 +869,7 @@ class MLArray:
832
869
  class_instance = cls()
833
870
  shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
834
871
  class_instance._construct_in_memory(
872
+ constructor="ones",
835
873
  shape=shape,
836
874
  dtype=dtype,
837
875
  meta=meta,
@@ -840,7 +878,7 @@ class MLArray:
840
878
  block_size=block_size,
841
879
  cparams=cparams,
842
880
  dparams=dparams,
843
- store_builder=lambda **kwargs: blosc2.ones(**kwargs),
881
+ compressed=compressed,
844
882
  )
845
883
  return class_instance
846
884
 
@@ -856,6 +894,7 @@ class MLArray:
856
894
  block_size: Optional[Union[int, List, Tuple]] = None,
857
895
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
858
896
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
897
+ compressed: bool = True,
859
898
  ):
860
899
  """Create an in-memory MLArray filled with ``fill_value`` and shape of ``x``.
861
900
 
@@ -886,6 +925,7 @@ class MLArray:
886
925
  class_instance = cls()
887
926
  shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
888
927
  class_instance._construct_in_memory(
928
+ constructor="full",
889
929
  shape=shape,
890
930
  dtype=dtype,
891
931
  meta=meta,
@@ -894,7 +934,8 @@ class MLArray:
894
934
  block_size=block_size,
895
935
  cparams=cparams,
896
936
  dparams=dparams,
897
- store_builder=lambda **kwargs: blosc2.full(fill_value=fill_value, **kwargs),
937
+ compressed=compressed,
938
+ constructor_kwargs={"fill_value": fill_value},
898
939
  )
899
940
  return class_instance
900
941
 
@@ -923,7 +964,8 @@ class MLArray:
923
964
 
924
965
  if Path(filepath).is_file():
925
966
  os.remove(str(filepath))
926
-
967
+
968
+ self._ensure_blosc2_store()
927
969
  self._write_metadata(force=True)
928
970
  self._store.save(str(filepath))
929
971
  self._update_blosc2_meta()
@@ -937,6 +979,7 @@ class MLArray:
937
979
  """
938
980
  self._write_metadata()
939
981
  self._store = None
982
+ self._backend = None
940
983
  self.filepath = None
941
984
  self.support_metadata = None
942
985
  self.mode = None
@@ -1360,6 +1403,7 @@ class MLArray:
1360
1403
  dparams = MLArray._resolve_dparams(dparams)
1361
1404
 
1362
1405
  self._store = blosc2.open(urlpath=str(filepath), dparams=dparams, mode=mode, mmap_mode=mmap_mode)
1406
+ self._backend = "blosc2"
1363
1407
  self._read_meta()
1364
1408
  self._update_blosc2_meta()
1365
1409
  self.mode = mode
@@ -1439,15 +1483,13 @@ class MLArray:
1439
1483
 
1440
1484
  self._validate_and_add_meta(meta, has_array=True)
1441
1485
  spatial_axis_mask = [True] * len(shape) if self.meta.spatial.axis_labels is None else _spatial_axis_mask(self.meta.spatial.axis_labels)
1442
- self.meta.blosc2 = self._comp_and_validate_blosc2_meta(self.meta.blosc2, patch_size, chunk_size, block_size, shape, np.dtype(dtype).itemsize, spatial_axis_mask)
1486
+ self.meta.blosc2 = self._comp_and_validate_blosc2_meta(self.meta.blosc2, patch_size, chunk_size, block_size, shape, np.dtype(dtype).itemsize, spatial_axis_mask, cparams, dparams)
1443
1487
  self.meta._has_array.has_array = True
1444
1488
 
1445
1489
  self.support_metadata = str(filepath).endswith(f".{MLARRAY_SUFFIX}")
1446
-
1447
- cparams = MLArray._resolve_cparams(cparams)
1448
- dparams = MLArray._resolve_dparams(dparams)
1449
1490
 
1450
- self._store = blosc2.empty(shape=shape, dtype=np.dtype(dtype), urlpath=str(filepath), chunks=self.meta.blosc2.chunk_size, blocks=self.meta.blosc2.block_size, cparams=cparams, dparams=dparams, mmap_mode=mmap_mode)
1491
+ self._store = blosc2.empty(shape=shape, dtype=np.dtype(dtype), urlpath=str(filepath), chunks=self.meta.blosc2.chunk_size, blocks=self.meta.blosc2.block_size, cparams=MLArray._resolve_cparams(self.meta.blosc2.cparams), dparams=MLArray._resolve_dparams(self.meta.blosc2.dparams), mmap_mode=mmap_mode)
1492
+ self._backend = "blosc2"
1451
1493
  self._update_blosc2_meta()
1452
1494
  self.mode = mode
1453
1495
  self.mmap_mode = mmap_mode
@@ -1458,6 +1500,7 @@ class MLArray:
1458
1500
  self,
1459
1501
  filepath: Union[str, Path],
1460
1502
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
1503
+ compressed: bool = True,
1461
1504
  ):
1462
1505
  """Internal MLArray load method. Loads a MLArray file. Both MLArray ('.mla') and Blosc2 ('.b2nd') files are supported.
1463
1506
 
@@ -1484,10 +1527,14 @@ class MLArray:
1484
1527
  ondisk = blosc2.open(str(filepath), dparams=dparams, mode="r")
1485
1528
  cframe = ondisk.to_cframe()
1486
1529
  self._store = blosc2.ndarray_from_cframe(cframe, copy=True)
1530
+ self._backend = "blosc2"
1487
1531
  self.mode = None
1488
1532
  self.mmap_mode = None
1489
1533
  self._read_meta()
1490
1534
  self._update_blosc2_meta()
1535
+ if not compressed:
1536
+ self._store = np.asarray(self._store[...])
1537
+ self._backend = "numpy"
1491
1538
 
1492
1539
  def _asarray(
1493
1540
  self,
@@ -1497,7 +1544,8 @@ class MLArray:
1497
1544
  chunk_size: Optional[Union[int, List, Tuple]]= None,
1498
1545
  block_size: Optional[Union[int, List, Tuple]] = None,
1499
1546
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
1500
- dparams: Optional[Union[Dict, blosc2.DParams]] = None
1547
+ dparams: Optional[Union[Dict, blosc2.DParams]] = None,
1548
+ compressed: bool = True,
1501
1549
  ):
1502
1550
  """Internal MLArray asarray method.
1503
1551
 
@@ -1539,25 +1587,20 @@ class MLArray:
1539
1587
  if not isinstance(array, np.ndarray):
1540
1588
  raise TypeError("array must be a numpy.ndarray")
1541
1589
  self._construct_in_memory(
1542
- source_array=array,
1590
+ constructor="asarray",
1591
+ source_array=np.ascontiguousarray(array[...]),
1543
1592
  meta=meta,
1544
1593
  patch_size=patch_size,
1545
1594
  chunk_size=chunk_size,
1546
1595
  block_size=block_size,
1547
1596
  cparams=cparams,
1548
1597
  dparams=dparams,
1549
- store_builder=lambda **kwargs: blosc2.asarray(
1550
- kwargs["source_array"],
1551
- chunks=kwargs["chunks"],
1552
- blocks=kwargs["blocks"],
1553
- cparams=kwargs["cparams"],
1554
- dparams=kwargs["dparams"],
1555
- ),
1598
+ compressed=compressed,
1556
1599
  )
1557
1600
 
1558
1601
  def _construct_in_memory(
1559
1602
  self,
1560
- store_builder,
1603
+ constructor: str,
1561
1604
  shape: Optional[Union[int, List, Tuple, np.ndarray]] = None,
1562
1605
  dtype: Optional[np.dtype] = None,
1563
1606
  source_array: Optional[np.ndarray] = None,
@@ -1567,6 +1610,8 @@ class MLArray:
1567
1610
  block_size: Optional[Union[int, List, Tuple]] = None,
1568
1611
  cparams: Optional[Union[Dict, blosc2.CParams]] = None,
1569
1612
  dparams: Optional[Union[Dict, blosc2.DParams]] = None,
1613
+ compressed: bool = True,
1614
+ constructor_kwargs: Optional[dict[str, Any]] = None,
1570
1615
  ):
1571
1616
  """Internal generic constructor for in-memory Blosc2-backed MLArrays.
1572
1617
 
@@ -1575,8 +1620,7 @@ class MLArray:
1575
1620
  array shape, required when ``source_array`` is None.
1576
1621
  dtype (Optional[np.dtype]): Target dtype, required when
1577
1622
  ``source_array`` is None.
1578
- store_builder (Callable): Callable receiving normalized kwargs and
1579
- returning a Blosc2 NDArray.
1623
+ constructor (str): Constructor operation name.
1580
1624
  source_array (Optional[np.ndarray]): Source array that should be
1581
1625
  converted into an in-memory Blosc2-backed store.
1582
1626
  meta (Optional[Union[Dict, Meta]]): Optional metadata attached to
@@ -1599,12 +1643,13 @@ class MLArray:
1599
1643
  Raises:
1600
1644
  ValueError: If constructor inputs are inconsistent.
1601
1645
  """
1646
+ constructor_kwargs = {} if constructor_kwargs is None else dict(constructor_kwargs)
1647
+
1602
1648
  if source_array is not None:
1603
1649
  if shape is not None or dtype is not None:
1604
1650
  raise ValueError(
1605
1651
  "shape/dtype must not be set when source_array is provided."
1606
1652
  )
1607
- source_array = np.ascontiguousarray(source_array[...])
1608
1653
  shape = self._normalize_shape(source_array.shape)
1609
1654
  dtype = np.dtype(source_array.dtype)
1610
1655
  else:
@@ -1621,6 +1666,7 @@ class MLArray:
1621
1666
  if self.meta.spatial.axis_labels is None
1622
1667
  else _spatial_axis_mask(self.meta.spatial.axis_labels)
1623
1668
  )
1669
+
1624
1670
  self.meta.blosc2 = self._comp_and_validate_blosc2_meta(
1625
1671
  self.meta.blosc2,
1626
1672
  patch_size,
@@ -1629,29 +1675,101 @@ class MLArray:
1629
1675
  shape,
1630
1676
  dtype.itemsize,
1631
1677
  spatial_axis_mask,
1678
+ cparams,
1679
+ dparams,
1632
1680
  )
1633
1681
  self.meta._has_array.has_array = True
1634
1682
 
1635
- cparams = MLArray._resolve_cparams(cparams)
1636
- dparams = MLArray._resolve_dparams(dparams)
1683
+ backend_lib = blosc2 if compressed else np
1684
+ blosc2_storage_kwargs = {}
1685
+ if compressed:
1686
+ blosc2_storage_kwargs = {
1687
+ "chunks": self.meta.blosc2.chunk_size,
1688
+ "blocks": self.meta.blosc2.block_size,
1689
+ "cparams": MLArray._resolve_cparams(self.meta.blosc2.cparams),
1690
+ "dparams": MLArray._resolve_dparams(self.meta.blosc2.dparams),
1691
+ }
1692
+
1693
+ if constructor == "asarray":
1694
+ if compressed:
1695
+ self._store = blosc2.asarray(source_array, **blosc2_storage_kwargs)
1696
+ else:
1697
+ self._store = np.asarray(source_array, dtype=dtype)
1698
+ else:
1699
+ call_kwargs = self._build_constructor_call_kwargs(
1700
+ constructor=constructor,
1701
+ shape=shape,
1702
+ dtype=dtype,
1703
+ constructor_kwargs=constructor_kwargs,
1704
+ )
1637
1705
 
1638
- builder_kwargs = dict(
1639
- shape=shape,
1640
- dtype=dtype,
1641
- chunks=self.meta.blosc2.chunk_size,
1642
- blocks=self.meta.blosc2.block_size,
1643
- cparams=cparams,
1644
- dparams=dparams,
1645
- )
1646
- if source_array is not None:
1647
- builder_kwargs["source_array"] = source_array
1706
+ if not compressed and constructor in ("arange", "linspace"):
1707
+ self._store = self._construct_numpy_range(constructor, call_kwargs)
1708
+ else:
1709
+ func = getattr(backend_lib, constructor, None)
1710
+ if func is None:
1711
+ raise ValueError(f"Unknown constructor '{constructor}'.")
1712
+ if compressed:
1713
+ call_kwargs.update(blosc2_storage_kwargs)
1714
+ self._store = func(**call_kwargs)
1715
+
1716
+ self._backend = "blosc2" if compressed else "numpy"
1648
1717
 
1649
- self._store = store_builder(**builder_kwargs)
1718
+ self.support_metadata = True
1650
1719
 
1651
1720
  self._update_blosc2_meta()
1652
1721
  self._validate_and_add_meta(self.meta)
1653
1722
 
1654
- def _comp_and_validate_blosc2_meta(self, meta_blosc2, patch_size, chunk_size, block_size, shape, dtype_itemsize, spatial_axis_mask):
1723
+ @staticmethod
1724
+ def _build_constructor_call_kwargs(
1725
+ constructor: str,
1726
+ shape: Tuple[int, ...],
1727
+ dtype: np.dtype,
1728
+ constructor_kwargs: dict[str, Any],
1729
+ ) -> dict[str, Any]:
1730
+ """Build constructor kwargs shared by NumPy and Blosc2 backends."""
1731
+ if constructor in ("empty", "zeros", "ones"):
1732
+ return {"shape": shape, "dtype": dtype}
1733
+ if constructor == "full":
1734
+ return {
1735
+ "shape": shape,
1736
+ "fill_value": constructor_kwargs["fill_value"],
1737
+ "dtype": dtype,
1738
+ }
1739
+ if constructor == "arange":
1740
+ return {
1741
+ "start": constructor_kwargs["start"],
1742
+ "stop": constructor_kwargs["stop"],
1743
+ "step": constructor_kwargs["step"],
1744
+ "dtype": dtype,
1745
+ "shape": shape,
1746
+ "c_order": constructor_kwargs.get("c_order", True),
1747
+ }
1748
+ if constructor == "linspace":
1749
+ return {
1750
+ "start": constructor_kwargs["start"],
1751
+ "stop": constructor_kwargs["stop"],
1752
+ "num": constructor_kwargs["num"],
1753
+ "dtype": dtype,
1754
+ "shape": shape,
1755
+ "endpoint": constructor_kwargs.get("endpoint", True),
1756
+ "c_order": constructor_kwargs.get("c_order", True),
1757
+ }
1758
+ raise ValueError(f"Unknown constructor '{constructor}'.")
1759
+
1760
+ @staticmethod
1761
+ def _construct_numpy_range(
1762
+ constructor: str,
1763
+ call_kwargs: dict[str, Any],
1764
+ ) -> np.ndarray:
1765
+ """Construct NumPy arange/linspace arrays and apply shape/order reshaping."""
1766
+ shape = call_kwargs.pop("shape")
1767
+ c_order = call_kwargs.pop("c_order", True)
1768
+ func = getattr(np, constructor)
1769
+ arr = func(**call_kwargs)
1770
+ return np.reshape(arr, shape, order="C" if c_order else "F")
1771
+
1772
+ def _comp_and_validate_blosc2_meta(self, meta_blosc2, patch_size, chunk_size, block_size, shape, dtype_itemsize, spatial_axis_mask, cparams, dparams):
1655
1773
  """Compute and validate Blosc2 chunk/block metadata.
1656
1774
 
1657
1775
  Args:
@@ -1693,7 +1811,16 @@ class MLArray:
1693
1811
  if patch_size is not None:
1694
1812
  chunk_size, block_size = MLArray.comp_blosc2_params(shape, patch_size, spatial_axis_mask, bytes_per_pixel=dtype_itemsize)
1695
1813
 
1696
- meta_blosc2 = MetaBlosc2(chunk_size, block_size, patch_size)
1814
+ cparams = MLArray._resolve_cparams(cparams)
1815
+ dparams = MLArray._resolve_dparams(dparams)
1816
+
1817
+ meta_blosc2 = MetaBlosc2(
1818
+ chunk_size=chunk_size,
1819
+ block_size=block_size,
1820
+ patch_size=patch_size,
1821
+ cparams=cparams,
1822
+ dparams=dparams,
1823
+ )
1697
1824
  meta_blosc2._validate_and_cast(ndims=len(shape), spatial_ndims=num_spatial_axes)
1698
1825
  return meta_blosc2
1699
1826
 
@@ -1747,14 +1874,16 @@ class MLArray:
1747
1874
  Updates ``self.meta.blosc2`` from the underlying store when the array
1748
1875
  is present.
1749
1876
  """
1750
- if self.meta._has_array.has_array == True:
1877
+ if self._backend != "blosc2":
1878
+ return
1879
+ if self.support_metadata and self.meta._has_array.has_array == True:
1751
1880
  self.meta.blosc2.chunk_size = list(self._store.chunks)
1752
1881
  self.meta.blosc2.block_size = list(self._store.blocks)
1753
1882
 
1754
1883
  def _read_meta(self):
1755
1884
  """Read MLArray metadata from the underlying store, if available."""
1756
1885
  meta = Meta()
1757
- if self.support_metadata and isinstance(self._store, blosc2.ndarray.NDArray):
1886
+ if self.support_metadata and self._backend == "blosc2":
1758
1887
  meta = self._store.vlmeta["mlarray"]
1759
1888
  meta = Meta.from_mapping(meta)
1760
1889
  self._validate_and_add_meta(meta)
@@ -1766,7 +1895,7 @@ class MLArray:
1766
1895
  force (bool): If True, write even when mmap_mode is read-only.
1767
1896
  """
1768
1897
  is_writable = False
1769
- if self.support_metadata and isinstance(self._store, blosc2.ndarray.NDArray):
1898
+ if self.support_metadata:
1770
1899
  if self.mode in ('a', 'w') and self.mmap_mode is None:
1771
1900
  is_writable = True
1772
1901
  elif self.mmap_mode in ('r+', 'w+'):
@@ -1776,12 +1905,57 @@ class MLArray:
1776
1905
 
1777
1906
  if not is_writable:
1778
1907
  return
1779
-
1908
+
1909
+ if self._backend != "blosc2":
1910
+ return
1911
+
1780
1912
  metadata = self.meta.to_mapping()
1781
1913
  if not is_serializable(metadata):
1782
1914
  raise RuntimeError("Metadata is not serializable.")
1783
1915
  self._store.vlmeta["mlarray"] = metadata
1784
1916
 
1917
+ def _ensure_blosc2_store(self):
1918
+ """Ensure underlying store is Blosc2, converting from NumPy when needed."""
1919
+ if self._store is None:
1920
+ self._store = blosc2.empty((0,))
1921
+ self._backend = "blosc2"
1922
+ return
1923
+
1924
+ if self._backend == "blosc2":
1925
+ return
1926
+
1927
+ array = np.asarray(self._store)
1928
+ if not self.meta._has_array.has_array:
1929
+ self._store = blosc2.empty((0,))
1930
+ self._backend = "blosc2"
1931
+ return
1932
+
1933
+ shape = self._normalize_shape(array.shape)
1934
+ spatial_axis_mask = (
1935
+ [True] * len(shape)
1936
+ if self.meta.spatial.axis_labels is None
1937
+ else _spatial_axis_mask(self.meta.spatial.axis_labels)
1938
+ )
1939
+ self.meta.blosc2 = self._comp_and_validate_blosc2_meta(
1940
+ self.meta.blosc2,
1941
+ patch_size="default",
1942
+ chunk_size=self.meta.blosc2.chunk_size,
1943
+ block_size=self.meta.blosc2.block_size,
1944
+ shape=shape,
1945
+ dtype_itemsize=np.dtype(array.dtype).itemsize,
1946
+ spatial_axis_mask=spatial_axis_mask,
1947
+ cparams=self.meta.blosc2.cparams,
1948
+ dparams=self.meta.blosc2.dparams,
1949
+ )
1950
+ self._store = blosc2.asarray(
1951
+ np.ascontiguousarray(array),
1952
+ chunks=self.meta.blosc2.chunk_size,
1953
+ blocks=self.meta.blosc2.block_size,
1954
+ cparams=MLArray._resolve_cparams(self.meta.blosc2.cparams),
1955
+ dparams=MLArray._resolve_dparams(self.meta.blosc2.dparams),
1956
+ )
1957
+ self._backend = "blosc2"
1958
+
1785
1959
  @staticmethod
1786
1960
  def _normalize_shape(shape: Union[int, List, Tuple, np.ndarray]) -> Tuple[int, ...]:
1787
1961
  if isinstance(shape, (int, np.integer)):
@@ -1812,6 +1986,19 @@ class MLArray:
1812
1986
  """Resolve compression params with MLArray defaults."""
1813
1987
  if cparams is None:
1814
1988
  return {"codec": blosc2.Codec.LZ4HC, "clevel": 8}
1989
+ if isinstance(cparams, dict):
1990
+ cparams = dict(cparams)
1991
+ if "codec" in cparams and not isinstance(cparams["codec"], blosc2.Codec):
1992
+ cparams["codec"] = blosc2.Codec(cparams["codec"])
1993
+ if "splitmode" in cparams and not isinstance(cparams["splitmode"], blosc2.SplitMode):
1994
+ cparams["splitmode"] = blosc2.SplitMode(cparams["splitmode"])
1995
+ if "tuner" in cparams and not isinstance(cparams["tuner"], blosc2.Tuner):
1996
+ cparams["tuner"] = blosc2.Tuner(cparams["tuner"])
1997
+ if "filters" in cparams and isinstance(cparams["filters"], (list, tuple)):
1998
+ cparams["filters"] = [
1999
+ f if isinstance(f, blosc2.Filter) else blosc2.Filter(f)
2000
+ for f in cparams["filters"]
2001
+ ]
1815
2002
  return cparams
1816
2003
 
1817
2004
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mlarray
3
- Version: 0.0.47
3
+ Version: 0.0.48
4
4
  Summary: Array format specialized for Machine Learning with Blosc2 backend and standardized metadata.
5
5
  Author-email: Karol Gotkowski <karol.gotkowski@dkfz.de>
6
6
  License: MIT
@@ -22,6 +22,7 @@ docs/why.md
22
22
  examples/example_asarray.py
23
23
  examples/example_bboxes_only.py
24
24
  examples/example_channel.py
25
+ examples/example_compressed_vs_uncompressed.py
25
26
  examples/example_in_memory_constructors.py
26
27
  examples/example_metadata_only.py
27
28
  examples/example_non_spatial.py
@@ -64,6 +64,22 @@ class TestAsArray(unittest.TestCase):
64
64
  self.assertEqual(image.meta.source.to_plain(), {"patient_id": "p-001"})
65
65
  self.assertTrue(image.meta.is_seg)
66
66
 
67
+ def test_asarray_uncompressed_numpy_store_and_save(self):
68
+ with tempfile.TemporaryDirectory() as tmpdir:
69
+ array = _make_array(seed=3)
70
+ image = MLArray.asarray(array, meta={"case_id": "np"}, compressed=False)
71
+
72
+ self.assertTrue(isinstance(image._store, np.ndarray))
73
+ self.assertTrue(np.allclose(image.to_numpy(), array))
74
+ self.assertEqual(image.meta.source.to_plain(), {"case_id": "np"})
75
+
76
+ path = Path(tmpdir) / "asarray-numpy-save.mla"
77
+ image.save(path)
78
+ loaded = MLArray(path)
79
+ self.assertFalse(isinstance(loaded._store, np.ndarray))
80
+ self.assertTrue(np.allclose(loaded.to_numpy(), array))
81
+ self.assertEqual(loaded.meta.source.to_plain(), {"case_id": "np"})
82
+
67
83
 
68
84
  if __name__ == "__main__":
69
85
  unittest.main()
@@ -25,6 +25,18 @@ class TestUsage(unittest.TestCase):
25
25
  loaded = MLArray(path)
26
26
  self.assertEqual(loaded.shape, array.shape)
27
27
 
28
+ def test_default_usage_uncompressed_backend(self):
29
+ with tempfile.TemporaryDirectory() as tmpdir:
30
+ array = _make_array(seed=11)
31
+ image = MLArray(array, compressed=False)
32
+ self.assertTrue(isinstance(image._store, np.ndarray))
33
+
34
+ path = Path(tmpdir) / "sample-uncompressed.mla"
35
+ image.save(path)
36
+ loaded = MLArray(path)
37
+ self.assertEqual(loaded.shape, array.shape)
38
+ self.assertTrue(np.allclose(loaded.to_numpy(), array))
39
+
28
40
  def test_mmap_loading(self):
29
41
  with tempfile.TemporaryDirectory() as tmpdir:
30
42
  array = _make_array()
@@ -34,6 +46,16 @@ class TestUsage(unittest.TestCase):
34
46
  loaded = MLArray.open(path, mmap_mode="r")
35
47
  self.assertFalse(isinstance(loaded._store, np.ndarray))
36
48
 
49
+ def test_load_uncompressed_store(self):
50
+ with tempfile.TemporaryDirectory() as tmpdir:
51
+ array = _make_array()
52
+ path = Path(tmpdir) / "sample-load-uncompressed.mla"
53
+ MLArray(array).save(path)
54
+
55
+ loaded = MLArray.load(path, compressed=False)
56
+ self.assertTrue(isinstance(loaded._store, np.ndarray))
57
+ self.assertTrue(np.allclose(loaded.to_numpy(), array))
58
+
37
59
  def test_loading_and_saving(self):
38
60
  with tempfile.TemporaryDirectory() as tmpdir:
39
61
  array = _make_array()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes