What's new with Vector?
Henry Schreiner (Princeton University)
Jim Pivarski (Princeton University)
Saransh Chopra (University of Delhi) 🙋🏻♂️
PyHEP 2023
Quick recap!
Create and manipulate 2D, 3D, and Lorentz vectors -
pip install vector
conda install -c conda-forge vector
- Pure Python objects (numba-object)
- NumPy arrays of vectors
- Awkward Arrays of vectors (numpy-awkward)
Pure Python vectors
In [1]: v1 = vector.obj(x=3, y=4, z=-2, t=10)
In [2]: v2 = vector.obj(rho=5, phi=0.9273, eta=-0.39, t=10)
In [3]: v1 + v2
Out[3]: VectorObject4D(x=5.999980871972148, y=8.000014345949428, z=-3.999809798461195, t=20)
In [4]: v1.x + v2.rho
Out[4]: 8
NumPy vectors
In [1]: v1 = vector.arr(
...: {
...: "x": [1.0, 2.0, 3.0, 4.0, 5.0],
...: "y": [1.1, 2.2, 3.3, 4.4, 5.5],
...: "z": [0.1, 0.2, 0.3, 0.4, 0.5],
...: }
...: )
In [2]: v2 = vector.arr(
...: {
...: "px": [1.0, 2.0, 3.0, 4.0, 5.0],
...: "py": [1.1, 2.2, 3.3, 4.4, 5.5],
...: "pz": [0.1, 0.2, 0.3, 0.4, 0.5],
...: }
...: )
In [3]: v1 + v2
Out[3]:
VectorNumpy3D([( 2., 2.2, 0.2), ( 4., 4.4, 0.4), ( 6., 6.6, 0.6),
( 8., 8.8, 0.8), (10., 11. , 1. )],
dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
In [4]: v1.x + v2.px
Out[4]: array([ 2., 4., 6., 8., 10.])
In [1]: v1 = vector.awk(
...: [
...: [{"x": 1, "y": 1.1, "z": 0.1}, {"x": 2, "y": 2.2, "z": 0.2}],
...: [],
...: [{"x": 3, "y": 3.3, "z": 0.3}],
...: [{"x": 4, "y": 4.4, "z": 0.4}, {"x": 5, "y": 5.5, "z": 0.5}],
...: ]
...: )
In [2]: v2 = vector.awk(
...: [
...: [{"rho": 1, "phi": 1.1, "pz": 0.1}, {"rho": 2, "phi": 2.2, "pz": 0.2}],
...: [],
...: [{"rho": 3, "phi": 3.3, "pz": 0.3}],
...: [{"rho": 4, "phi": 4.4, "pz": 0.4}, {"rho": 5, "phi": 5.5, "pz": 0.5}],
...: ]
...: )
In [3]: v1 + v2
Out[3]: <VectorArray3D [[{x: 1.45, y: 1.99, ... z: 1}]] type='4 * var * Vector3D["x": fl...'>
In [4]: v1.x + v2.rho
Out[4]: <Array [[2, 4], [], [6], [8, 10]] type='4 * var * int64'>
Awkward vectors
JIT compiled vectors
In [1]: @numba.njit
...: def compute_mass(v1, v2):
...: return (v1 + v2).mass
In [2]: compute_mass(
...: vector.obj(px=1, py=2, pz=3, E=4),
...: vector.obj(px=-1, py=-2, pz=-3, E=4)
...: )
Out[2]: 8.0
In [1]: array = vector.awk(
...: [
...: [
...: dict(
...: {
...: x: numpy.random.normal(0, 1)
...: for x in ("px", "py", "pz")
...: },
...: E=numpy.random.normal(10, 1),
...: )
...: for inner in range(numpy.random.poisson(1.5))
...: ]
...: for outer in range(50)
...: ]
...: )
In [2]: @numba.njit
...: def compute_masses(array):
...: out = numpy.empty(len(array), numpy.float64)
...: for i, event in enumerate(array):
...: total = vector.obj(px=0.0, py=0.0, pz=0.0, E=0.0)
...: for vec in event:
...: total = total + vec
...: out[i] = total.mass
...: return out
...:
In [3]: compute_masses(array)
Out[3]:
array([ 0. , 10.36167953, 0. , 9.15367049, 28.62136726,
9.54993212, 0. , 9.00308428, 20.04212849, 10.06963788,
19.8551194 , 0. , 30.6957947 , 0. , 23.24512715,
19.88520087, 18.98248046, 0. , 30.44632714, 10.2959194 ,
9.26368332, 10.03039074, 21.56346856, 8.75170183, 8.80118952,
18.05519623, 59.30509616, 11.4278945 , 47.78437373, 18.26799481,
8.88103215, 0. , 0. , 30.78770864, 38.00767698,
21.21591576, 19.53493617, 0. , 28.8201169 , 29.29564491,
0. , 0. , 10.85570784, 0. , 7.66535582,
19.03323138, 38.85245788, 10.13798577, 9.30661898, 0. ])
Detailed resources
-
PyHEP 2022 talk: Saransh-cpp/Constructing-HEP-vector-
and- analyzing-HEP-data-using-Vector - Vector's README: scikit-hep/vector
- Vector's Documentation: vector.readthedocs.io
-
Vector's CHANGELOG: scikit-hep/vector/docs/changelog.md
- All talks - linked in README: README.md#talks-about-vector
But, what's new?
New constructors
In [1]: vector.VectorObject2D(x=2, y=3)
Out[1]: VectorObject2D(x=2, y=3)
In [2]: vector.MomentumObject2D(pt=2, phi=3)
Out[2]: MomentumObject2D(pt=2, phi=3)
In [3]: vector.VectorObject3D.from_xyz(2, 3, 4)
Out[3]: VectorObject3D(x=2, y=3, z=4)
In [4]: vector.MomentumObject2D.from_rhophi(2, 3)
Out[4]: MomentumObject2D(pt=2, phi=3)
In [5]: from vector.backends.object import AzimuthalObjectXY, LongitudinalObjec
...: tZ, TemporalObjectT
In [6]: vector.VectorObject2D(azimuthal=AzimuthalObjectXY(2, 3), longitudinal=L
...: ongitudinalObjectZ(4), temporal=TemporalObjectT(5))
Out[6]: VectorObject4D(x=2, y=3, z=4, t=5)
In [7]: vector.MomentumObject4D(azimuthal=AzimuthalObjectXY(2, 3), longitudinal
...: =LongitudinalObjectZ(4), temporal=TemporalObjectT(5))
Out[7]: MomentumObject4D(px=2, py=3, pz=4, E=5)
In [1]: vector.VectorNumpy2D([(1.1, 2.1), (1.2, 2.2), (1.3, 2.3), (1.4, 2.4), (
...: 1.5, 2.5)], dtype=[('x', float), ('y', float)])
Out[1]:
VectorNumpy2D([(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')])
In [2]: vector.MomentumNumpy3D([(1.1, 2.1, 3.1), (1.2, 2.2, 3.2), (1.3, 2.3, 3.
...: 3), (1.4, 2.4, 3.4), (1.5, 2.5, 3.5)], dtype=[('px', float), ('py', flo
...: at), ('pz', float)])
Out[2]:
MomentumNumpy3D([(1.1, 2.1, 3.1), (1.2, 2.2, 3.2), (1.3, 2.3, 3.3), (1.4, 2.4, 3.4),
(1.5, 2.5, 3.5)], dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
In [1]: vector.Array(
...: [
...: [{"x": 1, "y": 1.1, "z": 0.1}],
...: [],
...: [{"x": 3, "y": 3.3, "z": 0.3}],
...: [
...: {"x": 4, "y": 4.4, "z": 0.4},
...: {"x": 5, "y": 5.5, "z": 0.5},
...: {"x": 6, "y": 6.6, "z": 0.6},
...: ],
...: ]
...: )
Out[1]: <VectorArray3D [[{x: 1, y: 1.1, z: 0.1, ... z: 0.6}]] type='4 * var * Vector3D["...'>
In [2]: vector.Array(
...: [
...: [{"px": 1, "py": 1.1, "pz": 0.1}],
...: [],
...: [{"px": 3, "py": 3.3, "pz": 0.3}],
...: [
...: {"px": 4, "py": 4.4, "pz": 0.4},
...: {"px": 5, "py": 5.5, "pz": 0.5},
...: {"px": 6, "py": 6.6, "pz": 0.6},
...: ],
...: ]
...: )
Out[2]: <MomentumArray3D [[{x: 1, y: 1.1, z: 0.1, ... z: 0.6}]] type='4 * var * Momentum...'>
Awkward v2 support
Still using awkward v1? Still works with awkward v1!
No extra "vector" overhead for switching between awkward v1 to v2, everything is handled automatically
New features
User experience: Better reprs, better error messages, better
private-public API segregation, ...
Methods: to_Vector*D, sum, count, count_nonzero,
deltaRapidityPhi, deltaRapidityPhi2, ...
Support: Python 3.11 and 3.12 support
Less annoying bugs
Checks: Type checks in constructors
Constructors: Bug fixes in old constructors
Properties and methods: numpy.sum, high coordinate values, ...
Revamped documentation
The documentation is now targeted towards users and not the developers of vector
A nice way to get involved!
- better layout for content
- more docstrings
- updated and tested examples
- doctests for public API
- better contributing guide
- and much more...
Where is vector now?
A stable place with v1.0.0 out
Development is being spearheaded by bug reports and feature requests, no specific development targets
Mostly voluntary development, participating in hacktoberfest!
Future plans (2024)
Possibility of making Vector differentiable using Jax
Ties up with making the AGC differentiable (AD)
@software{Schreiner_vector,
author = {Schreiner, Henry and Pivarski, Jim and Chopra, Saransh},
doi = {10.5281/zenodo.5942082},
license = {BSD-3-Clause},
title = {{vector}},
url = {https://github.com/scikit-hep/vector}
}
Thank you!
saransh-cpp.github.io
PyHEP 2023
By Saransh Chopra
PyHEP 2023
- 294