Skip to main content

Demystifying Amplitude Analysis

GitHub Release GitHub last commit GitHub Actions Workflow Status GitHub License Crates.io Version docs.rs Codecov PyPI - Version Read the Docs

Table of Contents:

Introduction

This project began with a desire to make a fast but easy to use interface for fitting amplitudes to particle physics data. That being said, there are performant methods such as AmpTools, which is written in C++, but in my personal experience, it can be a bit tricky to use and extend, and it generally requires a lot of boilerplate code to generate new amplitudes or plotting scripts. On the other hand, there are also libraries like PyPWA (written in Python) which seem like they could be easy to use, but often fail in this aspect due to Python's limiting syntax, speed issues, and a general lack of documentation and ongoing development. There have been attempts to bridge the gap between AmpTools and Python, most recently (and successfully) PyAmpTools. The difficulty with this method is that it relies on PyROOT, which also means you need ROOT installed (and built with your version of Python). For now, I'll spare you the anti-ROOT rant and just say that ROOT should be an opt-in, not a requirement. So where does that leave rustitude?

As the name suggests, rustitude was written in Rust, so let's get the obvious downside out of the way: not many particle physicists know how to write Rust code. Hopefully, this will change over the next decade (and there has already been some support from the US government, of all places). While Rust carries the disadvantage of relative obscurity compared to C++, it also has many benefits. No null means no null references (Tony Hoare's "billion dollar mistake"). In Rust, references are always valid, a guarantee made by a very helpful and only occasionally frustrating borrow checker. Rust "crates" are set up in a way which encourages documentation (see rustitude-core's documentation), and the basic syntax is fairly easy to learn for people who have been using optional type checking in Python. Perhaps one of the biggest benefits of Rust is how easy it is to employ parallelization, but the two reasons I like it most are that it's incredibly easy to write Python bindings (that's what this library is after all) and it has a package manager. This second point is important -- unlike C/C++, where a developer is swamped with some menagerie Makefile, CMakeLists.txt, or some scons monstrosity which may only work on "X" system and only if you install and use make, cmake, g++, or whatever (oh yeah, and you made sure all your external dependencies are linked correctly, right? Right?), Rust supports adding a package by simply adding a line to Cargo.toml (or using the cargo add command). In many ways, package management in Rust is actually simpler than Python, since there's only one prefered method of creating and managing projects, formatting, linting, and compiling.

Now I've covered why I don't like some of the existing solutions, and why I chose to use Rust, but what does this project have that makes it stand out? Here are some reasons to entice you:

  • rustitude will automatically parallelize amplitudes over the events in a dataset. There's no reason for a developer to ever write parallelized code themselves.
  • Implementing Node on a struct is all that is needed to use it as an amplitude. This means developers need only write two to three total methods to describe the entire functionality of their amplitude, and one of these just gives the names and order of the amplitude's input parameters.
  • A major goal of rustitude was to increase processing speed by sacrificing memory. This is done by precalculating parts of amplitudes which don't change when the free parameter inputs change. AmpTools supports a version of this, but only on the level of each general amplitude rather than on an individual basis. The simplest example of this is the Ylm amplitude (spherical harmonic), which can be entirely precalculated given the value of l and m. In AmpTools, different instances of Ylm with different ls and ms share precalculated data, whereas in rustitude, they don't. The AmpTools implementation of Ylm needs to calculate a spherical harmonic for every event on every function call, while rustitude just needs to look up a value in an array!
  • The majority of the library (the public interface) has Python bindings, so if there is no need for custom amplitudes, a developer never actually has to write any Rust code, and the resulting calculations will be as performant as if they were written in Rust.

Installation

Adding rustitude to an existing Rust project is as simple as

cargo add rustitude

The Python installation is equally straightforward:

pip install rustitude

Usage

See the rustitude-core crate for a more in-depth tutorial on writing custom amplitudes in Rust. This package is mostly focused on the Python side of things. Here is the setup for an example analysis:

import rustitude as rt
from rustitude import gluex
import numpy as np

# Start by creating some amplitudes:
f0p = gluex.resonances.KMatrixF0('f0+', channel=2)
f0n = gluex.resonances.KMatrixF0('f0-', channel=2)
f2 = gluex.resonances.KMatrixF2('f2', channel=2)
a0p = gluex.resonances.KMatrixA0('a0+', channel=1)
a0n = gluex.resonances.KMatrixA0('a0-', channel=1)
a2 = gluex.resonances.KMatrixA2('a2', channel=1)
s0p = gluex.harmonics.Zlm('Z00+', l=0, m=0, reflectivity='+')
s0n = gluex.harmonics.Zlm('Z00-', 0, 0, '-')
d2p = gluex.harmonics.Zlm('Z22+', 2, 2) # positive reflectivity is the default

# Next, let's put them together into a model
# The API supports addition and multiplication and has additional methods for the real part (`real`) and imaginary part (`imag`).
pos_re_sum = (f0p + a0p) * s0p.real() + (f2 + a2) * d2p.real()
pos_im_sum = (f0p + a0p) * s0p.imag() + (f2 + a2) * d2p.imag()
neg_re_sum = (f0n + a0n) * s0n.real()
neg_im_sum = (f0n + a0n) * s0n.imag()

mod = rt.Model([pos_re_sum, pos_im_sum, neg_re_sum, neg_im_sum])

# There is no need to constrain amplitudes, since each named amplitude is only ever evaluated once and a cached value gets pulled if we run across an amplitude by the same name!
# We should, however, fix some of the values to make the fit less ambiguous. For instance, suppose we are above the threshold for the f_0(500) which is included in the F0 K-Matrix:
mod.fix("f0+", "f0_500 re", 0.0)
mod.fix("f0+", "f0_500 im", 0.0)
mod.fix("f0-", "f0_500 re", 0.0)
mod.fix("f0-", "f0_500 im", 0.0)

# As mentioned, we should also fix at least one complex phase per coherent sum:
mod.fix("f0+", "f0_980 im", 0.0)
mod.fix("f0-", "f0_980 im", 0.0)

# All done! Now let's load our model into a Manager, which helps coordinate the model with datasets.
# First, load up some datasets. rustitude provides an open operation that uses uproot under the hood:
ds = rt.open('data.root')
ds_mc = rt.open('mc.root')

m_data = rt.Manager(mod, ds)
m_mc = rt.Manager(mod, ds_mc)

# We could stop here and evaluate just one dataset at a time:

# res = m_data([1.0, 3.4, 2.8, -3.6, ... ])
# -> [5.3, 0.2, ...], a list of intensities from the amplitude calculation

# Or, what we probably want to do is find the negative log-likelihood for some choice of parameters:

nll = rt.ExtendedLogLikelihood(m_data, m_mc)

res = nll([10.0] * mod.n_free) # automatic CPU parallelism without GIL
print(res) # prints some value for the NLL

mi = rt.minimizer(nll, method='Minuit') # use iminuit to create a Minuit minimizer
mi.migrad() # run the Migrad algorithm
print(mi) # print the fit result

Automatic parallelism over the CPU can be disabled via function calls which support it (for example, nll([10.0] * mod.n_free, parallel=False) would run without parallel processing), and the number of CPUs used can be controlled via the RAYON_NUM_THREADS environment variable, which can be set before the code is run or modified inside the code (for example, os.environ['RAYON_NUM_THREADS] = '5' would ensure only five threads are used past that point in the code). By default, an unset value or the value of '0' will use all available cores.

See the rustitude-gluex package for some of the currently implemented amplitudes (derived from GlueX's halld_sim repo). There are also some helper methods Scalar, CScalar, and PCScalar to create amplitudes which represent a single free parameter, a single complex free parameter, and a single complex free parameter in polar coordinates respectively.

TODOs

In no particular order, here is a list of what (probably) needs to be done before I will stop making any breaking changes:

  • Pure Rust parsing of ROOT files without the uproot backend (I have some moderate success with oxyroot, but there are still a few issues reading larger files)
  • Add plotting methods
  • A way to check if the number of parameters matches the input at compile time would be nice, not sure if it's possible though
  • Give managers a way to apply amplitudes to new datasets, like using the result from a fit to weight some generated Monte-Carlo for plotting the result. This is possible to do through Python, but a convenience method is probably needed
  • Lots of documentation
  • Lots of tests

Metadata

Release files for rustitude 0.10.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rustitude 0.10.3
File Size Uploaded
rustitude-0.10.3.tar.gz 8.1 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for rustitude 0.10.3
File
rustitude-0.10.3-cp37-abi3-win_amd64.whl CPython 3.7 abi3 Windows x86-64 Details
rustitude-0.10.3-cp37-abi3-win32.whl CPython 3.7 abi3 Windows x86-32 Details
rustitude-0.10.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.7 abi3 Linux glibc 2.17+ x86-64 Details
rustitude-0.10.3-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl CPython 3.7 abi3 Linux glibc 2.17+ IBM System/390x Details
rustitude-0.10.3-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl CPython 3.7 abi3 Linux glibc 2.17+ PowerPC 64-le Details
rustitude-0.10.3-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl CPython 3.7 abi3 Linux glibc 2.17+ ARMv7l Details
rustitude-0.10.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.7 abi3 Linux glibc 2.17+ ARM64 Details
rustitude-0.10.3-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl CPython 3.7 abi3 Linux glibc 2.12+ x86-32 Details
rustitude-0.10.3-cp37-abi3-macosx_11_0_arm64.whl CPython 3.7 abi3 macOS 11.0+ ARM64 Details
rustitude-0.10.3-cp37-abi3-macosx_10_12_x86_64.whl CPython 3.7 abi3 macOS 10.12+ x86-64 Details

Total release size: 110.8 MB

Release files / rustitude-0.10.3.tar.gz

Download URL rustitude-0.10.3.tar.gz
Size 8.1 MB
Tags Source
SHA-256 checksum
How to use checksums
021a761c5ea499730870cf5667f07f7382538b75c5684b1ddec440a8b06a7c1b
BLAKE2b-256 checksum
How to use checksums
c7c407d954ab25b4e513035ba42dc45f2e500a089519211c27e278b7b8b9ac53
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-win_amd64.whl

Download URL rustitude-0.10.3-cp37-abi3-win_amd64.whl
Size 2.2 MB
Tags CPython 3.7 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
ca6ef4bdf216305ae56b6e9653ae9817119fedd0326c3735bda8ba0ee315406e
BLAKE2b-256 checksum
How to use checksums
59e19c77d3ea6582b11c897f6c77c960a212c5ed21d161981017dacf01616438
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-win32.whl

Download URL rustitude-0.10.3-cp37-abi3-win32.whl
Size 2.1 MB
Tags CPython 3.7 Windows x86-32 abi3
SHA-256 checksum
How to use checksums
f8ab0d32acbcd09db7095b52078533ee497532df4a780b4bd2031b4265bf4f90
BLAKE2b-256 checksum
How to use checksums
fe0b8fed8f5a497c23e50b84fc412b2a51f3be2a1b0a85d63fddaac9315009be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL rustitude-0.10.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 15.5 MB
Tags CPython 3.7 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
4801d997428410d65c1e0e7575973d87445bef3af605e77af7fb4db2a9fedba5
BLAKE2b-256 checksum
How to use checksums
f54865b2cc256ad47018284868356a7ebf55a694e54d7a73ad4ca39d889fe883
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl

Download URL rustitude-0.10.3-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl
Size 19.7 MB
Tags CPython 3.7 Linux glibc 2.17+ IBM System/390x abi3
SHA-256 checksum
How to use checksums
0bf9474180e33fc5e5445e9769b5f73cc41efc65bccb040d6016f21bd60a8eae
BLAKE2b-256 checksum
How to use checksums
84b64cfd3003486fdc5b90e82e692b7cc1b172907894f6835a788d7b08dfcbbe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl

Download URL rustitude-0.10.3-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Size 14.6 MB
Tags CPython 3.7 Linux glibc 2.17+ PowerPC 64-le abi3
SHA-256 checksum
How to use checksums
a1eee1362e9b0c571f04689494891624db33f77ecbdfb5ddc4a5e498b1e57390
BLAKE2b-256 checksum
How to use checksums
1dac2971a5769e9899dbb00adbad2db278cb163d4d8ed9a4e1c60ce0a4147ebc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl

Download URL rustitude-0.10.3-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Size 14.5 MB
Tags CPython 3.7 Linux glibc 2.17+ ARMv7l abi3
SHA-256 checksum
How to use checksums
919433c996dd0f58fd5c190fed9f5c12290517b7ca5ed229b1b07c0594efa2ec
BLAKE2b-256 checksum
How to use checksums
e282a5e3f0dbacd6005f02e09032307338fe0a37ab8d1539ec8c74a4562183dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL rustitude-0.10.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 14.4 MB
Tags CPython 3.7 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
1e8e80a36f41b03f67d9e8b1c004676cc4ee26f5a9b2a7fbf56ff78cdc2e5a9d
BLAKE2b-256 checksum
How to use checksums
0aec2863bafd323e0047f846da48f9d22af1131176f30bd69cab78d5d394ba88
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl

Download URL rustitude-0.10.3-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
Size 14.9 MB
Tags CPython 3.7 Linux glibc 2.12+ x86-32 abi3
SHA-256 checksum
How to use checksums
7288ecc8ed62ad7d7f73374e908ff856b351b8755902cf0519ff798a9b9c1c31
BLAKE2b-256 checksum
How to use checksums
61c26a6cd9cd088aee925f30584ba3ac95d14d5123940307d2df7ef8c99e6b13
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-macosx_11_0_arm64.whl

Download URL rustitude-0.10.3-cp37-abi3-macosx_11_0_arm64.whl
Size 2.3 MB
Tags CPython 3.7 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
9a01fb207f66170c5c010ff222b6f43637a2405152f71901d42bf647507baa7a
BLAKE2b-256 checksum
How to use checksums
adc5a6de86182140f9b63c7d005578cba2a82270b4589067dd05d4f0f4a64796
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release files / rustitude-0.10.3-cp37-abi3-macosx_10_12_x86_64.whl

Download URL rustitude-0.10.3-cp37-abi3-macosx_10_12_x86_64.whl
Size 2.6 MB
Tags CPython 3.7 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
cf69a7943dcf141bfdec6c99f0ed0ae2abbf09a18f58268047c9e9695d51de98
BLAKE2b-256 checksum
How to use checksums
30d85fe6ab8ef7457c7ae9b656c1a385497e36b776542cb3bb82b9a3f3ccfdd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.7.0

Release history Release notifications | RSS feed

This release

0.10.3 This release

11 release files

0.9.3

11 release files

0.9.2

11 release files

0.9.1

11 release files

0.9.0

11 release files

0.8.0

11 release files

0.7.4

11 release files

0.7.3

11 release files

0.7.2

11 release files

0.7.1

11 release files

0.7.0

11 release files

0.5.0

11 release files

0.4.3

12 release files

0.4.2

11 release files

0.4.0

11 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page