From b6748502968bde2230d03d25d7dfbc230974cb7b Mon Sep 17 00:00:00 2001 From: ricardoV94 Date: Fri, 22 May 2026 00:25:09 +0200 Subject: [PATCH] Don't hide nfunc_spec of Real because of python Elemwise perform --- pytensor/scalar/basic.py | 3 +-- tests/tensor/test_elemwise.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pytensor/scalar/basic.py b/pytensor/scalar/basic.py index 90fde303e6..72a4fdab5c 100644 --- a/pytensor/scalar/basic.py +++ b/pytensor/scalar/basic.py @@ -3929,8 +3929,7 @@ class Real(UnaryScalarOp): """ - # numpy.real(float32) return a view on the inputs. - # nfunc_spec = ('real', 1, 1) + nfunc_spec = ("real", 1, 1) def impl(self, x): return np.real(x) diff --git a/tests/tensor/test_elemwise.py b/tests/tensor/test_elemwise.py index 7e217ab3ea..913a1036ff 100644 --- a/tests/tensor/test_elemwise.py +++ b/tests/tensor/test_elemwise.py @@ -1174,3 +1174,22 @@ def test_inplace_dtype_changed(): mode="fast_run", ) assert fn64.maker.fgraph.outputs[0].owner.op.destroy_map == {} + + +@pytest.mark.parametrize("linker", ["py", "cvm", "numba"]) +def test_nfunc_view_workaround(linker): + # np.real on a buffer returns a view, Elemwise python perform method works around it by making a copy + # Other backends shouldn't worry + a = pt.zvector("a") + b = pt.real(a) + c = Elemwise(ps.mul, inplace_pattern={0: 0})(b, 2.0) + out = c + a + + mode = Mode(linker=linker, optimizer=None) + f = function([a], out, mode=mode, accept_inplace=True) + + a_test = np.array([1 + 2j, 3 + 4j, 5 + 6j]) + out_expected = np.real(a_test) * 2 + a_test + + out_eval = f(a_test) + np.testing.assert_allclose(out_eval, out_expected)