跳转到主要内容

向量类和实用工具

项目描述

Vector logo

矢量:二维、三维和洛伦兹矢量的数组

Actions Status Documentation Status pre-commit.ci status codecov percentage GitHub Discussion Gitter

PyPI platforms PyPI version Conda latest release DOI LICENSE Scikit-HEP

矢量是一个Python 3.8+库(Python 3.6和3.7分别支持到v0.9.0v1.0.0),用于二维、三维和洛伦兹矢量(特别是矢量数组),以NumPy类似的方式解决常见的物理问题。

矢量的主要功能

  • 纯Python,仅依赖NumPy。这使得它更容易安装。
  • 矢量可以用各种坐标系表示:笛卡尔、圆柱、伪斜率以及这些坐标系的任何组合,对于洛伦兹矢量还包括时间或固有时间。总共有12个坐标系:{x-yρ-φ 在方位平面内} × {zθη 纵向} × {tτ 时间上}。
  • 使用由ROOTTLorentzVectorMath::LorentzVector以及scikit-hep/mathuproot-methods TLorentzVectorhenryiii/hepvectorcoffea.nanoevents.methods.vector设定的名称和约定。
  • 在多种后端实现
  • Awkward后端也在Numba中实现,用于矢量的即时编译计算。
  • 通过Awkward Arrays支持JAXDask
  • 几何矢量(具有最少的属性和方法名称)与表示动量的矢量(具有如pt = rhoenergy = tmass = tau的同义词)之间的区别。

安装

要安装,使用pip install vector或您喜欢的环境安装方式。

概述

本概述基于此处的文档

import vector
import numpy as np
import awkward as ak  # at least v2.0.0 (vector v1.4.* supports awkward v1 and v2 both)
import numba as nb

构建一个矢量或矢量数组

创建一个或多个矢量的最简单方法是使用辅助函数

  • vector.obj创建一个纯Python矢量对象,
  • vector.arr创建一个矢量NumPy数组(或小写的array,如np.array),
  • vector.awk创建一个矢量Awkward数组(或大写的Array,如ak.Array)。
  • vector.zip 用于创建向量数组(类似于 ak.zip

纯Python向量

您可以直接使用 VectorObjectMomentumObject 类来构建对象类型向量

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)

每个类都是如此。

或者,您可以使用单个包装函数来构建所有可能的对象类型向量组合

# 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),
)

2D向量的允许关键字参数有

  • xy 用于笛卡尔方位坐标
  • pxpy 用于动量
  • rhophi 用于极坐标方位
  • ptphi 用于动量

对于3D向量,您需要上述参数和

  • z 用于笛卡尔纵向坐标
  • pz 用于动量
  • theta 用于球极角(从 $0$ 到 $\pi$,包括)
  • eta 用于伪快度,这是一种球极角

对于4D向量,您需要上述参数和

  • t 用于笛卡尔时间坐标
  • Eenergy 用于四维动量
  • tau 用于“固有时间”(向量的静止坐标系中的时间坐标)
  • Mmass 用于四维动量

由于动量向量除了几何名称外还有动量同义词,任何动量同义词都会使整个向量成为动量向量。

如果您想通过关键字参数绕过维度和坐标系推断(例如,用于静态类型),则可以使用专用构造函数

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)

以及所有可能的方位、纵向和时间坐标的组合,几何和动量风味。

SymPy 向量

注意:SymPy 向量的操作仅在向量为正时间线型时与数值向量(Python、NumPy 和 Awkward 后端)100% 兼容,即如果 t**2 > x**2 + y**2 + z**2。空间线型和负时间线型情况有不同的符号约定。

您可以直接使用 VectorSympyMomentumSympy 类来构建对象类型向量

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)

每个类都是如此。

# 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),
)

由于 VectorSympy2DVectorSympy3DVectorSympy4D 和它们的动量等效操作 SymPy 表达式,因此所有正常的 SymPy 方法和函数都适用于结果、坐标和向量。

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()

适用于 vector.obj 构造的所有关键字参数和规则也适用于 vector.VectorSympyNDvector.MomentumObjectND 对象。

向量 NumPy 数组

您可以直接使用 VectorNumpy 类来构建对象类型向量

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

每个类都是如此。

或者,您可以使用单个包装函数来构建所有可能的 NumPy 类型向量组合

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

现有的 NumPy 数组可以视为向量数组,但需要是一个具有可识别字段名的 结构化数组

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

由于 VectorNumpy2DVectorNumpy3DVectorNumpy4D 和它们的动量等效是 NumPy 数组子类,因此所有正常的 NumPy 方法和函数都适用于它们。

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

适用于 vector.obj 构造的所有关键字参数和规则也适用于 vector.arr dtypes。

即使在构造时使用了动量同义词,dtype 中也使用几何名称。

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

Awkward Arrays 是比 NumPy 允许的更复杂的数据结构数组,例如变长列表、嵌套记录、缺失数据甚至异构数据(多个数据类型:谨慎使用)。

vector.awk 函数的行为与 ak.Array 构造函数完全相同,只是它创建向量数组。

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

如果您希望将名为 "Vector2D", "Vector3D", "Vector4D", "Momentum2D", "Momentum3D" 或 "Momentum4D" 的任何记录解释为向量,请全局注册这些行为。

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",
)

所有适用于 vector.obj 构造的参数和规则也适用于 vector.awk 字段名称。

最后,可以子类化 VectorAwkward 混合类以创建自定义向量类。尴尬的行为类和投影必须命名为 *Array。例如,coffea 使用以下名称 - TwoVectorArrayThreeVectorArrayPolarTwoVectorArraySphericalThreeVectorArray,...

向量属性

任何几何坐标都可以从任何坐标系中的向量中计算得出;它们将根据需要提供或计算。

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

有些属性不是坐标,而是由它们派生出来的。

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

这些属性被提供,因为它们可以在不同的坐标系中更快或更稳定地计算。例如,模量忽略了极坐标中的 phi

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

动量向量除了动量同义词之外,还具有几何属性。

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

关键在于:向量数组返回坐标数组

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.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]])
)

一些方法等同于二元运算符。

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

一些方法涉及多个向量。

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)

向量可以使用不同的坐标系。需要转换,但为了速度和数值稳定性而尽量减少。

# 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)

在 Python 中,一些“运算符”实际上是内置函数,例如 abs

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

注意,abs 返回

  • 2D 向量的 rho
  • 3D 向量的 mag
  • 4D 向量的 taumass

当您想要特定维度上的模量时,请使用命名属性;当您想要任何维度上的模量时,请使用 abs

向量可以来自不同的后端。适用于 Python 数字、NumPy 数组和 Awkward 数组的常规广播规则适用。

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
)

一些操作是为 2D 或 3D 向量定义的,但可以在更高维度的向量上使用,因为额外的分量可以被忽略或不受影响。

# 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))

这对于给 4D 向量赋予 3D 向量的所有功能特别有用。

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)
)

对于一些操作(如 +-==!= 等)- 向量的维度应该相等。这可以通过使用 like 方法、to_{coordinate_name} 方法、to_Vector*D 方法来实现。这些 to_Vector*D 方法为用户提供更大的灵活性,即,可以将新坐标值作为命名参数传递给方法。

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)

类似地,对于一些向量方法,输入向量的维度会进行严格的类型检查。

例如,叉积仅定义于 3D 和 7D 向量;因此,在 4D 向量上运行该方法将引发错误。

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

属性和方法(当前)列表如下

平面(2D、3D、4D)

  • xpx
  • ypy
  • rhopt):二维模量
  • rho2pt2):二维模量平方
  • phi
  • deltaphi(vector):phi 的差异(有符号,并校正为 $-\pi$ 至 $\pi$)
  • rotateZ(angle)
  • transform2D(obj):obj 必须通过 obj["xx"]obj["xy"]obj["yx"]obj["yy"] 提供组件
  • is_parallel(vector, tolerance=1e-5):只有当它们指向同一方向时才为真
  • is_antiparallel(vector, tolerance=1e-5):只有当它们指向相反方向时才为真
  • is_perpendicular(vector, tolerance=1e-5)

空间(3D、4D)

  • zpz
  • theta
  • eta
  • costheta
  • cottheta
  • magp):三维模量,不包括时间分量
  • mag2 (p2):三维模长的平方
  • cross:叉积(严格三维)
  • deltaangle(vector):角度差(总是非负)
  • deltaeta(vector)eta差(有符号)
  • deltaR(vector):$\Delta R = \sqrt{\Delta\phi^2 + \Delta\eta^2}$
  • deltaR2(vector):上述的平方
  • rotateX(angle)
  • rotateY(angle)
  • rotate_axis(axis, angle):忽略axis的模长,但必须至少为3D
  • rotate_euler(phi, theta, psi, order="zxz"):参数顺序与ROOT::Math::EulerAngles相同,且order="zxz"与ROOT的约定一致
  • rotate_nautical(yaw, pitch, roll)
  • rotate_quaternion(u, i, j, k):再次,约定与ROOT::Math::Quaternion相同。
  • transform3D(obj)obj必须通过obj["xx"]obj["xy"]等提供组件
  • is_parallel(vector, tolerance=1e-5):只有当它们指向同一方向时才为真
  • is_antiparallel(vector, tolerance=1e-5):只有当它们指向相反方向时才为真
  • is_perpendicular(vector, tolerance=1e-5)

洛伦兹(仅4D)

  • t (Eenergy):遵循ROOT::Math::LorentzVector的行为,将时空矢量视为负t和负tau,并截断方向错误的时空中矢量
  • t2 (E2energy2)
  • tau (Mmass):参见上面的注释
  • tau2 (M2mass2)
  • beta:介于$0$(包含)和$1$(不包含,除非矢量分量是无穷大)之间的标量
  • deltaRapidityPhi:$\Delta R_{\mbox{rapidity}} = \Delta\phi^2 + \Delta \mbox{rapidity}^2$
  • deltaRapidityPhi2:上述的平方
  • gamma:介于$1$(包含)和$\infty$之间的标量
  • rapidity:介于$0$(包含)和$\infty$之间的标量
  • boost_p4(four_vector):使用另一个4D矢量作为差值来改变坐标系
  • boost_beta(three_vector):使用3D beta矢量(所有分量介于$-1$和$+1$之间)来改变坐标系
  • boost(vector):使用给定vector的维度来确定行为
  • boostX(beta=None, gamma=None):提供betagamma,但不能同时提供
  • boostY(beta=None, gamma=None):提供betagamma,但不能同时提供
  • boostZ(beta=None, gamma=None):提供betagamma,但不能同时提供
  • transform4D(obj)obj必须通过obj["xx"]obj["xy"]等提供组件
  • to_beta3():将four_vector(用于boost_p4)转换为three_vector(用于boost_beta3
  • is_timelike(tolerance=0)
  • is_spacelike(tolerance=0)
  • is_lightlike(tolerance=1e-5):注意不同的容忍度

所有维度的数字

  • unit():注意括号
  • dot(vector):也可以使用@运算符
  • add(vector):也可以使用+运算符
  • subtract(vector):也可以使用-运算符
  • scale(factor):也可以使用*运算符
  • equal(vector):也可以使用==运算符,但考虑使用isclose
  • not_equal(vector):也可以使用!=运算符,但考虑使用isclose
  • sum():也可以使用numpy.sumawkward.sum,仅适用于NumPy和Awkward矢量
  • count_nonzero():也可以使用numpy.count_nonzeroawkward.count_nonzero,仅适用于NumPy和Awkward矢量
  • count():也可以使用awkward.count,仅适用于Awkward矢量
  • isclose(vector, rtol=1e-5, atol=1e-8, equal_nan=False):类似于np.isclose;数组还有allclose方法
  • to_VectorND(coordinates)/to_ND(coordinates):将N替换为所需的矢量维度
  • to_{coordinate-names}:例如 - to_rhophietatau
  • like(other):将矢量投影到other的维度上,例如 - two_d_vector.like(three_d_vector)

使用Numba编译您的Python

Numba 是一个用于 NumPy 和 Python 中数学相关子集的即时(JIT)编译器。它允许您在不离开 Python 环境的情况下编写快速代码。Numba 的缺点是它只能编译涉及它所识别的对象和函数的代码块。

Vector 库包括扩展,以告知 Numba 关于矢量对象、矢量 NumPy 数组和矢量 Awkward Arrays。截至编写时,由于 numba/numba#6148,矢量 NumPy 数组的实现尚不完整。

例如,考虑以下函数

@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))

当两个 MomentumObject4D 对象作为参数传递时,Numba 会识别它们,并将 Python 对象替换为低级结构。当它编译函数时,它会识别 + 为 4D add 函数,并识别 .mass 为结果的 tau 成分。

尽管这表明 Numba 可以操作矢量对象,但仅编译少数几个向量的计算并没有性能优势(而且可能存在劣势)。优势在于涉及大量向量的数组中。

# 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)

扩展 Awkward 混合子类

目前,可以通过子类化矢量混合子类来扩展矢量功能。尽管现有的机制运作良好,但它仍在不断改进。

例如,可以通过以下方式扩展 MomentumAwkward

behavior = vector.backends.awkward.behavior


@ak.mixin_class(behavior)
class TwoVector(vector.backends.awkward.MomentumAwkward2D):
    pass


@ak.mixin_class(behavior)
class ThreeVector(vector.backends.awkward.MomentumAwkward3D):
    pass


# required for transforming vectors
# the class names must always end with "Array"
TwoVectorArray.ProjectionClass2D = TwoVectorArray  # noqa: F821
TwoVectorArray.ProjectionClass3D = ThreeVectorArray  # noqa: F821
TwoVectorArray.MomentumClass = TwoVectorArray  # noqa: F821

ThreeVectorArray.ProjectionClass2D = TwoVectorArray  # noqa: F821
ThreeVectorArray.ProjectionClass3D = ThreeVectorArray  # noqa: F821
ThreeVectorArray.MomentumClass = ThreeVectorArray  # noqa: F821

vec = ak.zip(
    {
        "pt": [[1, 2], [], [3], [4]],
        "phi": [[1.2, 1.4], [], [1.6], [3.4]],
    },
    with_name="TwoVector",
    behavior=behavior,
)

vec

二元运算符不是由 awkward 自动注册的,但可以使用矢量方法在子类化的矢量上执行操作。

vec.add(vec)

类似地,新方法可以内部使用其他矢量方法。

@ak.mixin_class(behavior)
class LorentzVector(vector.backends.awkward.MomentumAwkward4D):
    @ak.mixin_class_method(np.divide, {numbers.Number})
    def divide(self, factor):
        return self.scale(1 / factor)


# required for transforming vectors
# the class names must always end with "Array"
LorentzVectorArray.ProjectionClass2D = TwoVectorArray  # noqa: F821
LorentzVectorArray.ProjectionClass3D = ThreeVectorArray  # noqa: F821
LorentzVectorArray.ProjectionClass4D = LorentzVectorArray  # noqa: F821
LorentzVectorArray.MomentumClass = LorentzVectorArray  # noqa: F821

vec = ak.zip(
    {
        "pt": [[1, 2], [], [3], [4]],
        "eta": [[1.2, 1.4], [], [1.6], [3.4]],
        "phi": [[0.3, 0.4], [], [0.5], [0.6]],
        "energy": [[50, 51], [], [52], [60]],
    },
    with_name="LorentzVector",
    behavior=behavior,
)

vec / 2
vec.like(vector.obj(x=1, y=2))
vec.like(vector.obj(x=1, y=2, z=3))

还可以在向量的行为字典中手动添加二元运算,以启用二元运算。

_binary_dispatch_cls = {
    "TwoVector": TwoVector,
    "ThreeVector": ThreeVector,
    "LorentzVector": LorentzVector,
}
_rank = [TwoVector, ThreeVector, LorentzVector]

for lhs, lhs_to in _binary_dispatch_cls.items():
    for rhs, rhs_to in _binary_dispatch_cls.items():
        out_to = min(lhs_to, rhs_to, key=_rank.index)
        behavior[(np.add, lhs, rhs)] = out_to.add
        behavior[(np.subtract, lhs, rhs)] = out_to.subtract

vec + vec
vec.to_2D() + vec.to_2D()

最后,而不是手动注册超类 ufunc,可以使用 copy_behaviors 实用函数复制新子类的行为项

behavior.update(ak._util.copy_behaviors("Vector2D", "TwoVector", behavior))
behavior.update(ak._util.copy_behaviors("Vector3D", "ThreeVector", behavior))
behavior.update(ak._util.copy_behaviors("Momentum4D", "LorentzVector", behavior))

vec + vec
vec.to_2D() + vec.to_2D()

关于向量的讨论

截至 2023 年 11 月 17 日的状态

vector 的首个重大版本已发布,该包已达到稳定状态。工作由 GitHub 上创建的错误报告和功能请求引领。它只能通过您的反馈来改进!

贡献者 ✨

感谢这些优秀的人们 (emoji key)


Jim Pivarski

🚧 💻 📖

亨利·施莱纳

🚧 💻 📖

埃杜阿多·罗德里格斯

🚧 💻 📖

N!no

📖

彼得·法克德尔

📖

卢克·克雷茨科

💻

尼古拉斯·史密斯

🤔

约纳斯·埃斯勒

🤔

本项目遵循所有贡献者规范。欢迎任何形式的贡献!有关设置开发环境的更多信息,请参阅CONTRIBUTING.md

致谢

该库主要是由吉姆·皮瓦尔斯基、亨利·施莱纳、萨兰什·乔普拉和埃杜阿多·罗德里格斯开发的。

本工作的支持来自国家科学基金会合作协议OAC-1836650和PHY-2323298(IRIS-HEP)以及OAC-1450377(DIANA/HEP)。在此材料中表达的意见、发现、结论或建议是作者的观点,并不一定反映国家科学基金会的观点。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

vector-1.5.1.tar.gz (326.5 kB 查看散列值)

上传时间

构建分布

vector-1.5.1-py3-none-any.whl (182.4 kB 查看散列值)

上传时间 Python 3

由以下支持