Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ee7ed92
Add == for Unicode strings
lshaw8317 Feb 5, 2026
2412106
Passes tests
lshaw8317 Feb 5, 2026
3a94b39
Incorporated new functions, have to add tests
lshaw8317 Feb 5, 2026
5e4e50e
Passes tests
lshaw8317 Feb 6, 2026
cd588d0
Update to miniexpr dsl branch
lshaw8317 Feb 7, 2026
a3c38c1
Add decorator for partial lazyfuncs
lshaw8317 Feb 9, 2026
16c5e67
Add decorator for partial lazyfuncs
lshaw8317 Feb 9, 2026
3103a94
Add bench
lshaw8317 Feb 9, 2026
fad321b
Remove raise exception when miniexpr fails
lshaw8317 Feb 9, 2026
4655d99
Miniexpr path working for string ops
lshaw8317 Feb 10, 2026
e7d2121
Update to latest miniexpr
lshaw8317 Feb 10, 2026
0f76b55
Add explanation of filtrs_meta
lshaw8317 Feb 11, 2026
4359317
Add arbitrary shuffle meta
lshaw8317 Feb 12, 2026
71a5c45
Add bench for string shuffle andf ops
lshaw8317 Feb 12, 2026
fe3df2a
Add tests for arrays of strings
lshaw8317 Feb 13, 2026
664c7c9
Add optimised compression and tests for constructors
lshaw8317 Feb 13, 2026
9cbc193
Update test
lshaw8317 Feb 13, 2026
4475332
Merge branch 'main' into add_str
lshaw8317 Feb 14, 2026
1c78b31
Clean up merge and update c-blosc2
lshaw8317 Feb 14, 2026
481e30b
Test without stringshuffle C-blosc2
lshaw8317 Feb 14, 2026
83136bc
Stringshuffle isn't the problem
lshaw8317 Feb 14, 2026
1b56b05
Remove optimised string compression
lshaw8317 Feb 14, 2026
8dd9619
Change it back
lshaw8317 Feb 14, 2026
54e9d82
Merge branch 'main' into optimise_cumsum
lshaw8317 Feb 17, 2026
5e3ad6e
Fix string compression typo bug
lshaw8317 Feb 17, 2026
ffabd01
add debugging
lshaw8317 Feb 18, 2026
8fc6f2e
Merge branch 'main' into add_str
lshaw8317 Feb 18, 2026
8d09e04
Remove obsolete me_variable_ex
lshaw8317 Feb 18, 2026
0d884a5
Further debugging
lshaw8317 Feb 18, 2026
dd8fe73
Update to latest c-blosc2 main
FrancescAlted Feb 19, 2026
a28037d
Test without nchunks, nupdates as (0, 0)
FrancescAlted Feb 19, 2026
5447cbf
Restore (0, 0) case
FrancescAlted Feb 19, 2026
110bbcf
Run just the offending tests
FrancescAlted Feb 19, 2026
006950e
Run just the offending tests (II)
FrancescAlted Feb 19, 2026
57f5e7c
Add debugging
lshaw8317 Feb 19, 2026
e20f4e4
Further debugging
lshaw8317 Feb 19, 2026
465b6c7
Debugging (III)
lshaw8317 Feb 19, 2026
b3f9871
Debugging (IV)
lshaw8317 Feb 19, 2026
969aeff
Debugging (V)
lshaw8317 Feb 19, 2026
d6e9901
Fix tests (?)
lshaw8317 Feb 19, 2026
74e65d6
Testing
lshaw8317 Feb 19, 2026
1a93ce8
Make sure to copy cparams for constructors
lshaw8317 Feb 19, 2026
4dcb0de
Enable all tests
lshaw8317 Feb 19, 2026
6918613
Clean up code
lshaw8317 Feb 19, 2026
2821248
Merge branch 'main' into add_str
lshaw8317 Feb 19, 2026
14e15c6
Remove unused isin function
lshaw8317 Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ set(MINIEXPR_BUILD_BENCH OFF CACHE BOOL "Build miniexpr benchmarks" FORCE)

FetchContent_Declare(miniexpr
GIT_REPOSITORY https://github.com/Blosc/miniexpr.git
GIT_TAG 77d633cb2c134552da045b8d2cc0ad23908e6b9e
GIT_TAG b4cfa9c2dc26772ad2126e6a611f93daf050915f # dsl funcs
Comment thread
lshaw8317 marked this conversation as resolved.
Outdated
)
FetchContent_MakeAvailable(miniexpr)

Expand Down
4 changes: 4 additions & 0 deletions src/blosc2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ def _raise(exc):
cosh,
count_nonzero,
divide,
endswith,
equal,
exp,
expm1,
Expand Down Expand Up @@ -604,6 +605,7 @@ def _raise(exc):
sqrt,
square,
squeeze,
startswith,
std,
subtract,
sum,
Expand Down Expand Up @@ -724,6 +726,7 @@ def _raise(exc):
"detect_number_of_cores",
"divide",
"dparams_dflts",
"endswith",
"empty",
"empty_like",
"equal",
Expand Down Expand Up @@ -830,6 +833,7 @@ def _raise(exc):
"square",
"squeeze",
"stack",
"startswith",
"std",
"storage_dflts",
"subtract",
Expand Down
80 changes: 45 additions & 35 deletions src/blosc2/lazyexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,33 @@ def ne_evaluate(expression, local_dict=None, **kwargs):
try:
return numexpr.evaluate(expression, local_dict=local_dict, **kwargs)
except ValueError as e:
raise e # unsafe expression
except Exception: # non_numexpr functions present
global safe_blosc2_globals
res = eval(expression, safe_blosc2_globals, local_dict)
if "out" in kwargs:
out = kwargs.pop("out")
out[:] = res # will handle calc/decomp if res is lazyarray
return out
return res[()] if isinstance(res, blosc2.Operand) else res
if e.args and e.args[0] == "NumExpr 2 does not support Unicode as a dtype.":
pass
else:
raise e # unsafe expression
except Exception:
pass
# Try with blosc2 funcs as presence of non-numexpr funcs probably caused failure
# ne_evaluate will need safe_blosc2_globals for some functions (e.g. clip, logaddexp)
Comment thread
lshaw8317 marked this conversation as resolved.
Outdated
# that are implemented in python-blosc2 not in numexpr
global safe_blosc2_globals
if len(safe_blosc2_globals) == 0:
# First eval call, fill blosc2_safe_globals
safe_blosc2_globals = {"blosc2": blosc2}
# Add all first-level blosc2 functions
safe_blosc2_globals.update(
{
name: getattr(blosc2, name)
for name in dir(blosc2)
if callable(getattr(blosc2, name)) and not name.startswith("_")
}
)
res = eval(expression, safe_blosc2_globals, local_dict)
if "out" in kwargs:
out = kwargs.pop("out")
out[:] = res # will handle calc/decomp if res is lazyarray
return out
return res[()] if isinstance(res, blosc2.Operand) else res


# Define empty ndindex tuple for function defaults
Expand Down Expand Up @@ -201,6 +219,8 @@ def ne_evaluate(expression, local_dict=None, **kwargs):
"hypot",
"maximum",
"minimum",
"startswith",
"endswith",
)


Expand Down Expand Up @@ -2544,7 +2564,7 @@ def check_dtype(op, value1, value2):


def result_type(
*arrays_and_dtypes: blosc2.NDArray | int | float | complex | bool | blosc2.dtype,
*arrays_and_dtypes: blosc2.NDArray | int | float | complex | bool | str | blosc2.dtype,
) -> blosc2.dtype:
"""
Returns the dtype that results from applying type promotion rules (see Type Promotion Rules) to the arguments.
Expand All @@ -2562,7 +2582,7 @@ def result_type(
# Follow NumPy rules for scalar-array operations
# Create small arrays with the same dtypes and let NumPy's type promotion determine the result type
arrs = [
value
(np.array(value).dtype if isinstance(value, str) else value)
if (np.isscalar(value) or not hasattr(value, "dtype"))
else np.array([0], dtype=_convert_dtype(value.dtype))
for value in arrays_and_dtypes
Expand Down Expand Up @@ -2612,6 +2632,8 @@ def __init__(self, new_op): # noqa: C901
if not (isinstance(value1, (blosc2.Operand, np.ndarray)) or np.isscalar(value1))
else value1
)
# Reset values represented as np.int64 etc. to be set as Python natives
value1 = value1.item() if np.isscalar(value1) and hasattr(value1, "item") else value1
if value2 is None:
if isinstance(value1, LazyExpr):
self.expression = value1.expression if op is None else f"{op}({value1.expression})"
Expand All @@ -2624,7 +2646,7 @@ def __init__(self, new_op): # noqa: C901
self.operands = value1.operands
else:
if np.isscalar(value1):
value1 = ne_evaluate(f"{op}({value1})")
value1 = ne_evaluate(f"{op}({value1!r})")
op = None
self.operands = {"o0": value1}
self.expression = "o0" if op is None else f"{op}(o0)"
Expand All @@ -2634,6 +2656,9 @@ def __init__(self, new_op): # noqa: C901
if not (isinstance(value2, (blosc2.Operand, np.ndarray)) or np.isscalar(value2))
else value2
)
# Reset values represented as np.int64 etc. to be set as Python natives
value2 = value2.item() if np.isscalar(value2) and hasattr(value2, "item") else value2

if isinstance(value1, LazyExpr) or isinstance(value2, LazyExpr):
if isinstance(value1, LazyExpr):
newexpr = value1.update_expr(new_op)
Expand All @@ -2646,13 +2671,13 @@ def __init__(self, new_op): # noqa: C901
elif op in funcs_2args:
if np.isscalar(value1) and np.isscalar(value2):
self.expression = "o0"
self.operands = {"o0": ne_evaluate(f"{op}({value1}, {value2})")} # eager evaluation
self.operands = {"o0": ne_evaluate(f"{op}({value1!r}, {value2!r})")} # eager evaluation
elif np.isscalar(value2):
self.operands = {"o0": value1}
self.expression = f"{op}(o0, {value2})"
self.expression = f"{op}(o0, {value2!r})"
elif np.isscalar(value1):
self.operands = {"o0": value2}
self.expression = f"{op}({value1}, o0)"
self.expression = f"{op}({value1!r}, o0)"
else:
self.operands = {"o0": value1, "o1": value2}
self.expression = f"{op}(o0, o1)"
Expand All @@ -2661,16 +2686,16 @@ def __init__(self, new_op): # noqa: C901
self._dtype = dtype_
if np.isscalar(value1) and np.isscalar(value2):
self.expression = "o0"
self.operands = {"o0": ne_evaluate(f"({value1} {op} {value2})")} # eager evaluation
self.operands = {"o0": ne_evaluate(f"({value1!r} {op} {value2!r})")} # eager evaluation
elif np.isscalar(value2):
self.operands = {"o0": value1}
self.expression = f"(o0 {op} {value2})"
self.expression = f"(o0 {op} {value2!r})"
elif hasattr(value2, "shape") and value2.shape == ():
self.operands = {"o0": value1}
self.expression = f"(o0 {op} {value2[()]})"
elif np.isscalar(value1):
self.operands = {"o0": value2}
self.expression = f"({value1} {op} o0)"
self.expression = f"({value1!r} {op} o0)"
elif hasattr(value1, "shape") and value1.shape == ():
self.operands = {"o0": value2}
self.expression = f"({value1[()]} {op} o0)"
Expand Down Expand Up @@ -3192,22 +3217,7 @@ def find_args(expr):

return value, expression[idx:idx2]

def _compute_expr(self, item, kwargs): # noqa : C901
# ne_evaluate will need safe_blosc2_globals for some functions (e.g. clip, logaddexp)
# that are implemented in python-blosc2 not in numexpr
global safe_blosc2_globals
if len(safe_blosc2_globals) == 0:
# First eval call, fill blosc2_safe_globals for ne_evaluate
safe_blosc2_globals = {"blosc2": blosc2}
# Add all first-level blosc2 functions
safe_blosc2_globals.update(
{
name: getattr(blosc2, name)
for name in dir(blosc2)
if callable(getattr(blosc2, name)) and not name.startswith("_")
}
)

def _compute_expr(self, item, kwargs):
if any(method in self.expression for method in eager_funcs):
# We have reductions in the expression (probably coming from a string lazyexpr)
# Also includes slice
Expand Down Expand Up @@ -3765,7 +3775,7 @@ def _numpy_eval_expr(expression, operands, prefer_blosc=False):
for key, value in operands.items()
}

if "contains" in expression:
if np.any([a in expression for a in ["contains", "startswith", "endswith"]]):
_out = ne_evaluate(expression, local_dict=ops)
else:
# Create a globals dict with blosc2 version of functions preferentially
Expand Down
Loading
Loading