Skip to content

Commit 866c259

Browse files
Merge pull request #610 from Blosc/b2objects-cframe
Add structured b2o persistence for logical objects and improve lazy/proxy semantics
2 parents 98b0c10 + 6de6630 commit 866c259

32 files changed

Lines changed: 1130 additions & 413 deletions

doc/reference/lazyarray.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ You can get an object following the LazyArray API with any of the following ways
1414

1515
The LazyArray object is a thin wrapper around the expression or user-defined function that allows for lazy computation. This means that the expression is not computed until the ``compute`` or ``__getitem__`` methods are called. The ``compute`` method will return a new NDArray object with the result of the expression evaluation. The ``__getitem__`` method will return an NumPy object instead.
1616

17+
LazyArray objects also support user metadata via :attr:`LazyArray.vlmeta`. For
18+
in-memory objects, this metadata lives on the Python object itself. For
19+
persisted LazyArrays reopened from disk, metadata is synchronized with the
20+
underlying carrier and survives reopening.
21+
1722
See the `LazyExpr`_ and `LazyUDF`_ sections for more information.
1823

1924
.. currentmodule:: blosc2
@@ -33,6 +38,10 @@ See the `LazyExpr`_ and `LazyUDF`_ sections for more information.
3338
---------------
3439
.. automethod:: __getitem__
3540

41+
Attributes
42+
----------
43+
.. autoattribute:: vlmeta
44+
3645
.. _LazyExpr:
3746

3847
LazyExpr
@@ -51,7 +60,7 @@ LazyUDF
5160

5261
For getting a LazyUDF object (which is LazyArray-compliant) from a user-defined Python function, you can use the lazyudf constructor below. See `a tutorial on how this works <../getting_started/tutorials/03.lazyarray-udf.html>`_.
5362

54-
This object follows the `LazyArray`_ API for computation, although storage is not supported yet.
63+
This object follows the `LazyArray`_ API for computation and storage.
5564

5665
.. autofunction:: lazyudf
5766

examples/embeded-expr-udf-b2z.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#######################################################################
2+
# Copyright (c) 2019-present, Blosc Development Team <blosc@blosc.org>
3+
# All rights reserved.
4+
#
5+
# SPDX-License-Identifier: BSD-3-Clause
6+
#######################################################################
7+
8+
import numpy as np
9+
10+
import blosc2
11+
from blosc2 import where
12+
13+
14+
def show(label, value):
15+
print(f"{label}: {value}")
16+
17+
18+
@blosc2.dsl_kernel
19+
def masked_energy(a, b, mask):
20+
return where(mask > 0, a * a + 2 * b, 0.0)
21+
22+
23+
bundle_path = "example_b2o_bundle.b2z"
24+
blosc2.remove_urlpath(bundle_path)
25+
26+
# Build a portable bundle with ordinary arrays plus persisted lazy recipes.
27+
with blosc2.DictStore(bundle_path, mode="w", threshold=1) as store:
28+
store["/data/a"] = np.linspace(0.0, 1.0, 10, dtype=np.float32)
29+
store["/data/b"] = np.linspace(1.0, 2.0, 10, dtype=np.float32)
30+
store["/data/mask"] = np.asarray([0, 1, 1, 0, 1, 0, 1, 1, 0, 1], dtype=np.int8)
31+
32+
# Reopen the array members through the store so operand refs point back to
33+
# the .b2z container via dictstore_key payloads.
34+
a = store["/data/a"]
35+
b = store["/data/b"]
36+
mask = store["/data/mask"]
37+
38+
expr = blosc2.lazyexpr("(a - b) / (a + b + 1e-6)", operands={"a": a, "b": b})
39+
udf = blosc2.lazyudf(masked_energy, (a, b, mask), dtype=np.float32, shape=a.shape)
40+
41+
# DictStore currently stores array-like external leaves, so persist the
42+
# logical lazy objects through their b2o NDArray carriers.
43+
store["/recipes/expr"] = blosc2.ndarray_from_cframe(expr.to_cframe())
44+
store["/recipes/udf"] = blosc2.ndarray_from_cframe(udf.to_cframe())
45+
46+
show("Bundle created", bundle_path)
47+
48+
# Reopen the bundle read-only. The persisted LazyExpr and LazyUDF can be
49+
# evaluated directly without re-saving, rebuilding, or re-deploying the .b2z.
50+
with blosc2.open(bundle_path, mode="r") as store:
51+
show("Read-only keys", sorted(store.keys()))
52+
53+
expr = store["/recipes/expr"]
54+
udf = store["/recipes/udf"]
55+
56+
expr_result = expr.compute()
57+
udf_result = udf.compute()
58+
59+
show("Reopened expr type", type(expr).__name__)
60+
show("Reopened udf type", type(udf).__name__)
61+
show("Expr operand refs", expr.array.schunk.vlmeta["b2o"]["operands"])
62+
show("UDF operand refs", udf.array.schunk.vlmeta["b2o"]["operands"])
63+
show("Expr values", np.round(expr_result[:], 4))
64+
show("UDF values", udf_result[:])
65+
66+
expected_expr = (store["/data/a"][:] - store["/data/b"][:]) / (
67+
store["/data/a"][:] + store["/data/b"][:] + 1e-6
68+
)
69+
expected_udf = np.where(
70+
store["/data/mask"][:] > 0,
71+
store["/data/a"][:] ** 2 + 2 * store["/data/b"][:],
72+
0.0,
73+
).astype(np.float32)
74+
np.testing.assert_allclose(expr_result[:], expected_expr, rtol=1e-6, atol=1e-6)
75+
np.testing.assert_allclose(udf_result[:], expected_udf, rtol=1e-6, atol=1e-6)
76+
show("Read-only evaluation", "ok")

src/blosc2/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ class FPAccuracy(Enum):
253253
The C-Blosc2 version's string."""
254254

255255
if IS_WASM:
256-
from ._wasm_jit import init_wasm_jit_helpers
256+
from .wasm_jit import init_wasm_jit_helpers
257257

258258
_WASM_MINIEXPR_ENABLED = init_wasm_jit_helpers()
259259

@@ -538,6 +538,7 @@ def _raise(exc):
538538
from .batch_store import Batch, BatchStore
539539
from .vlarray import VLArray, vlarray_from_cframe
540540
from .ref import Ref
541+
from .b2objects import open_b2object
541542

542543
from .c2array import c2context, C2Array, URLPath
543544

@@ -548,7 +549,7 @@ def _raise(exc):
548549
lazyexpr,
549550
LazyArray,
550551
LazyUDF,
551-
_open_lazyarray,
552+
open_lazyarray,
552553
get_expr_operands,
553554
validate_expr,
554555
evaluate,

src/blosc2/_msgpack_utils.py

Lines changed: 0 additions & 222 deletions
This file was deleted.

0 commit comments

Comments
 (0)