lorentz#

import ampform.kinematics.lorentz

Symbolic implementations for Lorentz vectors and boosts.

create_four_momentum_symbols(topology: Topology) FourMomenta[source]#

Create a set of array-symbols for a Topology.

>>> from qrules.topology import create_isobar_topologies
>>> topologies = create_isobar_topologies(3)
>>> create_four_momentum_symbols(topologies[0])
{0: p0, 1: p1, 2: p2}
create_four_momentum_symbol(index: int) ArraySymbol[source]#
FourMomenta#

A mapping of state IDs to their corresponding FourMomentumSymbol.

It’s best to create a dict of FourMomenta with create_four_momentum_symbols().

alias of Dict[int, FourMomentumSymbol]

FourMomentumSymbol[source]#

Array-Symbol that represents an array of four-momenta.

The array is assumed to be of shape \(n\times 4\) with \(n\) the number of events. The four-momenta are assumed to be in the order \(\left(E,\vec{p}\right)\). See also Energy, FourMomentumX, FourMomentumY, and FourMomentumZ.

class Energy(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Represents the energy-component of a FourMomentumSymbol.

\(E\left(p\right)=p\left[:, 0\right]\)

momentum: Basic[source]#
class FourMomentumX(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Component \(x\) of a FourMomentumSymbol.

\({p}_x=p\left[:, 1\right]\)

momentum: Basic[source]#
class FourMomentumY(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Component \(y\) of a FourMomentumSymbol.

\({p}_y=p\left[:, 2\right]\)

momentum: Basic[source]#
class FourMomentumZ(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Component \(z\) of a FourMomentumSymbol.

\({p}_z=p\left[:, 3\right]\)

momentum: Basic[source]#
class ThreeMomentum(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: NumPyPrintable

Spatial components of a FourMomentumSymbol.

\(\vec{p}=p\left[:, 1:\right]\)

p[:, 1:]
momentum: Basic[source]#
class EuclideanNorm(vector, *args, evaluate: bool = False, **kwargs)[source]#

Bases: NumPyPrintable

Take the euclidean norm of an array over axis 1.

\(\left|v\right|=\sqrt{\left|v\right|^{2}}\)

from numpy import sqrt, sum

numpy.sqrt(sum(v**2, axis=1))
vector: Basic[source]#
class EuclideanNormSquared(vector, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Take the squared euclidean norm of an array over axis 1.

vector: Basic[source]#
three_momentum_norm(momentum: Basic) EuclideanNorm[source]#
class InvariantMass(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Invariant mass of a FourMomentumSymbol.

(1)#\[\begin{split} \begin{array}{rcl} m_{p} &=& \sqrt[\mathrm{c}]{E\left(p\right)^{2} - \left|\vec{p}\right|^{2}} \\ \end{array}\end{split}\]
momentum: Basic[source]#
class NegativeMomentum(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Invert the spatial components of a FourMomentumSymbol.

momentum: Basic[source]#
class MinkowskiMetric(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: NumPyPrintable

Minkowski metric \(\eta = (1, -1, -1, -1)\).

momentum: Basic[source]#
class BoostZMatrix(beta, n_events, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Represents a Lorentz boost matrix in the \(z\)-direction.

Parameters:
  • beta – Velocity in the \(z\)-direction, \(\beta=p_z/E\).

  • n_events – Number of events \(n\) for this matrix array of shape \(n\times4\times4\). Defaults to the len of beta.

This boost operates on a FourMomentumSymbol and looks like:

(2)#\[\begin{split}\boldsymbol{B_z}\left(\beta\right) = \left[\begin{matrix}\frac{1}{\sqrt[\mathrm{c}]{1 - \beta^{2}}} & 0 & 0 & - \frac{\beta}{\sqrt[\mathrm{c}]{1 - \beta^{2}}}\\0 & 1 & 0 & 0\\0 & 0 & 1 & 0\\- \frac{\beta}{\sqrt[\mathrm{c}]{1 - \beta^{2}}} & 0 & 0 & \frac{1}{\sqrt[\mathrm{c}]{1 - \beta^{2}}}\end{matrix}\right]\end{split}\]

In TensorWaves, this class is expressed in a computational backend and it should operate on four-momentum arrays of rank-2. As such, this boost matrix becomes a rank-3 matrix. When using NumPy as backend, the computation looks as follows:

from numpy import array, ones, sqrt, zeros

def _lambdifygenerated(b):
    x0 = 1/numpy.sqrt(1 - b**2)
    x1 = len(b)
    return (array(
            [
                [x0, zeros(x1), zeros(x1), -b*x0],
                [zeros(x1), ones(x1), zeros(x1), zeros(x1)],
                [zeros(x1), zeros(x1), ones(x1), zeros(x1)],
                [-b*x0, zeros(x1), zeros(x1), x0],
            ]
        ).transpose((2, 0, 1)))

Note that this code was generated with sympy.lambdify with cse=True. The repetition of numpy.ones() is still bothersome, but these sub-nodes is also extracted by sympy.cse if the expression is nested further down in an expression tree, for instance when boosting a FourMomentumSymbol \(p\) in the \(z\)-direction:

(3)#\[\boldsymbol{B_z}\left(\beta\right) \boldsymbol{R_y}\left(\theta\right) \boldsymbol{R_z}\left(\phi\right) p\]

which in numpy code becomes:

from numpy import array, cos, einsum, ones, sin, sqrt, zeros

def _lambdifygenerated(beta, p, phi, theta):
    x0 = 1/numpy.sqrt(1 - beta**2)
    x1 = len(p)
    x2 = ones(x1)
    x3 = zeros(x1)
    return (einsum("...ij,...jk,...kl,...l->...i", array(
            [
                [x0, x3, x3, -beta*x0],
                [x3, x2, x3, x3],
                [x3, x3, x2, x3],
                [-beta*x0, x3, x3, x0],
            ]
        ).transpose((2, 0, 1)), array(
            [
                [x2, x3, x3, x3],
                [x3, numpy.cos(theta), x3, numpy.sin(theta)],
                [x3, x3, x2, x3],
                [x3, -numpy.sin(theta), x3, numpy.cos(theta)],
            ]
        ).transpose((2, 0, 1)), array(
            [
                [x2, x3, x3, x3],
                [x3, numpy.cos(phi), -numpy.sin(phi), x3],
                [x3, numpy.sin(phi), numpy.cos(phi), x3],
                [x3, x3, x3, x2],
            ]
        ).transpose((2, 0, 1)), p))
beta: Basic[source]#
n_events: Basic[source]#
class BoostMatrix(momentum, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Compute a rank-3 Lorentz boost matrix from a FourMomentumSymbol.

This boost operates on a FourMomentumSymbol and looks like:

(4)#\[\begin{split}\boldsymbol{B}\left(p\right) = \left[\begin{matrix}\frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}} & - \frac{{p}_x}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}} E\left(p\right)} & - \frac{{p}_y}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}} E\left(p\right)} & - \frac{{p}_z}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}} E\left(p\right)}\\- \frac{{p}_x}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}} E\left(p\right)} & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_x^{2}}{\left|\vec{p}\right|^{2}} + 1 & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_x {p}_y}{\left|\vec{p}\right|^{2}} & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_x {p}_z}{\left|\vec{p}\right|^{2}}\\- \frac{{p}_y}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}} E\left(p\right)} & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_x {p}_y}{\left|\vec{p}\right|^{2}} & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_y^{2}}{\left|\vec{p}\right|^{2}} + 1 & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_y {p}_z}{\left|\vec{p}\right|^{2}}\\- \frac{{p}_z}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}} E\left(p\right)} & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_x {p}_z}{\left|\vec{p}\right|^{2}} & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_y {p}_z}{\left|\vec{p}\right|^{2}} & \frac{\left(-1 + \frac{1}{\sqrt{1 - \frac{\left|\vec{p}\right|^{2}}{E\left(p\right)^{2}}}}\right) {p}_z^{2}}{\left|\vec{p}\right|^{2}} + 1\end{matrix}\right]\end{split}\]

In TensorWaves, this class is expressed in a computational backend and it should operate on four-momentum arrays of rank-2. As such, this boost matrix becomes a rank-3 matrix. When using NumPy as backend, the computation looks as follows:

from numpy import sqrt, sum

def _lambdifygenerated(p):
    x0 = p
    x1 = x0[:, 0]
    x2 = sum(x0[:, 1:]**2, axis=1)
    x3 = 1/numpy.sqrt(1 - x2/x1**2)
    x4 = x0[:, 1]
    x5 = x3/x1
    x6 = x0[:, 2]
    x7 = x0[:, 3]
    x8 = (x3 - 1)/x2
    x9 = x4*x8
    return (array(
            [
                [x3, -x4*x5, -x5*x6, -x5*x7],
                [-x4*x5, x4**2*x8 + 1, x6*x9, x7*x9],
                [-x5*x6, x6*x9, x6**2*x8 + 1, x6*x7*x8],
                [-x5*x7, x7*x9, x6*x7*x8, x7**2*x8 + 1],
            ]
        ).transpose((2, 0, 1)))
momentum: Basic[source]#
class RotationYMatrix(angle, n_events, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Rotation matrix around the \(y\)-axis for a FourMomentumSymbol.

Parameters:
  • angle – Angle with which to rotate, see e.g. Phi and Theta.

  • n_events – Number of events \(n\) for this matrix array of shape \(n\times4\times4\). Defaults to the len of angle.

The matrix for a rotation over angle \(\alpha\) around the \(y\)-axis operating on FourMomentumSymbol looks like:

(5)#\[\begin{split}\boldsymbol{R_y}\left(\alpha\right) = \left[\begin{matrix}1 & 0 & 0 & 0\\0 & \cos{\left(\alpha \right)} & 0 & \sin{\left(\alpha \right)}\\0 & 0 & 1 & 0\\0 & - \sin{\left(\alpha \right)} & 0 & \cos{\left(\alpha \right)}\end{matrix}\right]\end{split}\]

See RotationZMatrix for the computational code.

angle: Basic[source]#
n_events: Basic[source]#
class RotationZMatrix(angle, n_events, *args, evaluate: bool = False, **kwargs)[source]#

Bases: Expr

Rotation matrix around the \(z\)-axis for a FourMomentumSymbol.

Parameters:
  • angle – Angle with which to rotate, see e.g. Phi and Theta.

  • n_events – Number of events \(n\) for this matrix array of shape \(n\times4\times4\). Defaults to the len of angle.

The matrix for a rotation over angle \(\alpha\) around the \(z\)-axis operating on FourMomentumSymbol looks like:

(6)#\[\begin{split}\boldsymbol{R_z}\left(\alpha\right) = \left[\begin{matrix}1 & 0 & 0 & 0\\0 & \cos{\left(\alpha \right)} & - \sin{\left(\alpha \right)} & 0\\0 & \sin{\left(\alpha \right)} & \cos{\left(\alpha \right)} & 0\\0 & 0 & 0 & 1\end{matrix}\right]\end{split}\]

In TensorWaves, this class is expressed in a computational backend and it should operate on four-momentum arrays of rank-2. As such, this boost matrix becomes a rank-3 matrix. When using NumPy as backend, the computation looks as follows:

from numpy import array, cos, ones, sin, zeros

def _lambdifygenerated(a):
    x0 = len(a)
    return (array(
            [
                [ones(x0), zeros(x0), zeros(x0), zeros(x0)],
                [zeros(x0), numpy.cos(a), -numpy.sin(a), zeros(x0)],
                [zeros(x0), numpy.sin(a), numpy.cos(a), zeros(x0)],
                [zeros(x0), zeros(x0), zeros(x0), ones(x0)],
            ]
        ).transpose((2, 0, 1)))

See also the note that comes with Equation (3).

angle: Basic[source]#
n_events: Basic[source]#
class ArraySize(array, *args, evaluate: bool = False, **kwargs)[source]#

Bases: NumPyPrintable

Symbolic expression for getting the size of a numerical array.

array: Any[source]#
compute_boost_chain(topology: Topology, momenta: FourMomenta, state_id: int) list[BoostMatrix][source]#
get_four_momentum_sum(topology: Topology, momenta: FourMomenta, state_id: int) ArraySum | FourMomentumSymbol[source]#

Get the FourMomentumSymbol or sum of momenta for any edge ID.

If the edge ID is a final state ID, return its FourMomentumSymbol. If it’s an intermediate edge ID, return the sum of the momenta of the final states to which it decays.

>>> from qrules.topology import create_isobar_topologies
>>> topology = create_isobar_topologies(3)[0]
>>> momenta = create_four_momentum_symbols(topology)
>>> get_four_momentum_sum(topology, momenta, state_id=0)
p0
>>> get_four_momentum_sum(topology, momenta, state_id=3)
p1 + p2
compute_invariant_masses(four_momenta: FourMomenta, topology: Topology) dict[Symbol, Expr][source]#

Compute the invariant masses for all final state combinations.

get_invariant_mass_symbol(topology: Topology, state_id: int) Symbol[source]#

Generate an invariant mass label for a state (edge on a topology).

Example

In the case shown in Figure topologies[0], the invariant mass of state \(5\) is \(m_{034}\), because \(p_5=p_0+p_3+p_4\):

>>> from qrules.topology import create_isobar_topologies
>>> from ampform._qrules import get_qrules_version
>>> topologies = create_isobar_topologies(5)
>>> topology = topologies[0 if get_qrules_version() < (0, 10) else 3]
>>> get_invariant_mass_symbol(topology, state_id=5)
m_034

Naturally, the ‘invariant’ mass label for a final state is just the mass of the state itself:

>>> get_invariant_mass_symbol(topologies[0], state_id=1)
m_1