Skip to content

Commit

Permalink
Merge pull request #132 from tdixon97/main
Browse files Browse the repository at this point in the history
Allow table evaluations to return np.ndarray also without numexpr
  • Loading branch information
gipert authored Jan 28, 2025
2 parents b678606 + c22d610 commit c76cb31
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 19 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ test = [
"pylegendtestdata",
"pytest>=6.0",
"pytest-cov",
"dbetto",
]

[project.scripts]
Expand Down
50 changes: 31 additions & 19 deletions src/lgdo/types/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,31 +351,39 @@ def eval(
msg = f"evaluating {expr!r} with locals={(self_unwrap | parameters)} and {has_ak=}"
log.debug(msg)

# use numexpr if we are only dealing with numpy data types (and no global dictionary)
if not has_ak and modules is None:
out_data = ne.evaluate(
expr,
local_dict=(self_unwrap | parameters),
)

msg = f"...the result is {out_data!r}"
log.debug(msg)

# need to convert back to LGDO
# np.evaluate should always return a numpy thing?
if out_data.ndim == 0:
return Scalar(out_data.item())
if out_data.ndim == 1:
return Array(out_data)
if out_data.ndim == 2:
return ArrayOfEqualSizedArrays(nda=out_data)
def _make_lgdo(data):
if data.ndim == 0:
return Scalar(data.item())
if data.ndim == 1:
return Array(data)
if data.ndim == 2:
return ArrayOfEqualSizedArrays(nda=data)

msg = (
f"evaluation resulted in {out_data.ndim}-dimensional data, "
f"evaluation resulted in {data.ndim}-dimensional data, "
"I don't know which LGDO this corresponds to"
)
raise RuntimeError(msg)

# use numexpr if we are only dealing with numpy data types (and no global dictionary)
if not has_ak and modules is None:
try:
out_data = ne.evaluate(
expr,
local_dict=(self_unwrap | parameters),
)

msg = f"...the result is {out_data!r}"
log.debug(msg)

# need to convert back to LGDO
# np.evaluate should always return a numpy thing?
return _make_lgdo(out_data)

except Exception:
msg = f"Warning {expr} could not be evaluated with numexpr probably due to some not allowed characters, trying with eval()."
log.debug(msg)

# resort to good ol' eval()
globs = {"ak": ak, "np": np}
if modules is not None:
Expand All @@ -392,6 +400,10 @@ def eval(
return Array(out_data.to_numpy())
return VectorOfVectors(out_data)

# modules can still produce numpy array
if isinstance(out_data, np.ndarray):
return _make_lgdo(out_data)

if np.isscalar(out_data):
return Scalar(out_data)

Expand Down
14 changes: 14 additions & 0 deletions tests/types/test_table_eval.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import dbetto
import hist
import numpy as np
import pytest
Expand Down Expand Up @@ -85,6 +86,19 @@ def test_eval_dependency():
res = obj.eval("lgdo.Array([1,2,3])", {}, modules={"lgdo": lgdo})
assert res == lgdo.Array([1, 2, 3])

# test with module returning np.array
assert obj.eval("np.sum(a)", {}, modules={"np": np}).value == np.int64(10)

# check bad type
with pytest.raises(RuntimeError):
obj.eval("hist.Hist()", modules={"hist": hist})

# check impossible numexpr can still run
assert np.allclose(
obj.eval(
"a*args.value",
{"args": dbetto.AttrsDict({"value": 2})},
modules={"lgdo": lgdo},
).view_as("np"),
[2, 4, 6, 8],
)

0 comments on commit c76cb31

Please sign in to comment.