Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VectorNumpy3D's azimuthal and longitudinal properties throw an error (similar for VectorNumpy4D) #194

Open
Saransh-cpp opened this issue May 25, 2022 · 0 comments · May be fixed by #466
Open
Labels
bug (unverified) The problem described would be a bug, but needs to be triaged hacktoberfest help wanted Extra attention is needed

Comments

@Saransh-cpp
Copy link
Collaborator

Reproducible example

import vector

vec = vector.array(
    [
        (1.1, 2.1, 3.1),
        (1.2, 2.2, 3.2),
        (1.3, 2.3, 3.3),
        (1.4, 2.4, 4.4),
        (1.5, 2.5, 5.5)
    ], dtype=[("x", float), ("y", float), ("z", float)]
)
print(vec.azimuthal)

Similarly for -

  • vec.longitudinal
  • vec.azimuthal for a 4D NumPy vector (VectorNumpy4D)
  • vec.longitudinal for a 4D NumPy vector (VectorNumpy4D)
  • vec.temporal for a 4D NumPy vector (VectorNumpy4D)

Error

>>> vec.azimuthal
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\numpy\core\arrayprint.py", line 1488, in _array_repr_implementation
    lst = array2string(arr, max_line_width, precision, suppress_small,
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\numpy\core\arrayprint.py", line 736, in array2string
    return _array2string(a, options, separator, prefix)
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\numpy\core\arrayprint.py", line 513, in wrapper
    return f(self, *args, **kwargs)
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\numpy\core\arrayprint.py", line 546, in _array2string
    lst = _formatArray(a, format_function, options['linewidth'],
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\numpy\core\arrayprint.py", line 889, in _formatArray
    return recurser(index=(),
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\numpy\core\arrayprint.py", line 853, in recurser
    word = recurser(index + (-1,), next_hanging_indent, next_width)
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\numpy\core\arrayprint.py", line 799, in recurser
    return format_function(a[index])
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\vector\_backends\numpy_.py", line 218, in __getitem__
    return _getitem(self, where, self.__class__._IS_MOMENTUM)  # type: ignore[arg-type]
  File "D:\extras\IRIS-HEP\.env\lib\site-packages\vector\_backends\numpy_.py", line 143, in _getitem
    return array.ObjectClass(*out.view(numpy.ndarray))  # type: ignore[misc, return-value]
TypeError: <lambda>() takes 3 positional arguments but 4 were given

Cause

Going through the stack trace I looked into the function _getitem, which was throwing an error related to the number of arguments. After adding some debug statements, I discovered that the if-else statements written to initialize azimuthal, longitudinal, and temporal are never executed when running the example.

This is due to the fact that the type of the passed array argument is AzimuthalNumpyXY (in the case of calling vec.azimuthal on a structured array having "x" and "y" as the datatypes). As array belongs to AzimuthalNumpyXY, it never has an attribute named _azimuthal_type, rather it has attributes named x and y.

The main intention here was to pass in a VectorNumpy2D, VectorNumpy3D, or VectorNumpy4D object but I can't really figure out why the __getitem method in the class GetItem is picking up AzimuthalNumpyXY.

I think the case of calling azimuthal on a VectorNumpy2D object works coincidentally, through the last else condition.

_getitem

def _getitem(
    array: typing.Union["VectorNumpy2D", "VectorNumpy3D", "VectorNumpy4D"],
    where: typing.Any,
    is_momentum: bool,
) -> typing.Union[float, FloatArray]:
    if isinstance(where, str):
        if is_momentum:
            where = _repr_momentum_to_generic.get(where, where)
        return array.view(numpy.ndarray)[where]
    else:
        out = numpy.ndarray.__getitem__(array, where)
        if not isinstance(out, numpy.void):
            return out

        azimuthal, longitudinal, temporal = None, None, None
        if hasattr(array, "_azimuthal_type"):
            azimuthal = array._azimuthal_type.ObjectClass(
                *(out[x] for x in _coordinate_class_to_names[_aztype(array)])
            )
        if hasattr(array, "_longitudinal_type"):
            longitudinal = array._longitudinal_type.ObjectClass(  # type: ignore[union-attr]
                *(out[x] for x in _coordinate_class_to_names[_ltype(array)])  # type: ignore[arg-type]
            )
        if hasattr(array, "_temporal_type"):
            temporal = array._temporal_type.ObjectClass(  # type: ignore[union-attr]
                *(out[x] for x in _coordinate_class_to_names[_ttype(array)])  # type: ignore[arg-type]
            )
        if temporal is not None:
            return array.ObjectClass(azimuthal, longitudinal, temporal)  # type: ignore[call-arg, arg-type, return-value]
        elif longitudinal is not None:
            return array.ObjectClass(azimuthal, longitudinal)  # type: ignore[call-arg, arg-type, return-value]
        elif azimuthal is not None:
            return array.ObjectClass(azimuthal)  # type: ignore[call-arg, return-value]
        else:
            return array.ObjectClass(*out.view(numpy.ndarray))  # type: ignore[misc, return-value]

A possible fix

I tried fixing this by modifying the function but ended up breaking some tests -

Modified function

def _getitem(
    array: typing.Union["VectorNumpy2D", "VectorNumpy3D", "VectorNumpy4D"],
    where: typing.Any,
    is_momentum: bool,
) -> typing.Union[float, FloatArray]:
    if isinstance(where, str):
        if is_momentum:
            where = _repr_momentum_to_generic.get(where, where)
        return array.view(numpy.ndarray)[where]
    else:
        out = numpy.ndarray.__getitem__(array, where)
        if not isinstance(out, numpy.void):
            return out
        azimuthal, longitudinal, temporal = None, None, None

        if (hasattr(array, "x") and hasattr(array, "y")):
            azimuthal = vector._backends.object_.AzimuthalObjectXY(*(out[x] for x in _coordinate_class_to_names[AzimuthalXY]))
        elif (hasattr(array, "rho") and hasattr(array, "phi")):
            azimuthal = vector._backends.object_.AzimuthalObjectRhoPhi(*(out[x] for x in _coordinate_class_to_names[AzimuthalRhoPhi]))
        
        if hasattr(array, "z"):
            longitudinal = vector._backends.object_.LongitudinalObjectZ(*(out[x] for x in _coordinate_class_to_names[LongitudinalZ]))  # type: ignore[union-attr]
        elif hasattr(array, "eta"):
            longitudinal = vector._backends.object_.LongitudinalObjectEta(*(out[x] for x in _coordinate_class_to_names[LongitudinalEta]))  # type: ignore[union-attr]
        elif hasattr(array, "theta"):
            longitudinal = vector._backends.object_.LongitudinalObjectTheta(*(out[x] for x in _coordinate_class_to_names[LongitudinalTheta]))  # type: ignore[union-attr]

        if hasattr(array, "t"):
            temporal = vector._backends.object_.TemporalObjectT(*(out[x] for x in _coordinate_class_to_names[TemporalT]))  # type: ignore[union-attr]
        elif hasattr(array, "tau"):
            temporal = vector._backends.object_.TemporalObjectTau(*(out[x] for x in _coordinate_class_to_names[TemporalTau]))  # type: ignore[union-attr]

        if temporal is not None:
            return temporal  # type: ignore[call-arg, arg-type, return-value]
        elif longitudinal is not None:
            return longitudinal  # type: ignore[call-arg, arg-type, return-value]
        elif azimuthal is not None:
            return azimuthal  # type: ignore[call-arg, return-value]
        else:
            return array.ObjectClass(*out.view(numpy.ndarray))  # type: ignore[misc, return-value]

This works somewhat fine. An example -

import vector

vec = vector.array(
    [
        (1.1, 2.1, 3.1),
        (1.2, 2.2, 3.2),
        (1.3, 2.3, 3.3),
        (1.4, 2.4, 4.4),
        (1.5, 2.5, 5.5)
    ], dtype=[("x", float), ("y", float), ("z", float)]
)
print(vec.azimuthal)

# output
# AzimuthalNumpyXY([(1.1, 2.1), (1.2, 2.2), (1.3, 2.3), (1.4, 2.4),
#                 (1.5, 2.5)],
#                 dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])

print(vec.longitudinal)
# output
# LongitudinalNumpyZ([(3.1,), (3.2,), (3.3,), (4.4,), (5.5,)],
#                    dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])

Broken tests

These are most probably failing as I did a lot of hard coding inside _getitem -

=============================================================================================== FAILURES =============================================================================================== 
__________________________________________________________________________________________ test_spatial_numpy __________________________________________________________________________________________ 

    def test_spatial_numpy():
        v1 = vector._backends.numpy_.VectorNumpy3D(
            [(0.1, 0.2, 0.3)],
            dtype=[("x", numpy.float64), ("y", numpy.float64), ("z", numpy.float64)],
        )
        v2 = vector._backends.numpy_.VectorNumpy3D(
            [(0.4, 0.5, 0.6)],
            dtype=[("x", numpy.float64), ("y", numpy.float64), ("z", numpy.float64)],
        )
        out = v1.cross(v2)
        assert isinstance(out, vector._backends.numpy_.VectorNumpy3D)
        assert out.dtype.names == ("x", "y", "z")
>       assert (out[0].x, out[0].y, out[0].z) == pytest.approx((-0.03, 0.06, -0.03))
E       AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'

tests\compute\spatial\test_cross.py:55: AttributeError
__________________________________________________________________________________________ test_lorentz_numpy __________________________________________________________________________________________

    def test_lorentz_numpy():
        v1 = vector._backends.numpy_.VectorNumpy4D(
            [(0.1, 0.2, 0.3, 99)],
            dtype=[
                ("x", numpy.float64),
                ("y", numpy.float64),
                ("z", numpy.float64),
                ("t", numpy.float64),
            ],
        )
        v2 = vector._backends.numpy_.VectorNumpy4D(
            [(0.4, 0.5, 0.6, 99)],
            dtype=[
                ("x", numpy.float64),
                ("y", numpy.float64),
                ("z", numpy.float64),
                ("t", numpy.float64),
            ],
        )
        out = v1.cross(v2)
        assert isinstance(out, vector._backends.numpy_.VectorNumpy3D)
        assert out.dtype.names == ("x", "y", "z")
        assert out.tolist() == pytest.approx([(-0.03, 0.06, -0.030000000000000013)])

        for t1 in (
            "xyzt",
            "xythetat",
            "xyetat",
            "rhophizt",
            "rhophithetat",
            "rhophietat",
            "xyztau",
            "xythetatau",
            "xyetatau",
            "rhophiztau",
            "rhophithetatau",
            "rhophietatau",
        ):
            for t2 in (
                "xyzt",
                "xythetat",
                "xyetat",
                "rhophizt",
                "rhophithetat",
                "rhophietat",
                "xyztau",
                "xythetatau",
                "xyetatau",
                "rhophiztau",
                "rhophithetatau",
                "rhophietatau",
            ):
                transformed1, transformed2 = (
                    getattr(v1, "to_" + t1)(),
                    getattr(v2, "to_" + t2)(),
                )
                out = transformed1.cross(transformed2)
                assert isinstance(out, vector._backends.numpy_.VectorNumpy3D)
                assert out.dtype.names == ("x", "y", "z")
>               assert (out[0].x, out[0].y, out[0].z) == pytest.approx((-0.03, 0.06, -0.03))
E               AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'

tests\compute\spatial\test_cross.py:186: AttributeError
__________________________________________________________________________________________ test_spatial_numpy __________________________________________________________________________________________ 

    def test_spatial_numpy():
        vec = vector._backends.numpy_.VectorNumpy3D(
            [(0.1, 0.2, 0.3)],
            dtype=[("x", numpy.float64), ("y", numpy.float64), ("z", numpy.float64)],
        )
        out = vec.rotateX(0.25)
        assert isinstance(out.azimuthal, vector._methods.AzimuthalXY)
        assert isinstance(out.longitudinal, vector._methods.LongitudinalZ)
>       assert out[0].x == pytest.approx(0.1)
E       AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'

tests\compute\spatial\test_rotateX.py:43: AttributeError
__________________________________________________________________________________________ test_lorentz_numpy __________________________________________________________________________________________

    def test_lorentz_numpy():
        vec = vector._backends.numpy_.VectorNumpy4D(
            [(0.1, 0.2, 0.3, 99)],
            dtype=[
                ("x", numpy.float64),
                ("y", numpy.float64),
                ("z", numpy.float64),
                ("t", numpy.float64),
            ],
        )
        out = vec.rotateX(0.25)
        assert isinstance(out.azimuthal, vector._methods.AzimuthalXY)
        assert isinstance(out.longitudinal, vector._methods.LongitudinalZ)
>       assert out[0].x == pytest.approx(0.1)
E       AttributeError: 'TemporalObjectT' object has no attribute 'x'

tests\compute\spatial\test_rotateX.py:106: AttributeError
__________________________________________________________________________________________ test_spatial_numpy __________________________________________________________________________________________ 

    def test_spatial_numpy():
        vec = vector._backends.numpy_.VectorNumpy3D(
            [(0.1, 0.2, 0.3)],
            dtype=[("x", numpy.float64), ("y", numpy.float64), ("z", numpy.float64)],
        )
        out = vec.rotateY(0.25)
        assert isinstance(out.azimuthal, vector._methods.AzimuthalXY)
        assert isinstance(out.longitudinal, vector._methods.LongitudinalZ)
>       assert out[0].x == pytest.approx(0.17111242994742137)
E       AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'

tests\compute\spatial\test_rotateY.py:43: AttributeError
__________________________________________________________________________________________ test_lorentz_numpy __________________________________________________________________________________________

    def test_lorentz_numpy():
        vec = vector._backends.numpy_.VectorNumpy4D(
            [(0.1, 0.2, 0.3, 99)],
            dtype=[
                ("x", numpy.float64),
                ("y", numpy.float64),
                ("z", numpy.float64),
                ("t", numpy.float64),
            ],
        )
        out = vec.rotateY(0.25)
        assert isinstance(out.azimuthal, vector._methods.AzimuthalXY)
        assert isinstance(out.longitudinal, vector._methods.LongitudinalZ)
>       assert out[0].x == pytest.approx(0.17111242994742137)
E       AttributeError: 'TemporalObjectT' object has no attribute 'x'

tests\compute\spatial\test_rotateY.py:106: AttributeError
__________________________________________________________________________________________ test_spatial_numpy __________________________________________________________________________________________ 

    def test_spatial_numpy():
        axis = vector._backends.numpy_.VectorNumpy3D(
            [(0.1, 0.2, 0.3)],
            dtype=[("x", numpy.float64), ("y", numpy.float64), ("z", numpy.float64)],
        )
        vec = vector._backends.numpy_.VectorNumpy3D(
            [(0.4, 0.5, 0.6)],
            dtype=[("x", numpy.float64), ("y", numpy.float64), ("z", numpy.float64)],
        )
        out = vec.rotate_axis(axis, 0.25)
        assert isinstance(out, vector._backends.numpy_.VectorNumpy3D)
        assert out.dtype.names == ("x", "y", "z")
>       assert out[0].x == pytest.approx(0.37483425404335763)
E       AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'

tests\compute\spatial\test_rotate_axis.py:59: AttributeError
__________________________________________________________________________________________ test_lorentz_numpy __________________________________________________________________________________________ 

    def test_lorentz_numpy():
        axis = vector._backends.numpy_.VectorNumpy4D(
            [(0.1, 0.2, 0.3, 99)],
            dtype=[
                ("x", numpy.float64),
                ("y", numpy.float64),
                ("z", numpy.float64),
                ("t", numpy.float64),
            ],
        )
        vec = vector._backends.numpy_.VectorNumpy4D(
            [(0.4, 0.5, 0.6, 99)],
            dtype=[
                ("x", numpy.float64),
                ("y", numpy.float64),
                ("z", numpy.float64),
                ("t", numpy.float64),
            ],
        )
        out = vec.rotate_axis(axis, 0.25)
        assert isinstance(out, vector._backends.numpy_.VectorNumpy4D)
        assert out.dtype.names == ("x", "y", "z", "t")
>       assert out[0].x == pytest.approx(0.37483425404335763)
E       AttributeError: 'TemporalObjectT' object has no attribute 'x'

tests\compute\spatial\test_rotate_axis.py:163: AttributeError
=========================================================================================== warnings summary =========================================================================================== 
c:\users\saransh\saransh_softwares\python_3.9\lib\site-packages\pyreadline\py3k_compat.py:8
  c:\users\saransh\saransh_softwares\python_3.9\lib\site-packages\pyreadline\py3k_compat.py:8: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    return isinstance(x, collections.Callable)

-- Docs: https://docs.pytest.org/en/stable/warnings.html
======================================================================================= short test summary info ======================================================================================== 
SKIPPED [2404] tests\test_compute_features.py:99: Unsupported Python version 3.9 (canonic 3.9.0beta5)
FAILED tests/compute/spatial/test_cross.py::test_spatial_numpy - AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'
FAILED tests/compute/spatial/test_cross.py::test_lorentz_numpy - AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'
FAILED tests/compute/spatial/test_rotateX.py::test_spatial_numpy - AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'
FAILED tests/compute/spatial/test_rotateX.py::test_lorentz_numpy - AttributeError: 'TemporalObjectT' object has no attribute 'x'
FAILED tests/compute/spatial/test_rotateY.py::test_spatial_numpy - AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'
FAILED tests/compute/spatial/test_rotateY.py::test_lorentz_numpy - AttributeError: 'TemporalObjectT' object has no attribute 'x'
FAILED tests/compute/spatial/test_rotate_axis.py::test_spatial_numpy - AttributeError: 'LongitudinalObjectZ' object has no attribute 'x'
FAILED tests/compute/spatial/test_rotate_axis.py::test_lorentz_numpy - AttributeError: 'TemporalObjectT' object has no attribute 'x'
@Saransh-cpp Saransh-cpp added the bug The problem described is something that must be fixed label May 25, 2022
@Saransh-cpp Saransh-cpp added bug (unverified) The problem described would be a bug, but needs to be triaged help wanted Extra attention is needed and removed bug The problem described is something that must be fixed labels Aug 17, 2022
@Saransh-cpp Saransh-cpp linked a pull request May 16, 2024 that will close this issue
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug (unverified) The problem described would be a bug, but needs to be triaged hacktoberfest help wanted Extra attention is needed
Projects
Status: No status
Development

Successfully merging a pull request may close this issue.

1 participant