vector


Namevector JSON
Version 1.4.0 PyPI version JSON
download
home_pageNone
SummaryVector classes and utilities
upload_time2024-04-23 14:25:05
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords vector
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <img alt="Vector logo" width="433" src="https://raw.githubusercontent.com/scikit-hep/vector/main/docs/_images/vector-logo.png"/>

# Vector: arrays of 2D, 3D, and Lorentz vectors

[![Actions Status][actions-badge]][actions-link]
[![Documentation Status][rtd-badge]][rtd-link]
[![pre-commit.ci status][pre-commit-badge]][pre-commit-link]
[![codecov percentage][codecov-badge]][codecov-link]
[![GitHub Discussion][github-discussions-badge]][github-discussions-link]
[![Gitter][gitter-badge]][gitter-link]
[![Code style: black][black-badge]][black-link]

[![PyPI platforms][pypi-platforms]][pypi-link]
[![PyPI version][pypi-version]][pypi-link]
[![Conda latest release][conda-version]][conda-link]
[![DOI][zenodo-badge]][zenodo-link]
[![LICENSE][license-badge]][license-link]
[![Scikit-HEP][sk-badge]][sk-link]

Vector is a Python 3.8+ library (Python 3.6 and 3.7 supported till `v0.9.0` and `v1.0.0`, respectively) for 2D, 3D, and [Lorentz vectors](https://en.wikipedia.org/wiki/Special_relativity#Physics_in_spacetime), especially _arrays of vectors_, to solve common physics problems in a NumPy-like way.

Main features of Vector:

- Pure Python with NumPy as its only dependency. This makes it easier to install.
- Vectors may be represented in a variety of coordinate systems: Cartesian, cylindrical, pseudorapidity, and any combination of these with time or proper time for Lorentz vectors. In all, there are 12 coordinate systems: {_x_-_y_ vs _ρ_-_φ_ in the azimuthal plane} × {_z_ vs _θ_ vs _η_ longitudinally} × {_t_ vs _τ_ temporally}.
- Uses names and conventions set by [ROOT](https://root.cern/)'s [TLorentzVector](https://root.cern.ch/doc/master/classTLorentzVector.html) and [Math::LorentzVector](https://root.cern.ch/doc/master/classROOT_1_1Math_1_1LorentzVector.html), as well as [scikit-hep/math](https://github.com/scikit-hep/scikit-hep/tree/master/skhep/math), [uproot-methods TLorentzVector](https://github.com/scikit-hep/uproot3-methods/blob/master/uproot3_methods/classes/TLorentzVector.py), [henryiii/hepvector](https://github.com/henryiii/hepvector), and [coffea.nanoevents.methods.vector](https://coffeateam.github.io/coffea/modules/coffea.nanoevents.methods.vector.html).
- Implemented on a variety of backends:
  - pure Python objects
  - [SymPy](https://www.sympy.org/en/index.html) vectors
  - NumPy arrays of vectors (as a [structured array](https://numpy.org/doc/stable/user/basics.rec.html) subclass)
  - [Awkward Arrays](https://awkward-array.org/) of vectors
  - potential for more: CuPy, TensorFlow, Torch, JAX...
- Awkward backend also implemented in [Numba](https://numba.pydata.org/) for JIT-compiled calculations on vectors.
- [JAX](https://awkward-array.org/doc/main/user-guide/how-to-specialize-differentiate-jax.html) and [Dask](https://dask-awkward.readthedocs.io/en/stable/) support through Awkward Arrays.
- Distinction between geometrical vectors, which have a minimum of attribute and method names, and vectors representing momentum, which have synonyms like `pt` = `rho`, `energy` = `t`, `mass` = `tau`.

## Installation

To install, use `pip install vector` or your favorite way to install in an environment.

## Overview

This overview is based on the [documentation here](https://vector.readthedocs.io/en/develop/usage/intro.html).

```python
import vector
import numpy as np
import awkward as ak  # at least version 1.2.0
import numba as nb
```

## Constructing a vector or an array of vectors

The easiest way to create one or many vectors is with a helper function:

- `vector.obj` to make a pure Python vector object,
- `vector.arr` to make a NumPy array of vectors (or `array`, lowercase, like `np.array`),
- `vector.awk` to make an Awkward Array of vectors (or `Array`, uppercase, like `ak.Array`).
- `vector.zip` to make an Awkward Array of vectors (similar to `ak.zip`)

### Pure Python vectors

You can directly use the `VectorObject` and `MomentumObject` classes to construct object type vectors:

```python
vector.VectorObject2D(x=1.1, y=2.2)
vector.MomentumObject3D(px=1.1, py=2.2, pz=3.3)
vector.VectorObject4D(x=1.1, y=2.2, eta=3.3, tau=4.4)
```

and so on for every class.

Or, you can use a single wrapper function to construct all possible combinations of
object type vectors:

```python
# Cartesian 2D vector
vector.obj(x=3, y=4)
# same in polar coordinates
vector.obj(rho=5, phi=0.9273)
# use "isclose" unless they are exactly equal
vector.obj(x=3, y=4).isclose(vector.obj(rho=5, phi=0.9273))
# Cartesian 3D vector
vector.obj(x=3, y=4, z=-2)
# Cartesian 4D vector
vector.obj(x=3, y=4, z=-2, t=10)
# in rho-phi-eta-t cylindrical coordinates
vector.obj(rho=5, phi=0.9273, eta=-0.39, t=10)
# use momentum-synonyms to get a momentum vector
vector.obj(pt=5, phi=0.9273, eta=-0.39, E=10)
vector.obj(rho=5, phi=0.9273, eta=-0.39, t=10) == vector.obj(
    pt=5, phi=0.9273, eta=-0.390035, E=10
)
# geometrical vectors have to use geometrical names ("tau", not "mass")
vector.obj(rho=5, phi=0.9273, eta=-0.39, t=10).tau
# momentum vectors can use momentum names (as well as geometrical ones)
vector.obj(pt=5, phi=0.9273, eta=-0.39, E=10).mass
# any combination of azimuthal, longitudinal, and temporal coordinates is allowed
vector.obj(pt=5, phi=0.9273, theta=1.9513, mass=8.4262)
vector.obj(x=3, y=4, z=-2, t=10).isclose(
    vector.obj(pt=5, phi=0.9273, theta=1.9513, mass=8.4262)
)

# Test instance type for any level of granularity.
(
    # is a vector or array of vectors
    isinstance(vector.obj(x=1.1, y=2.2), vector.Vector),
    # is 2D (not 3D or 4D)
    isinstance(vector.obj(x=1.1, y=2.2), vector.Vector2D),
    # is a vector object (not an array)
    isinstance(vector.obj(x=1.1, y=2.2), vector.VectorObject),
    # has momentum synonyms
    isinstance(vector.obj(px=1.1, py=2.2), vector.Momentum),
    # has transverse plane (2D, 3D, or 4D)
    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4), vector.Planar),
    # has all spatial coordinates (3D or 4D)
    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4), vector.Spatial),
    # has temporal coordinates (4D)
    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4), vector.Lorentz),
    # azimuthal coordinate type
    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4).azimuthal, vector.AzimuthalXY),
    # longitudinal coordinate type
    isinstance(
        vector.obj(x=1.1, y=2.2, z=3.3, t=4.4).longitudinal, vector.LongitudinalZ
    ),
    # temporal coordinate type
    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4).temporal, vector.TemporalT),
)
```

The allowed keyword arguments for 2D vectors are:

- `x` and `y` for Cartesian azimuthal coordinates,
- `px` and `py` for momentum,
- `rho` and `phi` for polar azimuthal coordinates,
- `pt` and `phi` for momentum.

For 3D vectors, you need the above and:

- `z` for the Cartesian longitudinal coordinate,
- `pz` for momentum,
- `theta` for the spherical polar angle (from $0$ to $\pi$, inclusive),
- `eta` for pseudorapidity, which is a kind of spherical polar angle.

For 4D vectors, you need the above and:

- `t` for the Cartesian temporal coordinate,
- `E` or `energy` to get four-momentum,
- `tau` for the "proper time" (temporal coordinate in the vector's rest coordinate system),
- `M` or `mass` to get four-momentum.

Since momentum vectors have momentum-synonyms _in addition_ to the geometrical names, any momentum-synonym will make the whole vector a momentum vector.

If you want to bypass the dimension and coordinate system inference through keyword arguments (e.g. for static typing), you can use specialized constructors:

```python
vector.VectorObject2D.from_xy(1.1, 2.2)
vector.MomentumObject3D.from_rhophiz(1.1, 2.2, 3.3)
vector.VectorObject4D.from_xyetatau(1.1, 2.2, 3.3, 4.4)
```

and so on, for all combinations of azimuthal, longitudinal, and temporal coordinates, geometric and momentum-flavored.

### SymPy vectors

> **Note:** Operations on SymPy vectors are only 100% compatible with numeric vectors (Python, NumPy, and Awkward backends) if the vectors are positive time-like, that is, if `t**2 > x**2 + y**2 + z**2`. The space-like and negative time-like cases have different sign conventions.

You can directly use the `VectorSympy` and `MomentumSympy` classes to construct object type vectors:

```python
import sympy

x, y, z, t, px, py, pz, eta, tau = sympy.symbols(
    "x y z t px py pz eta tau",
    real=True,  # see sympy assumptions to add more restrictions on the symbols
)
vector.VectorSympy2D(x=x, y=y)
vector.MomentumSympy3D(px=px, py=py, pz=pz)
vector.VectorSympy4D(x=x, y=y, eta=eta, tau=tau)
```

and so on for every class.

```python
# Test instance type for any level of granularity.
(
    # is a vector or array of vectors
    isinstance(vector.VectorSympy2D(x=x, y=y), vector.Vector),
    # is 2D (not 3D or 4D)
    isinstance(vector.VectorSympy2D(x=x, y=y), vector.Vector2D),
    # is a sympy vector (not an array)
    isinstance(vector.VectorSympy2D(x=x, y=y), vector.VectorSympy),
    # has momentum synonyms
    isinstance(vector.MomentumSympy2D(px=px, py=py), vector.Momentum),
    # has transverse plane (2D, 3D, or 4D)
    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t), vector.Planar),
    # has all spatial coordinates (3D or 4D)
    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t), vector.Spatial),
    # has temporal coordinates (4D)
    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t), vector.Lorentz),
    # azimuthal coordinate type
    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t).azimuthal, vector.AzimuthalXY),
    # longitudinal coordinate type
    isinstance(
        vector.VectorSympy4D(x=x, y=y, z=z, t=t).longitudinal, vector.LongitudinalZ
    ),
    # temporal coordinate type
    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t).temporal, vector.TemporalT),
)
```

Since `VectorSympy2D`, `VectorSympy3D`, `VectorSympy4D`, and their momentum equivalents operate on SymPy expressions, all of the normal SymPy methods and functions work on the results, coordinates, and the vectors.

```python
sympy.init_session()  # latex printing

v1 = vector.VectorSympy2D(x=x, y=y)
sympy.Eq(v1.rho, sympy.sqrt(x**2 + y**2))

v2 = vector.VectorSympy4D(x=x, y=y, z=z, t=t)
v2.to_rhophithetatau().tau

values = {x: 3, y: 2, z: 1, t: 10}  # t**2 > x**2 + y**2 + z**2
v2.is_timelike()
v2.is_timelike().subs(values)

v2.to_rhophithetatau().tau.subs(values).evalf()

v2.boost(v2.to_beta3())
v2.boost(v2.to_beta3()).t
v2.boost(v2.to_beta3()).t.simplify()
v2.boost(v2.to_beta3()).t.subs(values)
v2.boost(v2.to_beta3()).t.subs(values).evalf()
```

All of the keyword arguments and rules that apply to `vector.obj` construction apply to `vector.VectorSympyND` and `vector.MomentumObjectND` objects.

### NumPy arrays of vectors

You can directly use the `VectorNumpy` classes to construct object type vectors:

```python
# NumPy-like arguments (literally passed through to NumPy)
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)],
)

# Pandas-like arguments (dict from names to column arrays)
vector.VectorNumpy2D({"x": [1.1, 1.2, 1.3, 1.4, 1.5], "y": [2.1, 2.2, 2.3, 2.4, 2.5]})

# As with objects, the coordinate system and dimension is taken from the names of the fields.
vector.VectorNumpy4D(
    {
        "x": [1.1, 1.2, 1.3, 1.4, 1.5],
        "y": [2.1, 2.2, 2.3, 2.4, 2.5],
        "z": [3.1, 3.2, 3.3, 3.4, 3.5],
        "t": [4.1, 4.2, 4.3, 4.4, 4.5],
    }
)
```

and so on for every class.

Or, you can use a single wrapper function to construct all possible combinations of
NumPy type vectors:

```python
# NumPy-like arguments (literally passed through to NumPy)
vector.array(
    [(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)],
)

# Pandas-like arguments (dict from names to column arrays)
vector.array({"x": [1.1, 1.2, 1.3, 1.4, 1.5], "y": [2.1, 2.2, 2.3, 2.4, 2.5]})

# As with objects, the coordinate system and dimension is taken from the names of the fields.
vector.array(
    {
        "x": [1.1, 1.2, 1.3, 1.4, 1.5],
        "y": [2.1, 2.2, 2.3, 2.4, 2.5],
        "z": [3.1, 3.2, 3.3, 3.4, 3.5],
        "t": [4.1, 4.2, 4.3, 4.4, 4.5],
    }
)

vector.array(
    {
        "pt": [1.1, 1.2, 1.3, 1.4, 1.5],
        "phi": [2.1, 2.2, 2.3, 2.4, 2.5],
        "eta": [3.1, 3.2, 3.3, 3.4, 3.5],
        "M": [4.1, 4.2, 4.3, 4.4, 4.5],
    }
)
```

Existing NumPy arrays can be viewed as arrays of vectors, but it needs to be a [structured array](https://numpy.org/doc/stable/user/basics.rec.html) with recognized field names.

```python
np.arange(0, 24, 0.1).view(  # NumPy array
    [
        ("x", float),
        ("y", float),
        ("z", float),
        ("t", float),
    ]  # interpret groups of four values as named fields
).view(
    vector.VectorNumpy4D
)  # give it vector properties and methods
```

Since `VectorNumpy2D`, `VectorNumpy3D`, `VectorNumpy4D`, and their momentum equivalents are NumPy array subclasses, all of the normal NumPy methods and functions work on them.

```python
np.arange(0, 24, 0.1).view(
    [("x", float), ("y", float), ("z", float), ("t", float)]
).view(vector.VectorNumpy4D).reshape(6, 5, 2)
```

All of the keyword arguments and rules that apply to `vector.obj` construction apply to `vector.arr` dtypes.

Geometrical names are used in the dtype, even if momentum-synonyms are used in construction.

```python
vector.arr({"px": [1, 2, 3, 4], "py": [1.1, 2.2, 3.3, 4.4], "pz": [0.1, 0.2, 0.3, 0.4]})
```

### Awkward Arrays of vectors

[Awkward Arrays](https://awkward-array.org/) are arrays with more complex data structures than NumPy allows, such as variable-length lists, nested records, missing and even heterogeneous data (multiple data types: use sparingly).

The `vector.awk` function behaves exactly like the [ak.Array](https://awkward-array.readthedocs.io/en/latest/_auto/ak.Array.html) constructor, except that it makes arrays of vectors.

```python
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},
            {"x": 6, "y": 6.6, "z": 0.6},
        ],
    ]
)
```

If you want _any_ records named "`Vector2D`", "`Vector3D`", "`Vector4D`", "`Momentum2D`", "`Momentum3D`", or "`Momentum4D`" to be interpreted as vectors, register the behaviors globally.

```python
vector.register_awkward()

ak.Array(
    [
        [{"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},
            {"x": 6, "y": 6.6, "z": 0.6},
        ],
    ],
    with_name="Vector3D",
)
```

All of the keyword arguments and rules that apply to `vector.obj` construction apply to `vector.awk` field names.

Finally, the `VectorAwkward` mixins can be subclassed to create custom vector classes. The awkward behavior classes and projections must be named as `*Array`. For example, `coffea` uses the following names - `TwoVectorArray`, `ThreeVectorArray`, `PolarTwoVectorArray`, `SphericalThreeVectorArray`, ...

## Vector properties

Any geometrical coordinate can be computed from vectors in any coordinate system; they'll be provided or computed as needed.

```python
vector.obj(x=3, y=4).rho
vector.obj(rho=5, phi=0.9273).x
vector.obj(rho=5, phi=0.9273).y
vector.obj(x=1, y=2, z=3).theta
vector.obj(x=1, y=2, z=3).eta
```

Some properties are not coordinates, but derived from them.

```python
vector.obj(x=1, y=2, z=3).costheta
vector.obj(x=1, y=2, z=3).mag  # spatial magnitude
vector.obj(x=1, y=2, z=3).mag2  # spatial magnitude squared
```

These properties are provided because they can be computed faster or with more numerical stability in different coordinate systems. For instance, the magnitude ignores `phi` in polar coordinates.

```python
vector.obj(rho=3, phi=0.123456789, z=4).mag2
```

Momentum vectors have geometrical properties as well as their momentum-synonyms.

```python
vector.obj(px=3, py=4).rho
vector.obj(px=3, py=4).pt
vector.obj(x=1, y=2, z=3, E=4).tau
vector.obj(x=1, y=2, z=3, E=4).mass
```

Here's the key thing: _arrays of vectors return arrays of coordinates_.

```python
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],
    }
).theta

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}],
    ]
).theta

# Make a large, random NumPy array of 3D momentum vectors.
array = (
    np.random.normal(0, 1, 150)
    .view([(x, float) for x in ("x", "y", "z")])
    .view(vector.MomentumNumpy3D)
    .reshape(5, 5, 2)
)

# Get the transverse momentum of each one.
array.pt

# The array and its components have the same shape.
array.shape
array.pt.shape

# Make a large, random Awkward Array of 3D momentum vectors.
array = vector.awk(
    [
        [
            {x: np.random.normal(0, 1) for x in ("px", "py", "pz")}
            for inner in range(np.random.poisson(1.5))
        ]
        for outer in range(50)
    ]
)

# Get the transverse momentum of each one, in the same nested structure.
array.pt

# The array and its components have the same list lengths (and can therefore be used together in subsequent calculations).
ak.num(array)
ak.num(array.pt)
```

## Vector methods

Vector methods require arguments (in parentheses), which may be scalars or other vectors, depending on the calculation.

```python
vector.obj(x=3, y=4).rotateZ(0.1)
vector.obj(rho=5, phi=0.4).rotateZ(0.1)

# Broadcasts a scalar rotation angle of 0.5 to all elements of the NumPy array.
print(
    vector.arr({"rho": [1, 2, 3, 4, 5], "phi": [0.1, 0.2, 0.3, 0.4, 0.5]}).rotateZ(0.5)
)

# Matches each rotation angle to an element of the NumPy array.
print(
    vector.arr({"rho": [1, 2, 3, 4, 5], "phi": [0.1, 0.2, 0.3, 0.4, 0.5]}).rotateZ(
        np.array([0.1, 0.2, 0.3, 0.4, 0.5])
    )
)

# Broadcasts a scalar rotation angle of 0.5 to all elements of the Awkward Array.
print(
    vector.awk(
        [[{"rho": 1, "phi": 0.1}, {"rho": 2, "phi": 0.2}], [], [{"rho": 3, "phi": 0.3}]]
    ).rotateZ(0.5)
)

# Broadcasts a rotation angle of 0.1 to both elements of the first list, 0.2 to the empty list, and 0.3 to the only element of the last list.
print(
    vector.awk(
        [[{"rho": 1, "phi": 0.1}, {"rho": 2, "phi": 0.2}], [], [{"rho": 3, "phi": 0.3}]]
    ).rotateZ([0.1, 0.2, 0.3])
)

# Matches each rotation angle to an element of the Awkward Array.
print(
    vector.awk(
        [[{"rho": 1, "phi": 0.1}, {"rho": 2, "phi": 0.2}], [], [{"rho": 3, "phi": 0.3}]]
    ).rotateZ([[0.1, 0.2], [], [0.3]])
)
```

Some methods are equivalent to binary operators.

```python
vector.obj(x=3, y=4).scale(10)
vector.obj(x=3, y=4) * 10
10 * vector.obj(x=3, y=4)
vector.obj(rho=5, phi=0.5) * 10
```

Some methods involve more than one vector.

```python
vector.obj(x=1, y=2).add(vector.obj(x=5, y=5))
vector.obj(x=1, y=2) + vector.obj(x=5, y=5)
vector.obj(x=1, y=2).dot(vector.obj(x=5, y=5))
vector.obj(x=1, y=2) @ vector.obj(x=5, y=5)
```

The vectors can use different coordinate systems. Conversions are necessary, but minimized for speed and numeric stability.

```python
# both are Cartesian, dot product is exact
vector.obj(x=3, y=4) @ vector.obj(x=6, y=8)
# one is polar, dot product is approximate
vector.obj(rho=5, phi=0.9273) @ vector.obj(x=6, y=8)
# one is polar, dot product is approximate
vector.obj(x=3, y=4) @ vector.obj(rho=10, phi=0.9273)
# both are polar, a formula that depends on phi differences is used
vector.obj(rho=5, phi=0.9273) @ vector.obj(rho=10, phi=0.9273)
```

In Python, some "operators" are actually built-in functions, such as `abs`.

```python
abs(vector.obj(x=3, y=4))
```

Note that `abs` returns

- `rho` for 2D vectors
- `mag` for 3D vectors
- `tau` (`mass`) for 4D vectors

Use the named properties when you want magnitude in a specific number of dimensions; use `abs` when you want the magnitude for any number of dimensions.

The vectors can be from different backends. Normal rules for broadcasting Python numbers, NumPy arrays, and Awkward Arrays apply.

```python
vector.arr({"x": [1, 2, 3, 4, 5], "y": [0.1, 0.2, 0.3, 0.4, 0.5]}) + vector.obj(
    x=10, y=5
)

(
    vector.awk(
        [  # an Awkward Array of vectors
            [{"x": 1, "y": 1.1}, {"x": 2, "y": 2.2}],
            [],
            [{"x": 3, "y": 3.3}],
            [{"x": 4, "y": 4.4}, {"x": 5, "y": 5.5}],
        ]
    )
    + vector.obj(x=10, y=5)  # and a single vector object
)

(
    vector.awk(
        [  # an Awkward Array of vectors
            [{"x": 1, "y": 1.1}, {"x": 2, "y": 2.2}],
            [],
            [{"x": 3, "y": 3.3}],
            [{"x": 4, "y": 4.4}, {"x": 5, "y": 5.5}],
        ]
    )
    + vector.arr(
        {"x": [4, 3, 2, 1], "y": [0.1, 0.1, 0.1, 0.1]}
    )  # and a NumPy array of vectors
)
```

Some operations are defined for 2D or 3D vectors, but are usable on higher-dimensional vectors because the additional components can be ignored or are passed through unaffected.

```python
# deltaphi is a planar operation (defined on the transverse plane)
vector.obj(rho=1, phi=0.5).deltaphi(vector.obj(rho=2, phi=0.3))
# but we can use it on 3D vectors
vector.obj(rho=1, phi=0.5, z=10).deltaphi(vector.obj(rho=2, phi=0.3, theta=1.4))
# and 4D vectors
vector.obj(rho=1, phi=0.5, z=10, t=100).deltaphi(
    vector.obj(rho=2, phi=0.3, theta=1.4, tau=1000)
)
# and mixed dimensionality
vector.obj(rho=1, phi=0.5).deltaphi(vector.obj(rho=2, phi=0.3, theta=1.4, tau=1000))
```

This is especially useful for giving 4D vectors all the capabilities of 3D vectors.

```python
vector.obj(x=1, y=2, z=3).rotateX(np.pi / 4)
vector.obj(x=1, y=2, z=3, tau=10).rotateX(np.pi / 4)
vector.obj(pt=1, phi=1.3, eta=2).deltaR(vector.obj(pt=2, phi=0.3, eta=1))
vector.obj(pt=1, phi=1.3, eta=2, mass=5).deltaR(
    vector.obj(pt=2, phi=0.3, eta=1, mass=10)
)
```

For a few operations - `+`, `-`, `==`, `!=`, ... - the dimension of the vectors should be equal. This can be achieved by using the `like` method, `to_{coordinate_name}` methods, `to_Vector*D` methods. The `to_Vector*D` methods provide more flexibility to the users, that is, new coordinate values can be passed into the methods as named arguments.

```python
v1 = vector.obj(x=1, y=2, z=3)
v2 = vector.obj(x=1, y=2)

v1 - v2.like(v1)  # transforms v2 to v1's coordinate system (imputes z=0)
v1.like(v2) - v2  # transforms v1 to v2's coordinate system (removes z)
v1 - v2.to_xyz()  # transforms v2 to xyz coordinates (imputes z=0)
v1.to_xy() - v2  # transforms v1 to xy coordinates (removes z)
v1 - v2.to_Vector3D(z=3)  # transforms v2 to 3D (imputes z=3)
v1.to_Vector2D() - v2  # transforms v1 to 2D (removes z)
```

Similarly, for a few vector methods, the dimension of the input vectors are type checked strictly.

For instance, a cross-product is only defined for 3D and 7D vectors; hence, running the method on a 4D vector will error out.

```python
vector.obj(x=0.1, y=0.2, z=0.3).cross(vector.obj(x=0.4, y=0.5, z=0.6))
```

The (current) list of properties and methods is:

**Planar (2D, 3D, 4D):**

- `x` (`px`)
- `y` (`py`)
- `rho` (`pt`): two-dimensional magnitude
- `rho2` (`pt2`): two-dimensional magnitude squared
- `phi`
- `deltaphi(vector)`: difference in `phi` (signed and rectified to $-\pi$ through $\pi$)
- `rotateZ(angle)`
- `transform2D(obj)`: the `obj` must supply components through `obj["xx"]`, `obj["xy"]`, `obj["yx"]`, `obj["yy"]`
- `is_parallel(vector, tolerance=1e-5)`: only true _if they're pointing in the same direction_
- `is_antiparallel(vector, tolerance=1e-5)`: only true _if they're pointing in opposite directions_
- `is_perpendicular(vector, tolerance=1e-5)`

**Spatial (3D, 4D):**

- `z` (`pz`)
- `theta`
- `eta`
- `costheta`
- `cottheta`
- `mag` (`p`): three-dimensional magnitude, does not include temporal component
- `mag2` (`p2`): three-dimensional magnitude squared
- `cross`: cross-product (strictly 3D)
- `deltaangle(vector)`: difference in angle (always non-negative)
- `deltaeta(vector)`: difference in `eta` (signed)
- `deltaR(vector)`: $\Delta R = \sqrt{\Delta\phi^2 + \Delta\eta^2}$
- `deltaR2(vector)`: the above, squared
- `rotateX(angle)`
- `rotateY(angle)`
- `rotate_axis(axis, angle)`: the magnitude of `axis` is ignored, but it must be at least 3D
- `rotate_euler(phi, theta, psi, order="zxz")`: the arguments are in the same order as [ROOT::Math::EulerAngles](https://root.cern.ch/doc/master/classROOT_1_1Math_1_1EulerAngles.html), and `order="zxz"` agrees with ROOT's choice of conventions
- `rotate_nautical(yaw, pitch, roll)`
- `rotate_quaternion(u, i, j, k)`: again, the conventions match [ROOT::Math::Quaternion](https://root.cern.ch/doc/master/classROOT_1_1Math_1_1Quaternion.html).
- `transform3D(obj)`: the `obj` must supply components through `obj["xx"]`, `obj["xy"]`, etc.
- `is_parallel(vector, tolerance=1e-5)`: only true _if they're pointing in the same direction_
- `is_antiparallel(vector, tolerance=1e-5)`: only true _if they're pointing in opposite directions_
- `is_perpendicular(vector, tolerance=1e-5)`

**Lorentz (4D only):**

- `t` (`E`, `energy`): follows the [ROOT::Math::LorentzVector](https://root.cern/doc/master/LorentzVectorPage.html) behavior of treating spacelike vectors as negative `t` and negative `tau` and truncating wrong-direction timelike vectors
- `t2` (`E2`, `energy2`)
- `tau` (`M`, `mass`): see note above
- `tau2` (`M2`, `mass2`)
- `beta`: scalar(s) between $0$ (inclusive) and $1$ (exclusive, unless the vector components are infinite)
- `deltaRapidityPhi`: $\Delta R_{\mbox{rapidity}} = \Delta\phi^2 + \Delta \mbox{rapidity}^2$
- `deltaRapidityPhi2`: the above, squared
- `gamma`: scalar(s) between $1$ (inclusive) and $\infty$
- `rapidity`: scalar(s) between $0$ (inclusive) and $\infty$
- `boost_p4(four_vector)`: change coordinate system using another 4D vector as the difference
- `boost_beta(three_vector)`: change coordinate system using a 3D beta vector (all components between $-1$ and $+1$)
- `boost(vector)`: uses the dimension of the given `vector` to determine behavior
- `boostX(beta=None, gamma=None)`: supply `beta` xor `gamma`, but not both
- `boostY(beta=None, gamma=None)`: supply `beta` xor `gamma`, but not both
- `boostZ(beta=None, gamma=None)`: supply `beta` xor `gamma`, but not both
- `transform4D(obj)`: the `obj` must supply components through `obj["xx"]`, `obj["xy"]`, etc.
- `to_beta3()`: turns a `four_vector` (for `boost_p4`) into a `three_vector` (for `boost_beta3`)
- `is_timelike(tolerance=0)`
- `is_spacelike(tolerance=0)`
- `is_lightlike(tolerance=1e-5)`: note the different tolerance

**All numbers of dimensions:**

- `unit()`: note the parentheses
- `dot(vector)`: can also use the `@` operator
- `add(vector)`: can also use the `+` operator
- `subtract(vector)`: can also use the `-` operator
- `scale(factor)`: can also use the `*` operator
- `equal(vector)`: can also use the `==` operator, but consider `isclose` instead
- `not_equal(vector)`: can also use the `!=` operator, but consider `isclose` instead
- `sum()`: can also use the `numpy.sum` or `awkward.sum`, only for NumPy and Awkward vectors
- `count_nonzero()`: can also use `numpy.count_nonzero` or `awkward.count_nonzero`, only for NumPy and Awkward vectors
- `count()`: can also use `awkward.count`, only for Awkward vectors
- `isclose(vector, rtol=1e-5, atol=1e-8, equal_nan=False)`: works like [np.isclose](https://numpy.org/doc/stable/reference/generated/numpy.isclose.html); arrays also have an [allclose](https://numpy.org/doc/stable/reference/generated/numpy.allclose.html) method
- `to_VectorND(coordinates)`/`to_ND(coordinates)`: replace `N` with the required vector dimension
- `to_{coordinate-names}`: for example - `to_rhophietatau`
- `like(other)`: projects the vector into the dimensions of `other`, for example - `two_d_vector.like(three_d_vector)`

## Compiling your Python with Numba

[Numba](https://numba.pydata.org/) is a just-in-time (JIT) compiler for a mathematically relevant subset of NumPy and Python. It allows you to write fast code without leaving the Python environment. The drawback of Numba is that it can only compile code blocks involving objects and functions that it recognizes.

The Vector library includes extensions to inform Numba about vector objects, vector NumPy arrays, and vector Awkward Arrays. At the time of writing, the implementation of vector NumPy arrays is incomplete due to [numba/numba#6148](https://github.com/numba/numba/pull/6148).

For instance, consider the following function:

```python
@nb.njit
def compute_mass(v1, v2):
    return (v1 + v2).mass


compute_mass(vector.obj(px=1, py=2, pz=3, E=4), vector.obj(px=-1, py=-2, pz=-3, E=4))
```

When the two `MomentumObject4D` objects are passed as arguments, Numba recognizes them and replaces the Python objects with low-level structs. When it compiles the function, it recognizes `+` as the 4D `add` function and recognizes `.mass` as the `tau` component of the result.

Although this demonstrates that Numba can manipulate vector objects, there is no performance advantage (and a likely disadvantage) to compiling a calculation on just a few vectors. The advantage comes when many vectors are involved, in arrays.

```python
# This is still not a large number. You want millions.
array = vector.awk(
    [
        [
            dict(
                {x: np.random.normal(0, 1) for x in ("px", "py", "pz")},
                E=np.random.normal(10, 1),
            )
            for inner in range(np.random.poisson(1.5))
        ]
        for outer in range(50)
    ]
)


@nb.njit
def compute_masses(array):
    out = np.empty(len(array), np.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


compute_masses(array)
```

## Talks about vector

- 9th October 2023 - [What’s new with Vector? First major release is out!](https://indi.to/35ym5) - [PyHEP 2023 (virtual)](https://indico.cern.ch/event/1252095/) [🎥](https://www.youtube.com/watch?v=JHEAb2R3xzE&list=PLKZ9c4ONm-VlAorAG8kR09ZqhMfHiH2LJ&index=10)
- 13th September 2022 - [Constructing HEP vectors and analyzing HEP data using Vector](https://indi.to/bPmMc) - [PyHEP 2022 (virtual)](https://indico.cern.ch/event/1150631/) [🎥](https://www.youtube.com/watch?v=4iveMzrbe7s&list=PLKZ9c4ONm-VkohKG-skzEG_gklMaSgaO7&index=15)
- 20th July 2022 - [Analysis Grand Challenge / HEP Scientific Python Ecosystem](https://indico.cern.ch/event/1151329/timetable/#3-analysis-grand-challenge-hep) - [DANCE/CoDaS@Snowmass 2022 computational and data science software training](https://indico.cern.ch/event/1151329/)
- 25th April 2022 - [Foundation libraries (uproot, awkward, hist, mplhep)](https://indico.cern.ch/event/1126109/contributions/4780138/) - [IRIS-HEP AGC Tools 2022 Workshop](https://indico.cern.ch/event/1126109/) [🎥](https://www.youtube.com/watch?v=O9KvsDMKOmY)
- 3rd November 2021 - [Data handling: uproot, awkward & vector](https://indico.cern.ch/event/1076231/contributions/4560398/) - [IRIS-HEP AGC Tools 2021 Workshop](https://indico.cern.ch/event/1076231/) [🎥](https://indico.cern.ch/event/1076231/contributions/4560398/attachments/2338579/4017718/agc_uproot_awk.mp4)

### Status as of November 17, 2023

First major release of vector is out and the package has reached a stable position. The work is spearheaded by bug reports and feature requests created on GitHub. It can only be improved by your feedback!

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
  <tr>
    <td align="center"><a href="https://github.com/jpivarski"><img src="https://avatars.githubusercontent.com/u/1852447?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jim Pivarski</b></sub></a><br /><a href="#maintenance-jpivarski" title="Maintenance">🚧</a> <a href="https://github.com/scikit-hep/vector/commits?author=jpivarski" title="Code">💻</a> <a href="https://github.com/scikit-hep/vector/commits?author=jpivarski" title="Documentation">📖</a></td>
    <td align="center"><a href="https://github.com/henryiii"><img src="https://avatars.githubusercontent.com/u/4616906?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Henry Schreiner</b></sub></a><br /><a href="#maintenance-henryiii" title="Maintenance">🚧</a> <a href="https://github.com/scikit-hep/vector/commits?author=henryiii" title="Code">💻</a> <a href="https://github.com/scikit-hep/vector/commits?author=henryiii" title="Documentation">📖</a></td>
    <td align="center"><a href="https://github.com/eduardo-rodrigues"><img src="https://avatars.githubusercontent.com/u/5013581?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eduardo Rodrigues</b></sub></a><br /><a href="#maintenance-eduardo-rodrigues" title="Maintenance">🚧</a> <a href="https://github.com/scikit-hep/vector/commits?author=eduardo-rodrigues" title="Code">💻</a> <a href="https://github.com/scikit-hep/vector/commits?author=eduardo-rodrigues" title="Documentation">📖</a></td>
    <td align="center"><a href="http://lovelybuggies.com.cn/"><img src="https://avatars.githubusercontent.com/u/29083689?v=4?s=100" width="100px;" alt=""/><br /><sub><b>N!no</b></sub></a><br /><a href="https://github.com/scikit-hep/vector/commits?author=LovelyBuggies" title="Documentation">📖</a></td>
    <td align="center"><a href="https://github.com/pfackeldey"><img src="https://avatars.githubusercontent.com/u/18463582?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peter Fackeldey</b></sub></a><br /><a href="https://github.com/scikit-hep/vector/commits?author=pfackeldey" title="Documentation">📖</a></td>
    <td align="center"><a href="https://github.com/kreczko"><img src="https://avatars.githubusercontent.com/u/1213276?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luke Kreczko</b></sub></a><br /><a href="https://github.com/scikit-hep/vector/commits?author=kreczko" title="Code">💻</a></td>
    <td align="center"><a href="https://github.com/nsmith-"><img src="https://avatars.githubusercontent.com/u/6587412?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nicholas Smith</b></sub></a><br /><a href="#ideas-nsmith-" title="Ideas, Planning, & Feedback">🤔</a></td>
  </tr>
  <tr>
    <td align="center"><a href="https://github.com/mayou36"><img src="https://avatars.githubusercontent.com/u/17454848?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonas Eschle</b></sub></a><br /><a href="#ideas-mayou36" title="Ideas, Planning, & Feedback">🤔</a></td>
  </tr>
</table>

<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the
[all-contributors](https://github.com/all-contributors/all-contributors)
specification. Contributions of any kind welcome! See
[CONTRIBUTING.md](./.github/CONTRIBUTING.md) for information on setting up a
development environment.

## Acknowledgements

This library was primarily developed by Jim Pivarski, Henry Schreiner, and Eduardo Rodrigues.

Support for this work was provided by the National Science Foundation cooperative agreement OAC-1836650 (IRIS-HEP) and OAC-1450377 (DIANA/HEP). Any opinions, findings, conclusions or recommendations expressed in this material are those of the authors and do not necessarily reflect the views of the National Science Foundation.

[actions-badge]: https://github.com/scikit-hep/vector/actions/workflows/ci.yml/badge.svg
[actions-link]: https://github.com/scikit-hep/vector/actions
[black-badge]: https://img.shields.io/badge/code%20style-black-000000.svg
[black-link]: https://github.com/psf/black
[codecov-badge]: https://codecov.io/gh/scikit-hep/vector/branch/main/graph/badge.svg?token=YBv60ueORQ
[codecov-link]: https://codecov.io/gh/scikit-hep/vector
[conda-version]: https://img.shields.io/conda/vn/conda-forge/vector.svg
[conda-link]: https://github.com/conda-forge/vector-feedstock
[github-discussions-badge]: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
[github-discussions-link]: https://github.com/scikit-hep/vector/discussions
[gitter-badge]: https://badges.gitter.im/Scikit-HEP/vector.svg
[gitter-link]: https://gitter.im/Scikit-HEP/vector?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[license-badge]: https://img.shields.io/badge/License-BSD_3--Clause-blue.svg
[license-link]: https://opensource.org/licenses/BSD-3-Clause
[pre-commit-badge]: https://results.pre-commit.ci/badge/github/scikit-hep/vector/develop.svg
[pre-commit-link]: https://results.pre-commit.ci/repo/github/scikit-hep/vector
[pypi-link]: https://pypi.org/project/vector/
[pypi-platforms]: https://img.shields.io/pypi/pyversions/vector
[pypi-version]: https://badge.fury.io/py/vector.svg
[rtd-badge]: https://readthedocs.org/projects/vector/badge/?version=latest
[rtd-link]: https://vector.readthedocs.io/en/latest/?badge=latest
[sk-badge]: https://scikit-hep.org/assets/images/Scikit--HEP-Project-blue.svg
[sk-link]: https://scikit-hep.org/
[zenodo-badge]: https://zenodo.org/badge/DOI/10.5281/zenodo.7054478.svg
[zenodo-link]: https://doi.org/10.5281/zenodo.7054478

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "vector",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "The Scikit-HEP admins <scikit-hep-admins@googlegroups.com>",
    "keywords": "vector",
    "author": null,
    "author_email": "\"Jim Pivarski, Henry Schreiner, Eduardo Rodrigues\" <eduardo.rodrigues@cern.ch>",
    "download_url": "https://files.pythonhosted.org/packages/e5/40/cdae41b890630e8800d2df341d80ee3feb78f2566cb4b019adabf327cf79/vector-1.4.0.tar.gz",
    "platform": null,
    "description": "<img alt=\"Vector logo\" width=\"433\" src=\"https://raw.githubusercontent.com/scikit-hep/vector/main/docs/_images/vector-logo.png\"/>\n\n# Vector: arrays of 2D, 3D, and Lorentz vectors\n\n[![Actions Status][actions-badge]][actions-link]\n[![Documentation Status][rtd-badge]][rtd-link]\n[![pre-commit.ci status][pre-commit-badge]][pre-commit-link]\n[![codecov percentage][codecov-badge]][codecov-link]\n[![GitHub Discussion][github-discussions-badge]][github-discussions-link]\n[![Gitter][gitter-badge]][gitter-link]\n[![Code style: black][black-badge]][black-link]\n\n[![PyPI platforms][pypi-platforms]][pypi-link]\n[![PyPI version][pypi-version]][pypi-link]\n[![Conda latest release][conda-version]][conda-link]\n[![DOI][zenodo-badge]][zenodo-link]\n[![LICENSE][license-badge]][license-link]\n[![Scikit-HEP][sk-badge]][sk-link]\n\nVector is a Python 3.8+ library (Python 3.6 and 3.7 supported till `v0.9.0` and `v1.0.0`, respectively) for 2D, 3D, and [Lorentz vectors](https://en.wikipedia.org/wiki/Special_relativity#Physics_in_spacetime), especially _arrays of vectors_, to solve common physics problems in a NumPy-like way.\n\nMain features of Vector:\n\n- Pure Python with NumPy as its only dependency. This makes it easier to install.\n- Vectors may be represented in a variety of coordinate systems: Cartesian, cylindrical, pseudorapidity, and any combination of these with time or proper time for Lorentz vectors. In all, there are 12 coordinate systems: {_x_-_y_ vs _\u03c1_-_\u03c6_ in the azimuthal plane} \u00d7 {_z_ vs _\u03b8_ vs _\u03b7_ longitudinally} \u00d7 {_t_ vs _\u03c4_ temporally}.\n- Uses names and conventions set by [ROOT](https://root.cern/)'s [TLorentzVector](https://root.cern.ch/doc/master/classTLorentzVector.html) and [Math::LorentzVector](https://root.cern.ch/doc/master/classROOT_1_1Math_1_1LorentzVector.html), as well as [scikit-hep/math](https://github.com/scikit-hep/scikit-hep/tree/master/skhep/math), [uproot-methods TLorentzVector](https://github.com/scikit-hep/uproot3-methods/blob/master/uproot3_methods/classes/TLorentzVector.py), [henryiii/hepvector](https://github.com/henryiii/hepvector), and [coffea.nanoevents.methods.vector](https://coffeateam.github.io/coffea/modules/coffea.nanoevents.methods.vector.html).\n- Implemented on a variety of backends:\n  - pure Python objects\n  - [SymPy](https://www.sympy.org/en/index.html) vectors\n  - NumPy arrays of vectors (as a [structured array](https://numpy.org/doc/stable/user/basics.rec.html) subclass)\n  - [Awkward Arrays](https://awkward-array.org/) of vectors\n  - potential for more: CuPy, TensorFlow, Torch, JAX...\n- Awkward backend also implemented in [Numba](https://numba.pydata.org/) for JIT-compiled calculations on vectors.\n- [JAX](https://awkward-array.org/doc/main/user-guide/how-to-specialize-differentiate-jax.html) and [Dask](https://dask-awkward.readthedocs.io/en/stable/) support through Awkward Arrays.\n- Distinction between geometrical vectors, which have a minimum of attribute and method names, and vectors representing momentum, which have synonyms like `pt` = `rho`, `energy` = `t`, `mass` = `tau`.\n\n## Installation\n\nTo install, use `pip install vector` or your favorite way to install in an environment.\n\n## Overview\n\nThis overview is based on the [documentation here](https://vector.readthedocs.io/en/develop/usage/intro.html).\n\n```python\nimport vector\nimport numpy as np\nimport awkward as ak  # at least version 1.2.0\nimport numba as nb\n```\n\n## Constructing a vector or an array of vectors\n\nThe easiest way to create one or many vectors is with a helper function:\n\n- `vector.obj` to make a pure Python vector object,\n- `vector.arr` to make a NumPy array of vectors (or `array`, lowercase, like `np.array`),\n- `vector.awk` to make an Awkward Array of vectors (or `Array`, uppercase, like `ak.Array`).\n- `vector.zip` to make an Awkward Array of vectors (similar to `ak.zip`)\n\n### Pure Python vectors\n\nYou can directly use the `VectorObject` and `MomentumObject` classes to construct object type vectors:\n\n```python\nvector.VectorObject2D(x=1.1, y=2.2)\nvector.MomentumObject3D(px=1.1, py=2.2, pz=3.3)\nvector.VectorObject4D(x=1.1, y=2.2, eta=3.3, tau=4.4)\n```\n\nand so on for every class.\n\nOr, you can use a single wrapper function to construct all possible combinations of\nobject type vectors:\n\n```python\n# Cartesian 2D vector\nvector.obj(x=3, y=4)\n# same in polar coordinates\nvector.obj(rho=5, phi=0.9273)\n# use \"isclose\" unless they are exactly equal\nvector.obj(x=3, y=4).isclose(vector.obj(rho=5, phi=0.9273))\n# Cartesian 3D vector\nvector.obj(x=3, y=4, z=-2)\n# Cartesian 4D vector\nvector.obj(x=3, y=4, z=-2, t=10)\n# in rho-phi-eta-t cylindrical coordinates\nvector.obj(rho=5, phi=0.9273, eta=-0.39, t=10)\n# use momentum-synonyms to get a momentum vector\nvector.obj(pt=5, phi=0.9273, eta=-0.39, E=10)\nvector.obj(rho=5, phi=0.9273, eta=-0.39, t=10) == vector.obj(\n    pt=5, phi=0.9273, eta=-0.390035, E=10\n)\n# geometrical vectors have to use geometrical names (\"tau\", not \"mass\")\nvector.obj(rho=5, phi=0.9273, eta=-0.39, t=10).tau\n# momentum vectors can use momentum names (as well as geometrical ones)\nvector.obj(pt=5, phi=0.9273, eta=-0.39, E=10).mass\n# any combination of azimuthal, longitudinal, and temporal coordinates is allowed\nvector.obj(pt=5, phi=0.9273, theta=1.9513, mass=8.4262)\nvector.obj(x=3, y=4, z=-2, t=10).isclose(\n    vector.obj(pt=5, phi=0.9273, theta=1.9513, mass=8.4262)\n)\n\n# Test instance type for any level of granularity.\n(\n    # is a vector or array of vectors\n    isinstance(vector.obj(x=1.1, y=2.2), vector.Vector),\n    # is 2D (not 3D or 4D)\n    isinstance(vector.obj(x=1.1, y=2.2), vector.Vector2D),\n    # is a vector object (not an array)\n    isinstance(vector.obj(x=1.1, y=2.2), vector.VectorObject),\n    # has momentum synonyms\n    isinstance(vector.obj(px=1.1, py=2.2), vector.Momentum),\n    # has transverse plane (2D, 3D, or 4D)\n    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4), vector.Planar),\n    # has all spatial coordinates (3D or 4D)\n    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4), vector.Spatial),\n    # has temporal coordinates (4D)\n    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4), vector.Lorentz),\n    # azimuthal coordinate type\n    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4).azimuthal, vector.AzimuthalXY),\n    # longitudinal coordinate type\n    isinstance(\n        vector.obj(x=1.1, y=2.2, z=3.3, t=4.4).longitudinal, vector.LongitudinalZ\n    ),\n    # temporal coordinate type\n    isinstance(vector.obj(x=1.1, y=2.2, z=3.3, t=4.4).temporal, vector.TemporalT),\n)\n```\n\nThe allowed keyword arguments for 2D vectors are:\n\n- `x` and `y` for Cartesian azimuthal coordinates,\n- `px` and `py` for momentum,\n- `rho` and `phi` for polar azimuthal coordinates,\n- `pt` and `phi` for momentum.\n\nFor 3D vectors, you need the above and:\n\n- `z` for the Cartesian longitudinal coordinate,\n- `pz` for momentum,\n- `theta` for the spherical polar angle (from $0$ to $\\pi$, inclusive),\n- `eta` for pseudorapidity, which is a kind of spherical polar angle.\n\nFor 4D vectors, you need the above and:\n\n- `t` for the Cartesian temporal coordinate,\n- `E` or `energy` to get four-momentum,\n- `tau` for the \"proper time\" (temporal coordinate in the vector's rest coordinate system),\n- `M` or `mass` to get four-momentum.\n\nSince momentum vectors have momentum-synonyms _in addition_ to the geometrical names, any momentum-synonym will make the whole vector a momentum vector.\n\nIf you want to bypass the dimension and coordinate system inference through keyword arguments (e.g. for static typing), you can use specialized constructors:\n\n```python\nvector.VectorObject2D.from_xy(1.1, 2.2)\nvector.MomentumObject3D.from_rhophiz(1.1, 2.2, 3.3)\nvector.VectorObject4D.from_xyetatau(1.1, 2.2, 3.3, 4.4)\n```\n\nand so on, for all combinations of azimuthal, longitudinal, and temporal coordinates, geometric and momentum-flavored.\n\n### SymPy vectors\n\n> **Note:** Operations on SymPy vectors are only 100% compatible with numeric vectors (Python, NumPy, and Awkward backends) if the vectors are positive time-like, that is, if `t**2 > x**2 + y**2 + z**2`. The space-like and negative time-like cases have different sign conventions.\n\nYou can directly use the `VectorSympy` and `MomentumSympy` classes to construct object type vectors:\n\n```python\nimport sympy\n\nx, y, z, t, px, py, pz, eta, tau = sympy.symbols(\n    \"x y z t px py pz eta tau\",\n    real=True,  # see sympy assumptions to add more restrictions on the symbols\n)\nvector.VectorSympy2D(x=x, y=y)\nvector.MomentumSympy3D(px=px, py=py, pz=pz)\nvector.VectorSympy4D(x=x, y=y, eta=eta, tau=tau)\n```\n\nand so on for every class.\n\n```python\n# Test instance type for any level of granularity.\n(\n    # is a vector or array of vectors\n    isinstance(vector.VectorSympy2D(x=x, y=y), vector.Vector),\n    # is 2D (not 3D or 4D)\n    isinstance(vector.VectorSympy2D(x=x, y=y), vector.Vector2D),\n    # is a sympy vector (not an array)\n    isinstance(vector.VectorSympy2D(x=x, y=y), vector.VectorSympy),\n    # has momentum synonyms\n    isinstance(vector.MomentumSympy2D(px=px, py=py), vector.Momentum),\n    # has transverse plane (2D, 3D, or 4D)\n    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t), vector.Planar),\n    # has all spatial coordinates (3D or 4D)\n    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t), vector.Spatial),\n    # has temporal coordinates (4D)\n    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t), vector.Lorentz),\n    # azimuthal coordinate type\n    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t).azimuthal, vector.AzimuthalXY),\n    # longitudinal coordinate type\n    isinstance(\n        vector.VectorSympy4D(x=x, y=y, z=z, t=t).longitudinal, vector.LongitudinalZ\n    ),\n    # temporal coordinate type\n    isinstance(vector.VectorSympy4D(x=x, y=y, z=z, t=t).temporal, vector.TemporalT),\n)\n```\n\nSince `VectorSympy2D`, `VectorSympy3D`, `VectorSympy4D`, and their momentum equivalents operate on SymPy expressions, all of the normal SymPy methods and functions work on the results, coordinates, and the vectors.\n\n```python\nsympy.init_session()  # latex printing\n\nv1 = vector.VectorSympy2D(x=x, y=y)\nsympy.Eq(v1.rho, sympy.sqrt(x**2 + y**2))\n\nv2 = vector.VectorSympy4D(x=x, y=y, z=z, t=t)\nv2.to_rhophithetatau().tau\n\nvalues = {x: 3, y: 2, z: 1, t: 10}  # t**2 > x**2 + y**2 + z**2\nv2.is_timelike()\nv2.is_timelike().subs(values)\n\nv2.to_rhophithetatau().tau.subs(values).evalf()\n\nv2.boost(v2.to_beta3())\nv2.boost(v2.to_beta3()).t\nv2.boost(v2.to_beta3()).t.simplify()\nv2.boost(v2.to_beta3()).t.subs(values)\nv2.boost(v2.to_beta3()).t.subs(values).evalf()\n```\n\nAll of the keyword arguments and rules that apply to `vector.obj` construction apply to `vector.VectorSympyND` and `vector.MomentumObjectND` objects.\n\n### NumPy arrays of vectors\n\nYou can directly use the `VectorNumpy` classes to construct object type vectors:\n\n```python\n# NumPy-like arguments (literally passed through to NumPy)\nvector.VectorNumpy2D(\n    [(1.1, 2.1), (1.2, 2.2), (1.3, 2.3), (1.4, 2.4), (1.5, 2.5)],\n    dtype=[(\"x\", float), (\"y\", float)],\n)\n\n# Pandas-like arguments (dict from names to column arrays)\nvector.VectorNumpy2D({\"x\": [1.1, 1.2, 1.3, 1.4, 1.5], \"y\": [2.1, 2.2, 2.3, 2.4, 2.5]})\n\n# As with objects, the coordinate system and dimension is taken from the names of the fields.\nvector.VectorNumpy4D(\n    {\n        \"x\": [1.1, 1.2, 1.3, 1.4, 1.5],\n        \"y\": [2.1, 2.2, 2.3, 2.4, 2.5],\n        \"z\": [3.1, 3.2, 3.3, 3.4, 3.5],\n        \"t\": [4.1, 4.2, 4.3, 4.4, 4.5],\n    }\n)\n```\n\nand so on for every class.\n\nOr, you can use a single wrapper function to construct all possible combinations of\nNumPy type vectors:\n\n```python\n# NumPy-like arguments (literally passed through to NumPy)\nvector.array(\n    [(1.1, 2.1), (1.2, 2.2), (1.3, 2.3), (1.4, 2.4), (1.5, 2.5)],\n    dtype=[(\"x\", float), (\"y\", float)],\n)\n\n# Pandas-like arguments (dict from names to column arrays)\nvector.array({\"x\": [1.1, 1.2, 1.3, 1.4, 1.5], \"y\": [2.1, 2.2, 2.3, 2.4, 2.5]})\n\n# As with objects, the coordinate system and dimension is taken from the names of the fields.\nvector.array(\n    {\n        \"x\": [1.1, 1.2, 1.3, 1.4, 1.5],\n        \"y\": [2.1, 2.2, 2.3, 2.4, 2.5],\n        \"z\": [3.1, 3.2, 3.3, 3.4, 3.5],\n        \"t\": [4.1, 4.2, 4.3, 4.4, 4.5],\n    }\n)\n\nvector.array(\n    {\n        \"pt\": [1.1, 1.2, 1.3, 1.4, 1.5],\n        \"phi\": [2.1, 2.2, 2.3, 2.4, 2.5],\n        \"eta\": [3.1, 3.2, 3.3, 3.4, 3.5],\n        \"M\": [4.1, 4.2, 4.3, 4.4, 4.5],\n    }\n)\n```\n\nExisting NumPy arrays can be viewed as arrays of vectors, but it needs to be a [structured array](https://numpy.org/doc/stable/user/basics.rec.html) with recognized field names.\n\n```python\nnp.arange(0, 24, 0.1).view(  # NumPy array\n    [\n        (\"x\", float),\n        (\"y\", float),\n        (\"z\", float),\n        (\"t\", float),\n    ]  # interpret groups of four values as named fields\n).view(\n    vector.VectorNumpy4D\n)  # give it vector properties and methods\n```\n\nSince `VectorNumpy2D`, `VectorNumpy3D`, `VectorNumpy4D`, and their momentum equivalents are NumPy array subclasses, all of the normal NumPy methods and functions work on them.\n\n```python\nnp.arange(0, 24, 0.1).view(\n    [(\"x\", float), (\"y\", float), (\"z\", float), (\"t\", float)]\n).view(vector.VectorNumpy4D).reshape(6, 5, 2)\n```\n\nAll of the keyword arguments and rules that apply to `vector.obj` construction apply to `vector.arr` dtypes.\n\nGeometrical names are used in the dtype, even if momentum-synonyms are used in construction.\n\n```python\nvector.arr({\"px\": [1, 2, 3, 4], \"py\": [1.1, 2.2, 3.3, 4.4], \"pz\": [0.1, 0.2, 0.3, 0.4]})\n```\n\n### Awkward Arrays of vectors\n\n[Awkward Arrays](https://awkward-array.org/) are arrays with more complex data structures than NumPy allows, such as variable-length lists, nested records, missing and even heterogeneous data (multiple data types: use sparingly).\n\nThe `vector.awk` function behaves exactly like the [ak.Array](https://awkward-array.readthedocs.io/en/latest/_auto/ak.Array.html) constructor, except that it makes arrays of vectors.\n\n```python\nvector.awk(\n    [\n        [{\"x\": 1, \"y\": 1.1, \"z\": 0.1}, {\"x\": 2, \"y\": 2.2, \"z\": 0.2}],\n        [],\n        [{\"x\": 3, \"y\": 3.3, \"z\": 0.3}],\n        [\n            {\"x\": 4, \"y\": 4.4, \"z\": 0.4},\n            {\"x\": 5, \"y\": 5.5, \"z\": 0.5},\n            {\"x\": 6, \"y\": 6.6, \"z\": 0.6},\n        ],\n    ]\n)\n```\n\nIf you want _any_ records named \"`Vector2D`\", \"`Vector3D`\", \"`Vector4D`\", \"`Momentum2D`\", \"`Momentum3D`\", or \"`Momentum4D`\" to be interpreted as vectors, register the behaviors globally.\n\n```python\nvector.register_awkward()\n\nak.Array(\n    [\n        [{\"x\": 1, \"y\": 1.1, \"z\": 0.1}, {\"x\": 2, \"y\": 2.2, \"z\": 0.2}],\n        [],\n        [{\"x\": 3, \"y\": 3.3, \"z\": 0.3}],\n        [\n            {\"x\": 4, \"y\": 4.4, \"z\": 0.4},\n            {\"x\": 5, \"y\": 5.5, \"z\": 0.5},\n            {\"x\": 6, \"y\": 6.6, \"z\": 0.6},\n        ],\n    ],\n    with_name=\"Vector3D\",\n)\n```\n\nAll of the keyword arguments and rules that apply to `vector.obj` construction apply to `vector.awk` field names.\n\nFinally, the `VectorAwkward` mixins can be subclassed to create custom vector classes. The awkward behavior classes and projections must be named as `*Array`. For example, `coffea` uses the following names - `TwoVectorArray`, `ThreeVectorArray`, `PolarTwoVectorArray`, `SphericalThreeVectorArray`, ...\n\n## Vector properties\n\nAny geometrical coordinate can be computed from vectors in any coordinate system; they'll be provided or computed as needed.\n\n```python\nvector.obj(x=3, y=4).rho\nvector.obj(rho=5, phi=0.9273).x\nvector.obj(rho=5, phi=0.9273).y\nvector.obj(x=1, y=2, z=3).theta\nvector.obj(x=1, y=2, z=3).eta\n```\n\nSome properties are not coordinates, but derived from them.\n\n```python\nvector.obj(x=1, y=2, z=3).costheta\nvector.obj(x=1, y=2, z=3).mag  # spatial magnitude\nvector.obj(x=1, y=2, z=3).mag2  # spatial magnitude squared\n```\n\nThese properties are provided because they can be computed faster or with more numerical stability in different coordinate systems. For instance, the magnitude ignores `phi` in polar coordinates.\n\n```python\nvector.obj(rho=3, phi=0.123456789, z=4).mag2\n```\n\nMomentum vectors have geometrical properties as well as their momentum-synonyms.\n\n```python\nvector.obj(px=3, py=4).rho\nvector.obj(px=3, py=4).pt\nvector.obj(x=1, y=2, z=3, E=4).tau\nvector.obj(x=1, y=2, z=3, E=4).mass\n```\n\nHere's the key thing: _arrays of vectors return arrays of coordinates_.\n\n```python\nvector.arr(\n    {\n        \"x\": [1.0, 2.0, 3.0, 4.0, 5.0],\n        \"y\": [1.1, 2.2, 3.3, 4.4, 5.5],\n        \"z\": [0.1, 0.2, 0.3, 0.4, 0.5],\n    }\n).theta\n\nvector.awk(\n    [\n        [{\"x\": 1, \"y\": 1.1, \"z\": 0.1}, {\"x\": 2, \"y\": 2.2, \"z\": 0.2}],\n        [],\n        [{\"x\": 3, \"y\": 3.3, \"z\": 0.3}],\n        [{\"x\": 4, \"y\": 4.4, \"z\": 0.4}, {\"x\": 5, \"y\": 5.5, \"z\": 0.5}],\n    ]\n).theta\n\n# Make a large, random NumPy array of 3D momentum vectors.\narray = (\n    np.random.normal(0, 1, 150)\n    .view([(x, float) for x in (\"x\", \"y\", \"z\")])\n    .view(vector.MomentumNumpy3D)\n    .reshape(5, 5, 2)\n)\n\n# Get the transverse momentum of each one.\narray.pt\n\n# The array and its components have the same shape.\narray.shape\narray.pt.shape\n\n# Make a large, random Awkward Array of 3D momentum vectors.\narray = vector.awk(\n    [\n        [\n            {x: np.random.normal(0, 1) for x in (\"px\", \"py\", \"pz\")}\n            for inner in range(np.random.poisson(1.5))\n        ]\n        for outer in range(50)\n    ]\n)\n\n# Get the transverse momentum of each one, in the same nested structure.\narray.pt\n\n# The array and its components have the same list lengths (and can therefore be used together in subsequent calculations).\nak.num(array)\nak.num(array.pt)\n```\n\n## Vector methods\n\nVector methods require arguments (in parentheses), which may be scalars or other vectors, depending on the calculation.\n\n```python\nvector.obj(x=3, y=4).rotateZ(0.1)\nvector.obj(rho=5, phi=0.4).rotateZ(0.1)\n\n# Broadcasts a scalar rotation angle of 0.5 to all elements of the NumPy array.\nprint(\n    vector.arr({\"rho\": [1, 2, 3, 4, 5], \"phi\": [0.1, 0.2, 0.3, 0.4, 0.5]}).rotateZ(0.5)\n)\n\n# Matches each rotation angle to an element of the NumPy array.\nprint(\n    vector.arr({\"rho\": [1, 2, 3, 4, 5], \"phi\": [0.1, 0.2, 0.3, 0.4, 0.5]}).rotateZ(\n        np.array([0.1, 0.2, 0.3, 0.4, 0.5])\n    )\n)\n\n# Broadcasts a scalar rotation angle of 0.5 to all elements of the Awkward Array.\nprint(\n    vector.awk(\n        [[{\"rho\": 1, \"phi\": 0.1}, {\"rho\": 2, \"phi\": 0.2}], [], [{\"rho\": 3, \"phi\": 0.3}]]\n    ).rotateZ(0.5)\n)\n\n# Broadcasts a rotation angle of 0.1 to both elements of the first list, 0.2 to the empty list, and 0.3 to the only element of the last list.\nprint(\n    vector.awk(\n        [[{\"rho\": 1, \"phi\": 0.1}, {\"rho\": 2, \"phi\": 0.2}], [], [{\"rho\": 3, \"phi\": 0.3}]]\n    ).rotateZ([0.1, 0.2, 0.3])\n)\n\n# Matches each rotation angle to an element of the Awkward Array.\nprint(\n    vector.awk(\n        [[{\"rho\": 1, \"phi\": 0.1}, {\"rho\": 2, \"phi\": 0.2}], [], [{\"rho\": 3, \"phi\": 0.3}]]\n    ).rotateZ([[0.1, 0.2], [], [0.3]])\n)\n```\n\nSome methods are equivalent to binary operators.\n\n```python\nvector.obj(x=3, y=4).scale(10)\nvector.obj(x=3, y=4) * 10\n10 * vector.obj(x=3, y=4)\nvector.obj(rho=5, phi=0.5) * 10\n```\n\nSome methods involve more than one vector.\n\n```python\nvector.obj(x=1, y=2).add(vector.obj(x=5, y=5))\nvector.obj(x=1, y=2) + vector.obj(x=5, y=5)\nvector.obj(x=1, y=2).dot(vector.obj(x=5, y=5))\nvector.obj(x=1, y=2) @ vector.obj(x=5, y=5)\n```\n\nThe vectors can use different coordinate systems. Conversions are necessary, but minimized for speed and numeric stability.\n\n```python\n# both are Cartesian, dot product is exact\nvector.obj(x=3, y=4) @ vector.obj(x=6, y=8)\n# one is polar, dot product is approximate\nvector.obj(rho=5, phi=0.9273) @ vector.obj(x=6, y=8)\n# one is polar, dot product is approximate\nvector.obj(x=3, y=4) @ vector.obj(rho=10, phi=0.9273)\n# both are polar, a formula that depends on phi differences is used\nvector.obj(rho=5, phi=0.9273) @ vector.obj(rho=10, phi=0.9273)\n```\n\nIn Python, some \"operators\" are actually built-in functions, such as `abs`.\n\n```python\nabs(vector.obj(x=3, y=4))\n```\n\nNote that `abs` returns\n\n- `rho` for 2D vectors\n- `mag` for 3D vectors\n- `tau` (`mass`) for 4D vectors\n\nUse the named properties when you want magnitude in a specific number of dimensions; use `abs` when you want the magnitude for any number of dimensions.\n\nThe vectors can be from different backends. Normal rules for broadcasting Python numbers, NumPy arrays, and Awkward Arrays apply.\n\n```python\nvector.arr({\"x\": [1, 2, 3, 4, 5], \"y\": [0.1, 0.2, 0.3, 0.4, 0.5]}) + vector.obj(\n    x=10, y=5\n)\n\n(\n    vector.awk(\n        [  # an Awkward Array of vectors\n            [{\"x\": 1, \"y\": 1.1}, {\"x\": 2, \"y\": 2.2}],\n            [],\n            [{\"x\": 3, \"y\": 3.3}],\n            [{\"x\": 4, \"y\": 4.4}, {\"x\": 5, \"y\": 5.5}],\n        ]\n    )\n    + vector.obj(x=10, y=5)  # and a single vector object\n)\n\n(\n    vector.awk(\n        [  # an Awkward Array of vectors\n            [{\"x\": 1, \"y\": 1.1}, {\"x\": 2, \"y\": 2.2}],\n            [],\n            [{\"x\": 3, \"y\": 3.3}],\n            [{\"x\": 4, \"y\": 4.4}, {\"x\": 5, \"y\": 5.5}],\n        ]\n    )\n    + vector.arr(\n        {\"x\": [4, 3, 2, 1], \"y\": [0.1, 0.1, 0.1, 0.1]}\n    )  # and a NumPy array of vectors\n)\n```\n\nSome operations are defined for 2D or 3D vectors, but are usable on higher-dimensional vectors because the additional components can be ignored or are passed through unaffected.\n\n```python\n# deltaphi is a planar operation (defined on the transverse plane)\nvector.obj(rho=1, phi=0.5).deltaphi(vector.obj(rho=2, phi=0.3))\n# but we can use it on 3D vectors\nvector.obj(rho=1, phi=0.5, z=10).deltaphi(vector.obj(rho=2, phi=0.3, theta=1.4))\n# and 4D vectors\nvector.obj(rho=1, phi=0.5, z=10, t=100).deltaphi(\n    vector.obj(rho=2, phi=0.3, theta=1.4, tau=1000)\n)\n# and mixed dimensionality\nvector.obj(rho=1, phi=0.5).deltaphi(vector.obj(rho=2, phi=0.3, theta=1.4, tau=1000))\n```\n\nThis is especially useful for giving 4D vectors all the capabilities of 3D vectors.\n\n```python\nvector.obj(x=1, y=2, z=3).rotateX(np.pi / 4)\nvector.obj(x=1, y=2, z=3, tau=10).rotateX(np.pi / 4)\nvector.obj(pt=1, phi=1.3, eta=2).deltaR(vector.obj(pt=2, phi=0.3, eta=1))\nvector.obj(pt=1, phi=1.3, eta=2, mass=5).deltaR(\n    vector.obj(pt=2, phi=0.3, eta=1, mass=10)\n)\n```\n\nFor a few operations - `+`, `-`, `==`, `!=`, ... - the dimension of the vectors should be equal. This can be achieved by using the `like` method, `to_{coordinate_name}` methods, `to_Vector*D` methods. The `to_Vector*D` methods provide more flexibility to the users, that is, new coordinate values can be passed into the methods as named arguments.\n\n```python\nv1 = vector.obj(x=1, y=2, z=3)\nv2 = vector.obj(x=1, y=2)\n\nv1 - v2.like(v1)  # transforms v2 to v1's coordinate system (imputes z=0)\nv1.like(v2) - v2  # transforms v1 to v2's coordinate system (removes z)\nv1 - v2.to_xyz()  # transforms v2 to xyz coordinates (imputes z=0)\nv1.to_xy() - v2  # transforms v1 to xy coordinates (removes z)\nv1 - v2.to_Vector3D(z=3)  # transforms v2 to 3D (imputes z=3)\nv1.to_Vector2D() - v2  # transforms v1 to 2D (removes z)\n```\n\nSimilarly, for a few vector methods, the dimension of the input vectors are type checked strictly.\n\nFor instance, a cross-product is only defined for 3D and 7D vectors; hence, running the method on a 4D vector will error out.\n\n```python\nvector.obj(x=0.1, y=0.2, z=0.3).cross(vector.obj(x=0.4, y=0.5, z=0.6))\n```\n\nThe (current) list of properties and methods is:\n\n**Planar (2D, 3D, 4D):**\n\n- `x` (`px`)\n- `y` (`py`)\n- `rho` (`pt`): two-dimensional magnitude\n- `rho2` (`pt2`): two-dimensional magnitude squared\n- `phi`\n- `deltaphi(vector)`: difference in `phi` (signed and rectified to $-\\pi$ through $\\pi$)\n- `rotateZ(angle)`\n- `transform2D(obj)`: the `obj` must supply components through `obj[\"xx\"]`, `obj[\"xy\"]`, `obj[\"yx\"]`, `obj[\"yy\"]`\n- `is_parallel(vector, tolerance=1e-5)`: only true _if they're pointing in the same direction_\n- `is_antiparallel(vector, tolerance=1e-5)`: only true _if they're pointing in opposite directions_\n- `is_perpendicular(vector, tolerance=1e-5)`\n\n**Spatial (3D, 4D):**\n\n- `z` (`pz`)\n- `theta`\n- `eta`\n- `costheta`\n- `cottheta`\n- `mag` (`p`): three-dimensional magnitude, does not include temporal component\n- `mag2` (`p2`): three-dimensional magnitude squared\n- `cross`: cross-product (strictly 3D)\n- `deltaangle(vector)`: difference in angle (always non-negative)\n- `deltaeta(vector)`: difference in `eta` (signed)\n- `deltaR(vector)`: $\\Delta R = \\sqrt{\\Delta\\phi^2 + \\Delta\\eta^2}$\n- `deltaR2(vector)`: the above, squared\n- `rotateX(angle)`\n- `rotateY(angle)`\n- `rotate_axis(axis, angle)`: the magnitude of `axis` is ignored, but it must be at least 3D\n- `rotate_euler(phi, theta, psi, order=\"zxz\")`: the arguments are in the same order as [ROOT::Math::EulerAngles](https://root.cern.ch/doc/master/classROOT_1_1Math_1_1EulerAngles.html), and `order=\"zxz\"` agrees with ROOT's choice of conventions\n- `rotate_nautical(yaw, pitch, roll)`\n- `rotate_quaternion(u, i, j, k)`: again, the conventions match [ROOT::Math::Quaternion](https://root.cern.ch/doc/master/classROOT_1_1Math_1_1Quaternion.html).\n- `transform3D(obj)`: the `obj` must supply components through `obj[\"xx\"]`, `obj[\"xy\"]`, etc.\n- `is_parallel(vector, tolerance=1e-5)`: only true _if they're pointing in the same direction_\n- `is_antiparallel(vector, tolerance=1e-5)`: only true _if they're pointing in opposite directions_\n- `is_perpendicular(vector, tolerance=1e-5)`\n\n**Lorentz (4D only):**\n\n- `t` (`E`, `energy`): follows the [ROOT::Math::LorentzVector](https://root.cern/doc/master/LorentzVectorPage.html) behavior of treating spacelike vectors as negative `t` and negative `tau` and truncating wrong-direction timelike vectors\n- `t2` (`E2`, `energy2`)\n- `tau` (`M`, `mass`): see note above\n- `tau2` (`M2`, `mass2`)\n- `beta`: scalar(s) between $0$ (inclusive) and $1$ (exclusive, unless the vector components are infinite)\n- `deltaRapidityPhi`: $\\Delta R_{\\mbox{rapidity}} = \\Delta\\phi^2 + \\Delta \\mbox{rapidity}^2$\n- `deltaRapidityPhi2`: the above, squared\n- `gamma`: scalar(s) between $1$ (inclusive) and $\\infty$\n- `rapidity`: scalar(s) between $0$ (inclusive) and $\\infty$\n- `boost_p4(four_vector)`: change coordinate system using another 4D vector as the difference\n- `boost_beta(three_vector)`: change coordinate system using a 3D beta vector (all components between $-1$ and $+1$)\n- `boost(vector)`: uses the dimension of the given `vector` to determine behavior\n- `boostX(beta=None, gamma=None)`: supply `beta` xor `gamma`, but not both\n- `boostY(beta=None, gamma=None)`: supply `beta` xor `gamma`, but not both\n- `boostZ(beta=None, gamma=None)`: supply `beta` xor `gamma`, but not both\n- `transform4D(obj)`: the `obj` must supply components through `obj[\"xx\"]`, `obj[\"xy\"]`, etc.\n- `to_beta3()`: turns a `four_vector` (for `boost_p4`) into a `three_vector` (for `boost_beta3`)\n- `is_timelike(tolerance=0)`\n- `is_spacelike(tolerance=0)`\n- `is_lightlike(tolerance=1e-5)`: note the different tolerance\n\n**All numbers of dimensions:**\n\n- `unit()`: note the parentheses\n- `dot(vector)`: can also use the `@` operator\n- `add(vector)`: can also use the `+` operator\n- `subtract(vector)`: can also use the `-` operator\n- `scale(factor)`: can also use the `*` operator\n- `equal(vector)`: can also use the `==` operator, but consider `isclose` instead\n- `not_equal(vector)`: can also use the `!=` operator, but consider `isclose` instead\n- `sum()`: can also use the `numpy.sum` or `awkward.sum`, only for NumPy and Awkward vectors\n- `count_nonzero()`: can also use `numpy.count_nonzero` or `awkward.count_nonzero`, only for NumPy and Awkward vectors\n- `count()`: can also use `awkward.count`, only for Awkward vectors\n- `isclose(vector, rtol=1e-5, atol=1e-8, equal_nan=False)`: works like [np.isclose](https://numpy.org/doc/stable/reference/generated/numpy.isclose.html); arrays also have an [allclose](https://numpy.org/doc/stable/reference/generated/numpy.allclose.html) method\n- `to_VectorND(coordinates)`/`to_ND(coordinates)`: replace `N` with the required vector dimension\n- `to_{coordinate-names}`: for example - `to_rhophietatau`\n- `like(other)`: projects the vector into the dimensions of `other`, for example - `two_d_vector.like(three_d_vector)`\n\n## Compiling your Python with Numba\n\n[Numba](https://numba.pydata.org/) is a just-in-time (JIT) compiler for a mathematically relevant subset of NumPy and Python. It allows you to write fast code without leaving the Python environment. The drawback of Numba is that it can only compile code blocks involving objects and functions that it recognizes.\n\nThe Vector library includes extensions to inform Numba about vector objects, vector NumPy arrays, and vector Awkward Arrays. At the time of writing, the implementation of vector NumPy arrays is incomplete due to [numba/numba#6148](https://github.com/numba/numba/pull/6148).\n\nFor instance, consider the following function:\n\n```python\n@nb.njit\ndef compute_mass(v1, v2):\n    return (v1 + v2).mass\n\n\ncompute_mass(vector.obj(px=1, py=2, pz=3, E=4), vector.obj(px=-1, py=-2, pz=-3, E=4))\n```\n\nWhen the two `MomentumObject4D` objects are passed as arguments, Numba recognizes them and replaces the Python objects with low-level structs. When it compiles the function, it recognizes `+` as the 4D `add` function and recognizes `.mass` as the `tau` component of the result.\n\nAlthough this demonstrates that Numba can manipulate vector objects, there is no performance advantage (and a likely disadvantage) to compiling a calculation on just a few vectors. The advantage comes when many vectors are involved, in arrays.\n\n```python\n# This is still not a large number. You want millions.\narray = vector.awk(\n    [\n        [\n            dict(\n                {x: np.random.normal(0, 1) for x in (\"px\", \"py\", \"pz\")},\n                E=np.random.normal(10, 1),\n            )\n            for inner in range(np.random.poisson(1.5))\n        ]\n        for outer in range(50)\n    ]\n)\n\n\n@nb.njit\ndef compute_masses(array):\n    out = np.empty(len(array), np.float64)\n    for i, event in enumerate(array):\n        total = vector.obj(px=0.0, py=0.0, pz=0.0, E=0.0)\n        for vec in event:\n            total = total + vec\n        out[i] = total.mass\n    return out\n\n\ncompute_masses(array)\n```\n\n## Talks about vector\n\n- 9th October 2023 - [What\u2019s new with Vector? First major release is out!](https://indi.to/35ym5) - [PyHEP 2023 (virtual)](https://indico.cern.ch/event/1252095/) [\ud83c\udfa5](https://www.youtube.com/watch?v=JHEAb2R3xzE&list=PLKZ9c4ONm-VlAorAG8kR09ZqhMfHiH2LJ&index=10)\n- 13th September 2022 - [Constructing HEP vectors and analyzing HEP data using Vector](https://indi.to/bPmMc) - [PyHEP 2022 (virtual)](https://indico.cern.ch/event/1150631/) [\ud83c\udfa5](https://www.youtube.com/watch?v=4iveMzrbe7s&list=PLKZ9c4ONm-VkohKG-skzEG_gklMaSgaO7&index=15)\n- 20th July 2022 - [Analysis Grand Challenge / HEP Scientific Python Ecosystem](https://indico.cern.ch/event/1151329/timetable/#3-analysis-grand-challenge-hep) - [DANCE/CoDaS@Snowmass 2022 computational and data science software training](https://indico.cern.ch/event/1151329/)\n- 25th April 2022 - [Foundation libraries (uproot, awkward, hist, mplhep)](https://indico.cern.ch/event/1126109/contributions/4780138/) - [IRIS-HEP AGC Tools 2022 Workshop](https://indico.cern.ch/event/1126109/) [\ud83c\udfa5](https://www.youtube.com/watch?v=O9KvsDMKOmY)\n- 3rd November 2021 - [Data handling: uproot, awkward & vector](https://indico.cern.ch/event/1076231/contributions/4560398/) - [IRIS-HEP AGC Tools 2021 Workshop](https://indico.cern.ch/event/1076231/) [\ud83c\udfa5](https://indico.cern.ch/event/1076231/contributions/4560398/attachments/2338579/4017718/agc_uproot_awk.mp4)\n\n### Status as of November 17, 2023\n\nFirst major release of vector is out and the package has reached a stable position. The work is spearheaded by bug reports and feature requests created on GitHub. It can only be improved by your feedback!\n\n## Contributors \u2728\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tr>\n    <td align=\"center\"><a href=\"https://github.com/jpivarski\"><img src=\"https://avatars.githubusercontent.com/u/1852447?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Jim Pivarski</b></sub></a><br /><a href=\"#maintenance-jpivarski\" title=\"Maintenance\">\ud83d\udea7</a> <a href=\"https://github.com/scikit-hep/vector/commits?author=jpivarski\" title=\"Code\">\ud83d\udcbb</a> <a href=\"https://github.com/scikit-hep/vector/commits?author=jpivarski\" title=\"Documentation\">\ud83d\udcd6</a></td>\n    <td align=\"center\"><a href=\"https://github.com/henryiii\"><img src=\"https://avatars.githubusercontent.com/u/4616906?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Henry Schreiner</b></sub></a><br /><a href=\"#maintenance-henryiii\" title=\"Maintenance\">\ud83d\udea7</a> <a href=\"https://github.com/scikit-hep/vector/commits?author=henryiii\" title=\"Code\">\ud83d\udcbb</a> <a href=\"https://github.com/scikit-hep/vector/commits?author=henryiii\" title=\"Documentation\">\ud83d\udcd6</a></td>\n    <td align=\"center\"><a href=\"https://github.com/eduardo-rodrigues\"><img src=\"https://avatars.githubusercontent.com/u/5013581?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Eduardo Rodrigues</b></sub></a><br /><a href=\"#maintenance-eduardo-rodrigues\" title=\"Maintenance\">\ud83d\udea7</a> <a href=\"https://github.com/scikit-hep/vector/commits?author=eduardo-rodrigues\" title=\"Code\">\ud83d\udcbb</a> <a href=\"https://github.com/scikit-hep/vector/commits?author=eduardo-rodrigues\" title=\"Documentation\">\ud83d\udcd6</a></td>\n    <td align=\"center\"><a href=\"http://lovelybuggies.com.cn/\"><img src=\"https://avatars.githubusercontent.com/u/29083689?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>N!no</b></sub></a><br /><a href=\"https://github.com/scikit-hep/vector/commits?author=LovelyBuggies\" title=\"Documentation\">\ud83d\udcd6</a></td>\n    <td align=\"center\"><a href=\"https://github.com/pfackeldey\"><img src=\"https://avatars.githubusercontent.com/u/18463582?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Peter Fackeldey</b></sub></a><br /><a href=\"https://github.com/scikit-hep/vector/commits?author=pfackeldey\" title=\"Documentation\">\ud83d\udcd6</a></td>\n    <td align=\"center\"><a href=\"https://github.com/kreczko\"><img src=\"https://avatars.githubusercontent.com/u/1213276?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Luke Kreczko</b></sub></a><br /><a href=\"https://github.com/scikit-hep/vector/commits?author=kreczko\" title=\"Code\">\ud83d\udcbb</a></td>\n    <td align=\"center\"><a href=\"https://github.com/nsmith-\"><img src=\"https://avatars.githubusercontent.com/u/6587412?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Nicholas Smith</b></sub></a><br /><a href=\"#ideas-nsmith-\" title=\"Ideas, Planning, & Feedback\">\ud83e\udd14</a></td>\n  </tr>\n  <tr>\n    <td align=\"center\"><a href=\"https://github.com/mayou36\"><img src=\"https://avatars.githubusercontent.com/u/17454848?v=4?s=100\" width=\"100px;\" alt=\"\"/><br /><sub><b>Jonas Eschle</b></sub></a><br /><a href=\"#ideas-mayou36\" title=\"Ideas, Planning, & Feedback\">\ud83e\udd14</a></td>\n  </tr>\n</table>\n\n<!-- markdownlint-restore -->\n<!-- prettier-ignore-end -->\n\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\nThis project follows the\n[all-contributors](https://github.com/all-contributors/all-contributors)\nspecification. Contributions of any kind welcome! See\n[CONTRIBUTING.md](./.github/CONTRIBUTING.md) for information on setting up a\ndevelopment environment.\n\n## Acknowledgements\n\nThis library was primarily developed by Jim Pivarski, Henry Schreiner, and Eduardo Rodrigues.\n\nSupport for this work was provided by the National Science Foundation cooperative agreement OAC-1836650 (IRIS-HEP) and OAC-1450377 (DIANA/HEP). Any opinions, findings, conclusions or recommendations expressed in this material are those of the authors and do not necessarily reflect the views of the National Science Foundation.\n\n[actions-badge]: https://github.com/scikit-hep/vector/actions/workflows/ci.yml/badge.svg\n[actions-link]: https://github.com/scikit-hep/vector/actions\n[black-badge]: https://img.shields.io/badge/code%20style-black-000000.svg\n[black-link]: https://github.com/psf/black\n[codecov-badge]: https://codecov.io/gh/scikit-hep/vector/branch/main/graph/badge.svg?token=YBv60ueORQ\n[codecov-link]: https://codecov.io/gh/scikit-hep/vector\n[conda-version]: https://img.shields.io/conda/vn/conda-forge/vector.svg\n[conda-link]: https://github.com/conda-forge/vector-feedstock\n[github-discussions-badge]: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github\n[github-discussions-link]: https://github.com/scikit-hep/vector/discussions\n[gitter-badge]: https://badges.gitter.im/Scikit-HEP/vector.svg\n[gitter-link]: https://gitter.im/Scikit-HEP/vector?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge\n[license-badge]: https://img.shields.io/badge/License-BSD_3--Clause-blue.svg\n[license-link]: https://opensource.org/licenses/BSD-3-Clause\n[pre-commit-badge]: https://results.pre-commit.ci/badge/github/scikit-hep/vector/develop.svg\n[pre-commit-link]: https://results.pre-commit.ci/repo/github/scikit-hep/vector\n[pypi-link]: https://pypi.org/project/vector/\n[pypi-platforms]: https://img.shields.io/pypi/pyversions/vector\n[pypi-version]: https://badge.fury.io/py/vector.svg\n[rtd-badge]: https://readthedocs.org/projects/vector/badge/?version=latest\n[rtd-link]: https://vector.readthedocs.io/en/latest/?badge=latest\n[sk-badge]: https://scikit-hep.org/assets/images/Scikit--HEP-Project-blue.svg\n[sk-link]: https://scikit-hep.org/\n[zenodo-badge]: https://zenodo.org/badge/DOI/10.5281/zenodo.7054478.svg\n[zenodo-link]: https://doi.org/10.5281/zenodo.7054478\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Vector classes and utilities",
    "version": "1.4.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/scikit-hep/vector/issues",
        "Changelog": "https://vector.readthedocs.io/en/latest/changelog.html",
        "Discussions": "https://github.com/scikit-hep/vector/discussions",
        "Documentation": "https://vector.readthedocs.io/",
        "Homepage": "https://github.com/scikit-hep/vector"
    },
    "split_keywords": [
        "vector"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "646315fc58a95f45ebcf222aebcadc3b005a2fdd57e5e03125f128e084065512",
                "md5": "efbf86a33c5870833ca5f70a1f87c633",
                "sha256": "a7d0e65eb929015b11816f720380c5c9d59a92b25833d3035d65d5296e97db65"
            },
            "downloads": -1,
            "filename": "vector-1.4.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "efbf86a33c5870833ca5f70a1f87c633",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 181278,
            "upload_time": "2024-04-23T14:25:01",
            "upload_time_iso_8601": "2024-04-23T14:25:01.788504Z",
            "url": "https://files.pythonhosted.org/packages/64/63/15fc58a95f45ebcf222aebcadc3b005a2fdd57e5e03125f128e084065512/vector-1.4.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "e540cdae41b890630e8800d2df341d80ee3feb78f2566cb4b019adabf327cf79",
                "md5": "af6f16dd9f8629666a828c852e94315e",
                "sha256": "1083f08510678330118113f99c03e85088ddc7e3a69b296da0e8d0d0978a573d"
            },
            "downloads": -1,
            "filename": "vector-1.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "af6f16dd9f8629666a828c852e94315e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 368490,
            "upload_time": "2024-04-23T14:25:05",
            "upload_time_iso_8601": "2024-04-23T14:25:05.463460Z",
            "url": "https://files.pythonhosted.org/packages/e5/40/cdae41b890630e8800d2df341d80ee3feb78f2566cb4b019adabf327cf79/vector-1.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-23 14:25:05",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "scikit-hep",
    "github_project": "vector",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "vector"
}
        
Elapsed time: 0.25389s