From f772aa4515520595096554511b79f075bde297a2 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Thu, 31 Oct 2024 16:02:22 -0400
Subject: [PATCH 01/26] docs: update README with codspeed badge
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index c673b42..817ea7a 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@
+
`laddu` (/ˈlʌduː/) is a library for analysis of particle physics data. It is intended to be a simple and efficient alternative to some of the [other tools](#alternatives) out there. `laddu` is written in Rust with bindings to Python via [`PyO3`](https://github.com/PyO3/pyo3) and [`maturin`](https://github.com/PyO3/maturin) and is the spiritual successor to [`rustitude`](https://github.com/denehoffman/rustitude), one of my first Rust projects. The goal of this project is to allow users to perform complex amplitude analyses (like partial-wave analyses) without complex code or configuration files.
From ebeabcdb41edc34f0df60ece7aba475e4fa9d342 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 09:59:45 -0400
Subject: [PATCH 02/26] feat: add python stub file for vectors
---
python/laddu/utils/vectors/__init__.pyi | 38 +++++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 python/laddu/utils/vectors/__init__.pyi
diff --git a/python/laddu/utils/vectors/__init__.pyi b/python/laddu/utils/vectors/__init__.pyi
new file mode 100644
index 0000000..ad039ab
--- /dev/null
+++ b/python/laddu/utils/vectors/__init__.pyi
@@ -0,0 +1,38 @@
+import numpy as np
+import numpy.typing as npt
+
+class Vector3:
+ mag: float
+ mag2: float
+ costheta: float
+ theta: float
+ phi: float
+ unit: Vector3
+ px: float
+ py: float
+ pz: float
+ def __init__(self, px: float, py: float, pz: float): ...
+ def __add__(self, other: Vector3) -> Vector3: ...
+ def dot(self, other: Vector3) -> float: ...
+ def cross(self, other: Vector3) -> Vector3: ...
+ def to_numpy(self) -> npt.NDArray[np.float64]: ...
+
+class Vector4:
+ mag: float
+ mag2: float
+ vec3: Vector3
+ e: float
+ px: float
+ py: float
+ pz: float
+ momentum: Vector3
+ beta: Vector3
+ m: float
+ m2: float
+ def __init__(self, e: float, px: float, py: float, pz: float): ...
+ def __add__(self, other: Vector4) -> Vector4: ...
+ def boost(self, beta: Vector3) -> Vector4: ...
+ def boost_along(self, other: Vector4) -> Vector4: ...
+ def to_numpy(self) -> npt.NDArray[np.float64]: ...
+ @staticmethod
+ def from_momentum(momentum: Vector3, mass: float) -> Vector4: ...
From 66726b0fe0b1e21c63ce582ac8c284f6a94fa2de Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 10:00:06 -0400
Subject: [PATCH 03/26] feat: add stable ABI with minimum python version of 3.7
---
Cargo.toml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/Cargo.toml b/Cargo.toml
index acec312..4aeb238 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,7 +29,10 @@ auto_ops = "0.3.0"
rand = "0.8.5"
rand_chacha = "0.3.1"
rayon = { version = "1.10.0", optional = true }
-pyo3 = { version = "0.22.5", optional = true, features = ["num-complex"] }
+pyo3 = { version = "0.22.5", optional = true, features = [
+ "num-complex",
+ "abi3-py37",
+] }
numpy = { version = "0.22.0", optional = true, features = ["nalgebra"] }
ganesh = "0.12.2"
thiserror = "1.0.64"
From f2c169decbf8d1ef8b011448ea00dcdb0a968bee Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 12:38:54 -0400
Subject: [PATCH 04/26] fix: use incremental builds for maturin development
---
.justfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.justfile b/.justfile
index a0574e3..d067674 100644
--- a/.justfile
+++ b/.justfile
@@ -2,4 +2,4 @@ default:
just --list
develop:
- maturin develop -r --uv --strip
+ CARGO_INCREMENTAL=true maturin develop -r --uv --strip
From 79379ccd2b00e1f035411769941e159834c879bc Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 12:39:28 -0400
Subject: [PATCH 05/26] docs: add automatic documentation and readthedocs
support
---
.readthedocs.yaml | 19 ++++++++++
docs/Makefile | 20 ++++++++++
docs/make.bat | 35 ++++++++++++++++++
docs/requirements.txt | 3 ++
docs/source/conf.py | 37 +++++++++++++++++++
docs/source/index.rst | 11 ++++++
docs/source/modules/index.rst | 10 +++++
.../modules/laddu/amplitudes/breit_wigner.rst | 6 +++
.../modules/laddu/amplitudes/common.rst | 6 +++
.../source/modules/laddu/amplitudes/index.rst | 12 ++++++
.../modules/laddu/amplitudes/kmatrix.rst | 6 +++
docs/source/modules/laddu/amplitudes/ylm.rst | 6 +++
docs/source/modules/laddu/amplitudes/zlm.rst | 6 +++
docs/source/modules/laddu/data.rst | 6 +++
docs/source/modules/laddu/likelihoods.rst | 6 +++
docs/source/modules/laddu/utils/index.rst | 9 +++++
docs/source/modules/laddu/utils/variables.rst | 6 +++
docs/source/modules/laddu/utils/vectors.rst | 6 +++
pyproject.toml | 1 +
19 files changed, 211 insertions(+)
create mode 100644 .readthedocs.yaml
create mode 100644 docs/Makefile
create mode 100644 docs/make.bat
create mode 100644 docs/requirements.txt
create mode 100644 docs/source/conf.py
create mode 100644 docs/source/index.rst
create mode 100644 docs/source/modules/index.rst
create mode 100644 docs/source/modules/laddu/amplitudes/breit_wigner.rst
create mode 100644 docs/source/modules/laddu/amplitudes/common.rst
create mode 100644 docs/source/modules/laddu/amplitudes/index.rst
create mode 100644 docs/source/modules/laddu/amplitudes/kmatrix.rst
create mode 100644 docs/source/modules/laddu/amplitudes/ylm.rst
create mode 100644 docs/source/modules/laddu/amplitudes/zlm.rst
create mode 100644 docs/source/modules/laddu/data.rst
create mode 100644 docs/source/modules/laddu/likelihoods.rst
create mode 100644 docs/source/modules/laddu/utils/index.rst
create mode 100644 docs/source/modules/laddu/utils/variables.rst
create mode 100644 docs/source/modules/laddu/utils/vectors.rst
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..1f0ca08
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,19 @@
+# .readthedocs.yaml
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+version: 2
+
+build:
+ os: ubuntu-24.04
+ tools:
+ python: "3.12"
+
+sphinx:
+ configuration: docs/conf.py
+
+formats:
+ - pdf
+
+python:
+ install:
+ - requirements: docs/requirements.txt
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d0c3cbf
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..747ffb7
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.https://www.sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..dee52b7
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,3 @@
+laddu
+sphinx==8.1.3
+sphinx-rtd-theme==3.0.1
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..c71ca5c
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,37 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+import laddu
+
+project = "laddu"
+copyright = "2024, Nathaniel Dene Hoffman"
+author = "Nathaniel Dene Hoffman"
+release = laddu.__version__
+version = release
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx.ext.napoleon"]
+
+autodoc_default_options = {
+ "members": True,
+ "undoc-members": True,
+}
+
+autodoc_member_order = "groupwise"
+
+templates_path = ["_templates"]
+exclude_patterns = []
+
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = "sphinx_rtd_theme"
+html_static_path = ["_static"]
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..00e03d6
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,11 @@
+laddu
+=====
+
+``laddu`` is an library for analyzing particle physics data. It is written in Rust with bindings to Python which are documented here.
+
+.. toctree::
+ :hidden:
+ :caption: API Reference
+ :name: sec-api
+
+ modules/index
diff --git a/docs/source/modules/index.rst b/docs/source/modules/index.rst
new file mode 100644
index 0000000..49e2e1d
--- /dev/null
+++ b/docs/source/modules/index.rst
@@ -0,0 +1,10 @@
+Submodules
+==========
+
+.. toctree::
+ :maxdepth: 1
+
+ laddu/amplitudes/index
+ laddu/data
+ laddu/likelihoods
+ laddu/utils/index
diff --git a/docs/source/modules/laddu/amplitudes/breit_wigner.rst b/docs/source/modules/laddu/amplitudes/breit_wigner.rst
new file mode 100644
index 0000000..048be14
--- /dev/null
+++ b/docs/source/modules/laddu/amplitudes/breit_wigner.rst
@@ -0,0 +1,6 @@
+Breit-Wigner
+============
+
+.. automodule:: laddu.amplitudes.breit_wigner
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/amplitudes/common.rst b/docs/source/modules/laddu/amplitudes/common.rst
new file mode 100644
index 0000000..fa4d078
--- /dev/null
+++ b/docs/source/modules/laddu/amplitudes/common.rst
@@ -0,0 +1,6 @@
+Common
+======
+
+.. automodule:: laddu.amplitudes.common
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/amplitudes/index.rst b/docs/source/modules/laddu/amplitudes/index.rst
new file mode 100644
index 0000000..2453a27
--- /dev/null
+++ b/docs/source/modules/laddu/amplitudes/index.rst
@@ -0,0 +1,12 @@
+Amplitudes
+==========
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Amplitudes:
+
+ common
+ ylm
+ zlm
+ breit_wigner
+ kmatrix
diff --git a/docs/source/modules/laddu/amplitudes/kmatrix.rst b/docs/source/modules/laddu/amplitudes/kmatrix.rst
new file mode 100644
index 0000000..6812c86
--- /dev/null
+++ b/docs/source/modules/laddu/amplitudes/kmatrix.rst
@@ -0,0 +1,6 @@
+K-Matrix
+========
+
+.. automodule:: laddu.amplitudes.kmatrix
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/amplitudes/ylm.rst b/docs/source/modules/laddu/amplitudes/ylm.rst
new file mode 100644
index 0000000..83463c2
--- /dev/null
+++ b/docs/source/modules/laddu/amplitudes/ylm.rst
@@ -0,0 +1,6 @@
+Ylm
+===
+
+.. automodule:: laddu.amplitudes.ylm
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/amplitudes/zlm.rst b/docs/source/modules/laddu/amplitudes/zlm.rst
new file mode 100644
index 0000000..80ab4fb
--- /dev/null
+++ b/docs/source/modules/laddu/amplitudes/zlm.rst
@@ -0,0 +1,6 @@
+Zlm
+===
+
+.. automodule:: laddu.amplitudes.zlm
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/data.rst b/docs/source/modules/laddu/data.rst
new file mode 100644
index 0000000..e39671f
--- /dev/null
+++ b/docs/source/modules/laddu/data.rst
@@ -0,0 +1,6 @@
+Data
+====
+
+.. automodule:: laddu.data
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/likelihoods.rst b/docs/source/modules/laddu/likelihoods.rst
new file mode 100644
index 0000000..51559c4
--- /dev/null
+++ b/docs/source/modules/laddu/likelihoods.rst
@@ -0,0 +1,6 @@
+Likelihoods
+===========
+
+.. automodule:: laddu.likelihoods
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/utils/index.rst b/docs/source/modules/laddu/utils/index.rst
new file mode 100644
index 0000000..d93e072
--- /dev/null
+++ b/docs/source/modules/laddu/utils/index.rst
@@ -0,0 +1,9 @@
+Utilities
+=========
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Utility Modules:
+
+ vectors
+ variables
diff --git a/docs/source/modules/laddu/utils/variables.rst b/docs/source/modules/laddu/utils/variables.rst
new file mode 100644
index 0000000..31c374d
--- /dev/null
+++ b/docs/source/modules/laddu/utils/variables.rst
@@ -0,0 +1,6 @@
+Variables
+=========
+
+.. automodule:: laddu.utils.variables
+ :members:
+ :undoc-members:
diff --git a/docs/source/modules/laddu/utils/vectors.rst b/docs/source/modules/laddu/utils/vectors.rst
new file mode 100644
index 0000000..d9a3d76
--- /dev/null
+++ b/docs/source/modules/laddu/utils/vectors.rst
@@ -0,0 +1,6 @@
+Vectors
+=======
+
+.. automodule:: laddu.utils.vectors
+ :members:
+ :undoc-members:
diff --git a/pyproject.toml b/pyproject.toml
index 26b78c1..87932f0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,6 +25,7 @@ amptools-to-laddu = "laddu:convert.run"
[project.optional-dependencies]
tests = ["pytest"]
+docs = ["sphinx", "sphinx-rtd-theme"]
[tool.maturin]
python-source = "python"
From 9452da71dca81e01972b94e7caf275f43504591a Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 12:52:05 -0400
Subject: [PATCH 06/26] fix: correct path to sphinx config
---
.readthedocs.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 1f0ca08..54286a4 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -9,7 +9,7 @@ build:
python: "3.12"
sphinx:
- configuration: docs/conf.py
+ configuration: docs/source/conf.py
formats:
- pdf
From c47bae2218082846b96aae58cdd48b40073da18e Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 20:17:55 -0400
Subject: [PATCH 07/26] ci: add documentation commands to justfile
---
.justfile | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/.justfile b/.justfile
index d067674..a55cfe4 100644
--- a/.justfile
+++ b/.justfile
@@ -3,3 +3,10 @@ default:
develop:
CARGO_INCREMENTAL=true maturin develop -r --uv --strip
+
+pydoc:
+ make -C docs clean
+ make -C docs html
+
+odoc:
+ firefox ./docs/build/html/index.html
From e53494f6cb530beee8e19c4d743e17f9752a859d Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 20:18:14 -0400
Subject: [PATCH 08/26] fix: add amplitude module-level documentation
---
docs/source/modules/laddu/amplitudes/index.rst | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docs/source/modules/laddu/amplitudes/index.rst b/docs/source/modules/laddu/amplitudes/index.rst
index 2453a27..49304f3 100644
--- a/docs/source/modules/laddu/amplitudes/index.rst
+++ b/docs/source/modules/laddu/amplitudes/index.rst
@@ -1,6 +1,11 @@
Amplitudes
==========
+.. automodule:: laddu.amplitudes
+ :members:
+ :undoc-members:
+
+
.. toctree::
:maxdepth: 1
:caption: Amplitudes:
From 8ccaef92397cebc68072aef82871e30715aaac1b Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Fri, 1 Nov 2024 20:18:51 -0400
Subject: [PATCH 09/26] feat: test documentation
---
src/python.rs | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/python.rs b/src/python.rs
index c8c05a7..94c8c91 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -46,7 +46,21 @@ pub(crate) mod laddu {
fn __add__(&self, other: Self) -> Self {
Self(self.0 + other.0)
}
- fn dot(&self, other: Self) -> Float {
+ /// The dot product
+ ///
+ /// This method is documented.
+ ///
+ /// Parameters
+ /// ----------
+ /// other : Vector3
+ /// A vector input with which the dot product is taken
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The dot product of this vector and `other`
+ ///
+ pub fn dot(&self, other: Self) -> Float {
self.0.dot(&other.0)
}
fn cross(&self, other: Self) -> Self {
From 655055cc1493f88295d55482734961be3d498cca Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Sun, 3 Nov 2024 13:21:47 -0500
Subject: [PATCH 10/26] fix: correct `PolAngle` by normalizing the beam vector
---
src/utils/variables.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/utils/variables.rs b/src/utils/variables.rs
index 6781f40..f74dec4 100644
--- a/src/utils/variables.rs
+++ b/src/utils/variables.rs
@@ -268,7 +268,7 @@ impl Variable for PolAngle {
let y = beam.vec3().cross(&-recoil.vec3()).unit();
Float::atan2(
y.dot(&event.eps[self.beam]),
- beam.vec3().dot(&event.eps[self.beam].cross(&y)),
+ beam.vec3().unit().dot(&event.eps[self.beam].cross(&y)),
)
}
}
From 48b4484e13e2875aae36333829fc01ca6b63ccb4 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Sun, 3 Nov 2024 13:23:48 -0500
Subject: [PATCH 11/26] fix: correct phase in Zlm
---
src/amplitudes/zlm.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/amplitudes/zlm.rs b/src/amplitudes/zlm.rs
index bf32c39..81982b4 100644
--- a/src/amplitudes/zlm.rs
+++ b/src/amplitudes/zlm.rs
@@ -70,7 +70,7 @@ impl Amplitude for Zlm {
);
let pol_angle = self.polarization.pol_angle.value(event);
let pgamma = self.polarization.pol_magnitude.value(event);
- let phase = Complex::new(Float::cos(pol_angle), Float::sin(pol_angle));
+ let phase = Complex::new(Float::cos(-pol_angle), Float::sin(-pol_angle));
let zlm = ylm * phase;
cache.store_complex_scalar(
self.csid,
From 0f564faacda0a2e497d521128d1954f92006f5a5 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Sun, 3 Nov 2024 14:23:47 -0500
Subject: [PATCH 12/26] fix: use the unweighted total number of events and
divide data likelihood terms by `n_data`
This seems to achieve parity with AmpTools
---
src/likelihoods.rs | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/likelihoods.rs b/src/likelihoods.rs
index 0cd04a0..5431ac7 100644
--- a/src/likelihoods.rs
+++ b/src/likelihoods.rs
@@ -113,7 +113,7 @@ impl NLL {
#[cfg(feature = "rayon")]
pub fn project(&self, parameters: &[Float]) -> Vec {
let mc_result = self.mc_evaluator.evaluate(parameters);
- let n_mc = self.mc_evaluator.dataset.weighted_len();
+ let n_mc = self.mc_evaluator.dataset.len() as Float;
mc_result
.par_iter()
.zip(self.mc_evaluator.dataset.par_iter())
@@ -134,7 +134,7 @@ impl NLL {
#[cfg(not(feature = "rayon"))]
pub fn project(&self, parameters: &[Float]) -> Vec {
let mc_result = self.mc_evaluator.evaluate(parameters);
- let n_mc = self.mc_evaluator.dataset.weighted_len();
+ let n_mc = self.mc_evaluator.dataset.len() as Float;
mc_result
.iter()
.zip(self.mc_evaluator.dataset.iter())
@@ -162,17 +162,18 @@ impl LikelihoodTerm for NLL {
/// result is given by the following formula:
///
/// ```math
- /// NLL(\vec{p}) = -2 \left(\sum_{e \in \text{Data}} \text{weight}(e) \ln(\mathcal{L}(e)) - \frac{1}{N_{\text{MC}}} \sum_{e \in \text{MC}} \text{weight}(e) \mathcal{L}(e) \right)
+ /// NLL(\vec{p}) = -2 \left(\sum_{e \in \text{Data}} \text{weight}(e) \ln(\mathcal{L}(e) / N_{\text{DATA}}) - \frac{1}{N_{\text{MC}}} \sum_{e \in \text{MC}} \text{weight}(e) \mathcal{L}(e) \right)
/// ```
#[cfg(feature = "rayon")]
fn evaluate(&self, parameters: &[Float]) -> Float {
let data_result = self.data_evaluator.evaluate(parameters);
+ let n_data = self.data_evaluator.dataset.len() as Float;
let mc_result = self.mc_evaluator.evaluate(parameters);
- let n_mc = self.mc_evaluator.dataset.weighted_len();
+ let n_mc = self.mc_evaluator.dataset.len() as Float;
let data_term: Float = data_result
.par_iter()
.zip(self.data_evaluator.dataset.par_iter())
- .map(|(l, e)| e.weight * Float::ln(l.re))
+ .map(|(l, e)| e.weight * Float::ln(l.re / n_data))
.parallel_sum_with_accumulator::>();
let mc_term: Float = mc_result
.par_iter()
@@ -189,17 +190,18 @@ impl LikelihoodTerm for NLL {
/// result is given by the following formula:
///
/// ```math
- /// NLL(\vec{p}) = -2 \left(\sum_{e \in \text{Data}} \text{weight}(e) \ln(\mathcal{L}(e)) - \frac{N_{\text{Data}}}{N_{\text{MC}}} \sum_{e \in \text{MC}} \text{weight}(e) \mathcal{L}(e) \right)
+ /// NLL(\vec{p}) = -2 \left(\sum_{e \in \text{Data}} \text{weight}(e) \ln(\mathcal{L}(e) / N_{\text{DATA}}) - \frac{1}{N_{\text{MC}}} \sum_{e \in \text{MC}} \text{weight}(e) \mathcal{L}(e) \right)
/// ```
#[cfg(not(feature = "rayon"))]
fn evaluate(&self, parameters: &[Float]) -> Float {
let data_result = self.data_evaluator.evaluate(parameters);
+ let n_data = self.data_evaluator.dataset.len() as Float;
let mc_result = self.mc_evaluator.evaluate(parameters);
- let n_mc = self.mc_evaluator.dataset.weighted_len();
+ let n_mc = self.mc_evaluator.dataset.len() as Float;
let data_term: Float = data_result
.iter()
.zip(self.data_evaluator.dataset.iter())
- .map(|(l, e)| e.weight * Float::ln(l.re))
+ .map(|(l, e)| e.weight * Float::ln(l.re / n_data))
.sum_with_accumulator::>();
let mc_term: Float = mc_result
.iter()
@@ -220,7 +222,7 @@ impl LikelihoodTerm for NLL {
let data_parameters = Parameters::new(parameters, &data_resources.constants);
let mc_resources = self.mc_evaluator.resources.read();
let mc_parameters = Parameters::new(parameters, &mc_resources.constants);
- let n_mc = self.mc_evaluator.dataset.weighted_len();
+ let n_mc = self.mc_evaluator.dataset.len() as Float;
let data_term: DVector = self
.data_evaluator
.dataset
@@ -336,7 +338,7 @@ impl LikelihoodTerm for NLL {
let n_data = self.data_evaluator.dataset.weighted_len();
let mc_resources = self.mc_evaluator.resources.read();
let mc_parameters = Parameters::new(parameters, &mc_resources.constants);
- let n_mc = self.mc_evaluator.dataset.weighted_len();
+ let n_mc = self.mc_evaluator.dataset.len() as Float;
let data_term: DVector = self
.data_evaluator
.dataset
From b204769acf209ffecf0a6d8d9385dcdadb0fe543 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Sun, 3 Nov 2024 14:24:20 -0500
Subject: [PATCH 13/26] ci: docstrings are not exported with `maturin develop`
---
.justfile | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.justfile b/.justfile
index a55cfe4..db1b84f 100644
--- a/.justfile
+++ b/.justfile
@@ -4,7 +4,9 @@ default:
develop:
CARGO_INCREMENTAL=true maturin develop -r --uv --strip
-pydoc:
+makedocs:
+ CARGO_INCREMENTAL=true maturin build -r --strip
+ uv pip install ./target/wheels/*
make -C docs clean
make -C docs html
From 40e2252f5cc49712942e4349959f51b858790b88 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Sun, 3 Nov 2024 16:30:34 -0500
Subject: [PATCH 14/26] fix: update results of `example_1` given likelihood and
Zlm changes
---
python_examples/example_1/example_1.svg | 3474 +++++++++++------------
python_examples/example_1/example_1.txt | 2 +-
2 files changed, 1738 insertions(+), 1738 deletions(-)
diff --git a/python_examples/example_1/example_1.svg b/python_examples/example_1/example_1.svg
index 18e684d..f08d2b6 100644
--- a/python_examples/example_1/example_1.svg
+++ b/python_examples/example_1/example_1.svg
@@ -6,7 +6,7 @@
- 2024-10-31T15:42:10.046502
+ 2024-11-03T15:14:47.292893
image/svg+xml
@@ -40,813 +40,813 @@ z
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
+" clip-path="url(#pcae40e8602)" style="fill: #007bc0; opacity: 0.1"/>
-
-
+
@@ -905,7 +905,7 @@ z
-
+
@@ -946,7 +946,7 @@ z
-
+
@@ -982,7 +982,7 @@ z
-
+
@@ -1029,7 +1029,7 @@ z
-
+
@@ -1085,7 +1085,7 @@ z
-
+
@@ -1100,124 +1100,124 @@ z
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1542,12 +1542,12 @@ z
-
-
+
@@ -1560,12 +1560,12 @@ L 8 0
-
+
-
+
@@ -1576,12 +1576,12 @@ L 8 0
-
+
-
+
@@ -1592,12 +1592,12 @@ L 8 0
-
+
-
+
@@ -1608,12 +1608,12 @@ L 8 0
-
+
-
+
@@ -1624,12 +1624,12 @@ L 8 0
-
+
-
+
@@ -1641,12 +1641,12 @@ L 8 0
-
+
-
+
@@ -1658,12 +1658,12 @@ L 8 0
-
+
-
+
@@ -1675,173 +1675,173 @@ L 8 0
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1956,416 +1956,416 @@ z
+" clip-path="url(#pcae40e8602)" style="fill: none; stroke: #000000; stroke-linejoin: miter"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2729,7 +2729,7 @@ L 574.585 152.53125
-
+
@@ -2757,7 +2757,7 @@ L 574.585 189.4425
-
+
@@ -2791,808 +2791,808 @@ z
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
+" clip-path="url(#p6180711b8f)" style="fill: #ef3a47; opacity: 0.1"/>
-
+
@@ -3607,7 +3607,7 @@ z
-
+
@@ -3622,7 +3622,7 @@ z
-
+
@@ -3637,7 +3637,7 @@ z
-
+
@@ -3652,7 +3652,7 @@ z
-
+
@@ -3667,7 +3667,7 @@ z
-
+
@@ -3682,119 +3682,119 @@ z
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -3831,224 +3831,224 @@ z
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -4078,470 +4078,470 @@ z
+" clip-path="url(#p6180711b8f)" style="fill: none; stroke: #000000; stroke-linejoin: miter"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4743,7 +4743,7 @@ L 1314.72 152.29125
-
+
@@ -4771,7 +4771,7 @@ L 1314.72 189.2025
-
+
@@ -4795,10 +4795,10 @@ L 1314.72 189.2025
-
+
-
+
diff --git a/python_examples/example_1/example_1.txt b/python_examples/example_1/example_1.txt
index 6930ad9..872e63e 100644
--- a/python_examples/example_1/example_1.txt
+++ b/python_examples/example_1/example_1.txt
@@ -2,5 +2,5 @@
┃ ┃ f0(1500) Width ┃ f2'(1525) Width ┃ S/D Ratio ┃ S-D Phase ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ Truth │ 0.11200 │ 0.08600 │ 1.41421 │ 0.78540 │
-│ Fit │ 0.10828 ± 0.00079 │ 0.08575 ± 0.00039 │ 1.46706 ± 0.00479 │ 0.93561 ± 0.00314 │
+│ Fit │ 0.11252 ± 0.00104 │ 0.08656 ± 0.00031 │ 1.44949 ± 0.00771 │ 0.80024 ± 0.00369 │
└───────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘
From 8119755c037620026b9e13256a5d8015c3278083 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Sun, 3 Nov 2024 16:57:27 -0500
Subject: [PATCH 15/26] docs: add documentation for `Vector3` in Python API
---
src/python.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 134 insertions(+), 1 deletion(-)
diff --git a/src/python.rs b/src/python.rs
index 94c8c91..23625ec 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -34,6 +34,18 @@ pub(crate) mod laddu {
env!("CARGO_PKG_VERSION").to_string()
}
+ /// A 3-momentum vector formed from Cartesian components
+ ///
+ /// Parameters
+ /// ----------
+ /// px, py, pz : float
+ /// The Cartesian components of the 3-vector
+ ///
+ /// Returns
+ /// -------
+ /// Vector3
+ /// A new 3-momentum vector made from the given components
+ ///
#[pyclass]
#[derive(Clone)]
struct Vector3(nalgebra::Vector3);
@@ -48,7 +60,7 @@ pub(crate) mod laddu {
}
/// The dot product
///
- /// This method is documented.
+ /// Calculates the dot product of two Vector3s.
///
/// Parameters
/// ----------
@@ -63,45 +75,166 @@ pub(crate) mod laddu {
pub fn dot(&self, other: Self) -> Float {
self.0.dot(&other.0)
}
+ /// The cross product
+ ///
+ /// Calculates the cross product of two Vector3s.
+ ///
+ /// Parameters
+ /// ----------
+ /// other : Vector3
+ /// A vector input with which the cross product is taken
+ ///
+ /// Returns
+ /// -------
+ /// Vector3
+ /// The cross product of this vector and `other`
+ ///
fn cross(&self, other: Self) -> Self {
Self(self.0.cross(&other.0))
}
+ /// The magnitude of the 3-vector
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: \sqrt{p_x^2 + p_y^2 + p_z^2}
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The magnitude of this vector
#[getter]
fn mag(&self) -> Float {
self.0.mag()
}
+ /// The magnitude-squared of the 3-vector
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: p_x^2 + p_y^2 + p_z^2
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The squared magnitude of this vector
+ ///
#[getter]
fn mag2(&self) -> Float {
self.0.mag2()
}
+ /// The cosine of the polar angle of this vector in spherical coordinates
+ ///
+ /// The polar angle is defined in the range
+ ///
+ /// .. math:: 0 \leq \theta \leq \pi
+ ///
+ /// so the cosine falls in the range
+ ///
+ /// .. math:: -1 \leq \cos\theta \leq +1
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: \cos\theta = \frac{p_z}{|\vec{p}|}
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The cosine of the polar angle of this vector
+ ///
#[getter]
fn costheta(&self) -> Float {
self.0.costheta()
}
+ /// The polar angle of this vector in spherical coordinates
+ ///
+ /// The polar angle is defined in the range
+ ///
+ /// .. math:: 0 \leq \theta \leq \pi
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: \theta = \arccos\left(\frac{p_z}{|\vec{p}|}\right)
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The polar angle of this vector
+ ///
#[getter]
fn theta(&self) -> Float {
self.0.theta()
}
+ /// The azimuthal angle of this vector in spherical coordinates
+ ///
+ /// The azimuthal angle is defined in the range
+ ///
+ /// .. math:: 0 \leq \varphi \leq 2\pi
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: \varphi = \text{sgn}(p_y)\arccos\left(\frac{p_x}{\sqrt{p_x^2 + p_y^2}}\right)
+ ///
+ /// although the actual calculation just uses the `atan2` function
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The azimuthal angle of this vector
+ ///
#[getter]
fn phi(&self) -> Float {
self.0.phi()
}
+ /// The normalized unit vector pointing in the direction of this vector
+ ///
+ /// Returns
+ /// -------
+ /// Vector3
+ /// A unit vector pointing in the same direction as this vector
+ ///
#[getter]
fn unit(&self) -> Self {
Self(self.0.unit())
}
+ /// The x-component of this vector
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The x-component
+ ///
#[getter]
fn px(&self) -> Float {
self.0.px()
}
+ /// The y-component of this vector
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The y-component
+ ///
#[getter]
fn py(&self) -> Float {
self.0.py()
}
+ /// The z-component of this vector
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The z-component
+ ///
#[getter]
fn pz(&self) -> Float {
self.0.pz()
}
+ /// Convert the 3-vector to a `numpy` array
+ ///
+ /// Returns
+ /// -------
+ /// numpy_vec: array_like
+ /// A `numpy` array built from the components of this `Vector3`
+ ///
fn to_numpy<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, self.0.as_slice())
}
From 1975fee36329b8834f3e0eb9ee4aac80c2501a2a Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Mon, 4 Nov 2024 10:24:16 -0500
Subject: [PATCH 16/26] feat: add gamma factor calculation to 4-momentum
---
python/laddu/utils/vectors/__init__.pyi | 1 +
src/python.rs | 4 ++++
src/utils/vectors.rs | 8 ++++++++
3 files changed, 13 insertions(+)
diff --git a/python/laddu/utils/vectors/__init__.pyi b/python/laddu/utils/vectors/__init__.pyi
index ad039ab..4c93fd1 100644
--- a/python/laddu/utils/vectors/__init__.pyi
+++ b/python/laddu/utils/vectors/__init__.pyi
@@ -26,6 +26,7 @@ class Vector4:
py: float
pz: float
momentum: Vector3
+ gamma: float
beta: Vector3
m: float
m2: float
diff --git a/src/python.rs b/src/python.rs
index 23625ec..7671f7d 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -294,6 +294,10 @@ pub(crate) mod laddu {
Vector3(self.0.momentum().into())
}
#[getter]
+ fn gamma(&self) -> Float {
+ self.0.gamma()
+ }
+ #[getter]
fn beta(&self) -> Vector3 {
Vector3(self.0.beta())
}
diff --git a/src/utils/vectors.rs b/src/utils/vectors.rs
index e2729c5..12003a4 100644
--- a/src/utils/vectors.rs
+++ b/src/utils/vectors.rs
@@ -26,6 +26,8 @@ pub trait FourMomentum: FourVector {
fn pz(&self) -> Float;
/// The three-momentum
fn momentum(&self) -> VectorView;
+ /// The $`\gamma`$ factor $`\frac{1}{\sqrt{1 - \beta^2}}`$.
+ fn gamma(&self) -> Float;
/// The $`\vec{\beta}`$ vector $`\frac{\vec{p}}{E}`$.
fn beta(&self) -> Vector3;
/// The mass of the corresponding object.
@@ -118,6 +120,12 @@ impl FourMomentum for Vector4 {
self.vec3()
}
+ fn gamma(&self) -> Float {
+ let beta = self.beta();
+ let b2 = beta.dot(&beta);
+ 1.0 / Float::sqrt(1.0 - b2)
+ }
+
fn beta(&self) -> Vector3 {
self.momentum().unscale(self.e())
}
From 2d6ade8695d3138689ab77fc80bf5b90ba9f36b1 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Mon, 4 Nov 2024 10:24:44 -0500
Subject: [PATCH 17/26] docs: document`vectors` Python API
---
src/python.rs | 277 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 271 insertions(+), 6 deletions(-)
diff --git a/src/python.rs b/src/python.rs
index 7671f7d..9790174 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -96,21 +96,22 @@ pub(crate) mod laddu {
///
/// This is calculated as:
///
- /// .. math:: \sqrt{p_x^2 + p_y^2 + p_z^2}
+ /// .. math:: |\vec{p}| = \sqrt{p_x^2 + p_y^2 + p_z^2}
///
/// Returns
/// -------
/// float
/// The magnitude of this vector
+ ///
#[getter]
fn mag(&self) -> Float {
self.0.mag()
}
- /// The magnitude-squared of the 3-vector
+ /// The squared magnitude of the 3-vector
///
/// This is calculated as:
///
- /// .. math:: p_x^2 + p_y^2 + p_z^2
+ /// .. math:: |\vec{p}|^2 = p_x^2 + p_y^2 + p_z^2
///
/// Returns
/// -------
@@ -173,7 +174,7 @@ pub(crate) mod laddu {
///
/// .. math:: \varphi = \text{sgn}(p_y)\arccos\left(\frac{p_x}{\sqrt{p_x^2 + p_y^2}}\right)
///
- /// although the actual calculation just uses the `atan2` function
+ /// although the actual calculation just uses the ``atan2`` function
///
/// Returns
/// -------
@@ -228,12 +229,12 @@ pub(crate) mod laddu {
fn pz(&self) -> Float {
self.0.pz()
}
- /// Convert the 3-vector to a `numpy` array
+ /// Convert the 3-vector to a ``numpy`` array
///
/// Returns
/// -------
/// numpy_vec: array_like
- /// A `numpy` array built from the components of this `Vector3`
+ /// A ``numpy`` array built from the components of this ``Vector3``
///
fn to_numpy<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, self.0.as_slice())
@@ -246,6 +247,23 @@ pub(crate) mod laddu {
}
}
+ /// A 4-momentum vector formed from energy and Cartesian 3-momentum components
+ ///
+ /// This vector is ordered with energy as the zeroeth component (:math:`[E, p_x, p_y, p_z]`) and assumes a :math:`(+---)`
+ /// signature
+ ///
+ /// Parameters
+ /// ----------
+ /// e : float
+ /// The energy component
+ /// px, py, pz : float
+ /// The Cartesian components of the 3-vector
+ ///
+ /// Returns
+ /// -------
+ /// Vector4
+ /// A new 4-momentum vector made from the given components
+ ///
#[pyclass]
#[derive(Clone)]
struct Vector4(nalgebra::Vector4);
@@ -258,64 +276,277 @@ pub(crate) mod laddu {
fn __add__(&self, other: Self) -> Self {
Self(self.0 + other.0)
}
+ /// The magnitude of the 4-vector
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: |p| = \sqrt{E^2 - (p_x^2 + p_y^2 + p_z^2)}
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The magnitude of this vector
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.m
+ ///
#[getter]
fn mag(&self) -> Float {
self.0.mag()
}
+ /// The squared magnitude of the 4-vector
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: |p|^2 = E^2 - (p_x^2 + p_y^2 + p_z^2)
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The squared magnitude of this vector
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.m2
+ ///
#[getter]
fn mag2(&self) -> Float {
self.0.mag2()
}
+ /// The 3-vector part of this 4-vector
+ ///
+ /// Returns
+ /// -------
+ /// Vector3
+ /// The internal 3-vector
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.momentum
+ ///
#[getter]
fn vec3(&self) -> Vector3 {
Vector3(self.0.vec3().into())
}
+ /// Boost the given 4-momentum according to a boost velocity
+ ///
+ /// The resulting 4-momentum is equal to the original boosted to an inertial frame with
+ /// relative velocity :math:`\beta`:
+ ///
+ /// .. math:: \left[E'; \vec{p}'\right] = \left[ \gamma E - \vec{\beta}\cdot\vec{p}; \vec{p} + \left(\frac{(\gamma - 1) \vec{p}\cdot\vec{\beta}}{\beta^2} - \gamma E\right)\vec{\beta}\right]
+ ///
+ /// Parameters
+ /// ----------
+ /// beta : Vector3
+ /// The relative velocity needed to get to the new frame from the current one
+ ///
+ /// Returns
+ /// -------
+ /// Vector4
+ /// The boosted 4-momentum
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.beta
+ /// Vector4.gamma
+ /// Vector4.boost_along
+ ///
fn boost(&self, beta: &Vector3) -> Self {
Self(self.0.boost(&beta.0))
}
+ /// The energy associated with this vector
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The energy
+ ///
#[getter]
fn e(&self) -> Float {
self.0[0]
}
+ /// The x-component of this vector
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The x-component
+ ///
#[getter]
fn px(&self) -> Float {
self.0.px()
}
+ /// The y-component of this vector
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The y-component
+ ///
#[getter]
fn py(&self) -> Float {
self.0.py()
}
+ /// The z-component of this vector
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The z-component
+ ///
#[getter]
fn pz(&self) -> Float {
self.0.pz()
}
+ /// The 3-momentum part of this 4-momentum
+ ///
+ /// Returns
+ /// -------
+ /// Vector3
+ /// The internal 3-momentum
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.vec3
+ ///
#[getter]
fn momentum(&self) -> Vector3 {
Vector3(self.0.momentum().into())
}
+ /// The relativistic gamma factor
+ ///
+ /// The :math:`\gamma` factor is equivalent to
+ ///
+ /// .. math:: \gamma = \frac{1}{\sqrt{1 - \beta^2}}
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The associated :math:`\gamma` factor
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.beta
+ /// Vector4.boost
+ /// Vector4.boost_along
+ ///
#[getter]
fn gamma(&self) -> Float {
self.0.gamma()
}
+ /// The velocity 3-vector
+ ///
+ /// The :math:`\beta` vector is equivalent to
+ ///
+ /// .. math:: \vec{\beta} = \frac{\vec{p}}{E}
+ ///
+ /// Returns
+ /// -------
+ /// Vector3
+ /// The associated velocity vector
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.gamma
+ /// Vector4.boost
+ /// Vector4.boost_along
+ ///
#[getter]
fn beta(&self) -> Vector3 {
Vector3(self.0.beta())
}
+ /// The invariant mass associated with the four-momentum
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: m = \sqrt{E^2 - (p_x^2 + p_y^2 + p_z^2)}
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The magnitude of this vector
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.mag
+ ///
#[getter]
fn m(&self) -> Float {
self.0.m()
}
+ /// The square of the invariant mass associated with the four-momentum
+ ///
+ /// This is calculated as:
+ ///
+ /// .. math:: m^2 = E^2 - (p_x^2 + p_y^2 + p_z^2)
+ ///
+ /// Returns
+ /// -------
+ /// float
+ /// The squared magnitude of this vector
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.mag2
+ ///
#[getter]
fn m2(&self) -> Float {
self.0.m2()
}
+ /// Boost the given 4-momentum into the rest frame of another
+ ///
+ /// The resulting 4-momentum is equal to the original boosted into the rest frame
+ /// of `other`
+ ///
+ /// Boosting a vector by itself should yield that vector in its own rest frame
+ ///
+ /// Parameters
+ /// ----------
+ /// other : Vector4
+ /// The 4-momentum whose rest-frame is the boost target
+ ///
+ /// Returns
+ /// -------
+ /// Vector4
+ /// The boosted 4-momentum
+ ///
+ /// See Also
+ /// --------
+ /// Vector4.boost
+ ///
fn boost_along(&self, other: &Self) -> Self {
Self(self.0.boost_along(&other.0))
}
+ /// Construct a 4-momentum from a 3-momentum and corresponding `mass`
+ ///
+ /// The mass-energy equivalence is used to compute the energy of the 4-momentum:
+ ///
+ /// .. math:: E = \sqrt{m^2 + p^2}
+ ///
+ /// Parameters
+ /// ----------
+ /// momentum : Vector3
+ /// The spatial 3-momentum
+ /// mass : float
+ /// The associated rest mass
+ ///
+ /// Returns
+ /// -------
+ /// Vector4
+ /// A new 4-momentum with the given spatial `momentum` and `mass`
+ ///
#[staticmethod]
fn from_momentum(momentum: &Vector3, mass: Float) -> Self {
Self(nalgebra::Vector4::from_momentum(&momentum.0, mass))
}
+ /// Convert the 4-vector to a `numpy` array
+ ///
+ /// Returns
+ /// -------
+ /// numpy_vec: array_like
+ /// A ``numpy`` array built from the components of this ``Vector4``
+ ///
fn to_numpy<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, self.0.as_slice())
}
@@ -327,6 +558,27 @@ pub(crate) mod laddu {
}
}
+ /// A single event
+ ///
+ /// Events are composed of a set of 4-momenta of particles in the overall
+ /// center-of-momentum frame, polarizations or helicities described by 3-vectors, and a
+ /// weight
+ ///
+ /// Parameters
+ /// ----------
+ /// p4s : list[Vector4]
+ /// 4-momenta of each particle in the event in the overall center-of-momentum frame
+ /// eps : list[Vector3]
+ /// 3-vectors describing the polarization or helicity of the particles
+ /// given in `p4s`
+ /// weight : float
+ /// The weight associated with this event
+ ///
+ /// Returns
+ /// -------
+ /// event : Event
+ /// An event formed from the given components
+ ///
#[pyclass]
#[derive(Clone)]
struct Event(Arc);
@@ -344,20 +596,33 @@ pub(crate) mod laddu {
pub(crate) fn __str__(&self) -> String {
self.0.to_string()
}
+ /// The list of 4-momenta for each particle in the event
+ ///
#[getter]
pub(crate) fn get_p4s(&self) -> Vec {
self.0.p4s.iter().map(|p4| Vector4(*p4)).collect()
}
+ /// The list of 3-vectors describing the polarization or helicity of particles in
+ /// the event
+ ///
#[getter]
pub(crate) fn get_eps(&self) -> Vec {
self.0.eps.iter().map(|eps_vec| Vector3(*eps_vec)).collect()
}
+ /// The weight of this event relative to othersvin a Dataset
+ ///
#[getter]
pub(crate) fn get_weight(&self) -> Float {
self.0.weight
}
}
+ /// A set of Events
+ ///
+ /// Parameters
+ /// ----------
+ /// events : list[Event]
+ ///
#[pyclass]
#[derive(Clone)]
struct Dataset(Arc);
From 9bd054c1f26ead820d4d38f2b18356234e7d3196 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Tue, 5 Nov 2024 16:44:03 -0500
Subject: [PATCH 18/26] fix: make sure code works if no pol angle/magnitude are
provided
---
python/laddu/convert.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/laddu/convert.py b/python/laddu/convert.py
index 1eb8a6e..dee9cde 100644
--- a/python/laddu/convert.py
+++ b/python/laddu/convert.py
@@ -125,8 +125,8 @@ def run():
output_file = args[""]
tree_name = args["--tree"]
pol_in_beam = args["--pol-in-beam"]
- pol_angle = float(args["--pol-angle"]) * np.pi / 180
- pol_magnitude = float(args["--pol-magnitude"])
+ pol_angle = float(args["--pol-angle"]) * np.pi / 180 if args["--pol-angle"] else None
+ pol_magnitude = float(args["--pol-magnitude"]) if args["--pol-magnitude"] else None
num_entries = int(args["-n"]) if args["-n"] else None
convert_from_amptools(
From 74af349793979195c02d1828a7317c451f1d7a21 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Tue, 5 Nov 2024 16:44:30 -0500
Subject: [PATCH 19/26] docs: fix data format which said that `eps` vectors
have a "p" in their column names
---
README.md | 6 +++---
src/lib.rs | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index 817ea7a..7565ff4 100644
--- a/README.md
+++ b/README.md
@@ -246,9 +246,9 @@ The data format for `laddu` is a bit different from some of the alternatives lik
| `p4_0_Px` | `Float32` | Beam Momentum (x-component) |
| `p4_0_Py` | `Float32` | Beam Momentum (y-component) |
| `p4_0_Pz` | `Float32` | Beam Momentum (z-component) |
-| `eps_0_Px` | `Float32` | Beam Polarization (x-component) |
-| `eps_0_Py` | `Float32` | Beam Polarization (y-component) |
-| `eps_0_Pz` | `Float32` | Beam Polarization (z-component) |
+| `eps_0_x` | `Float32` | Beam Polarization (x-component) |
+| `eps_0_y` | `Float32` | Beam Polarization (y-component) |
+| `eps_0_z` | `Float32` | Beam Polarization (z-component) |
| `p4_1_E` | `Float32` | Recoil Proton Energy |
| `p4_1_Px` | `Float32` | Recoil Proton Momentum (x-component) |
| `p4_1_Py` | `Float32` | Recoil Proton Momentum (y-component) |
diff --git a/src/lib.rs b/src/lib.rs
index c6ca0a1..46a256a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -220,9 +220,9 @@
//! | `p4_0_Px` | `Float32` | Beam Momentum (x-component) |
//! | `p4_0_Py` | `Float32` | Beam Momentum (y-component) |
//! | `p4_0_Pz` | `Float32` | Beam Momentum (z-component) |
-//! | `eps_0_Px` | `Float32` | Beam Polarization (x-component) |
-//! | `eps_0_Py` | `Float32` | Beam Polarization (y-component) |
-//! | `eps_0_Pz` | `Float32` | Beam Polarization (z-component) |
+//! | `eps_0_x` | `Float32` | Beam Polarization (x-component) |
+//! | `eps_0_y` | `Float32` | Beam Polarization (y-component) |
+//! | `eps_0_z` | `Float32` | Beam Polarization (z-component) |
//! | `p4_1_E` | `Float32` | Recoil Proton Energy |
//! | `p4_1_Px` | `Float32` | Recoil Proton Momentum (x-component) |
//! | `p4_1_Py` | `Float32` | Recoil Proton Momentum (y-component) |
From c73dbfc709865875bd854da27257e046b57e72c8 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Tue, 5 Nov 2024 16:45:51 -0500
Subject: [PATCH 20/26] docs: more documentation for Python API
I think I might want to redocument some classes using the `Attributes` field, but I'd like to get the PR through before I worry about the nitpicks since there are other important commits here
---
src/python.rs | 625 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 621 insertions(+), 4 deletions(-)
diff --git a/src/python.rs b/src/python.rs
index 9790174..e7b6262 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -566,9 +566,9 @@ pub(crate) mod laddu {
///
/// Parameters
/// ----------
- /// p4s : list[Vector4]
+ /// p4s : list of Vector4
/// 4-momenta of each particle in the event in the overall center-of-momentum frame
- /// eps : list[Vector3]
+ /// eps : list of Vector3
/// 3-vectors describing the polarization or helicity of the particles
/// given in `p4s`
/// weight : float
@@ -609,7 +609,7 @@ pub(crate) mod laddu {
pub(crate) fn get_eps(&self) -> Vec {
self.0.eps.iter().map(|eps_vec| Vector3(*eps_vec)).collect()
}
- /// The weight of this event relative to othersvin a Dataset
+ /// The weight of this event relative to others in a Dataset
///
#[getter]
pub(crate) fn get_weight(&self) -> Float {
@@ -619,9 +619,17 @@ pub(crate) mod laddu {
/// A set of Events
///
+ /// Datasets can be created from lists of Events or by using the provided ``laddu.open`` function
+ ///
+ /// Datasets can also be indexed directly to access individual Events
+ ///
/// Parameters
/// ----------
- /// events : list[Event]
+ /// events : list of Event
+ ///
+ /// See Also
+ /// --------
+ /// laddu.open
///
#[pyclass]
#[derive(Clone)]
@@ -638,16 +646,44 @@ pub(crate) mod laddu {
fn __len__(&self) -> usize {
self.0.len()
}
+ /// Get the number of Events in the Dataset
+ ///
+ /// Returns
+ /// -------
+ /// n_events : int
+ /// The number of Events
+ ///
fn len(&self) -> usize {
self.0.len()
}
+ /// Get the weighted number of Events in the Dataset
+ ///
+ /// Returns
+ /// -------
+ /// n_events : float
+ /// The sum of all Event weights
+ ///
fn weighted_len(&self) -> Float {
self.0.weighted_len()
}
+ /// The weights associated with the Dataset
+ ///
+ /// Returns
+ /// -------
+ /// weights : array_like
+ /// A ``numpy`` array of Event weights
+ ///
#[getter]
fn weights<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.weights())
}
+ /// The internal list of Events stored in the Dataset
+ ///
+ /// Returns
+ /// -------
+ /// events : list of Event
+ /// The Events in the Dataset
+ ///
#[getter]
fn events(&self) -> Vec {
self.0
@@ -662,6 +698,28 @@ pub(crate) mod laddu {
.ok_or(PyIndexError::new_err("index out of range"))
.map(|rust_event| Event(rust_event.clone()))
}
+ /// Separates a Dataset into histogram bins by a Variable value
+ ///
+ /// Currently supports ``laddu.Mass`` as the binning variable.
+ ///
+ /// Parameters
+ /// ----------
+ /// variable : Mass
+ /// The Variable by which each Event is binned
+ /// bins : int
+ /// The number of equally-spaced bins
+ /// range : tuple[float, float]
+ /// The minimum and maximum bin edges
+ ///
+ /// Returns
+ /// -------
+ /// datasets : BinnedDataset
+ /// A structure that holds a list of Datasets binned by the given `variable`
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Mass
+ ///
#[pyo3(signature = (variable, bins, range))]
fn bin_by(
&self,
@@ -676,11 +734,33 @@ pub(crate) mod laddu {
};
Ok(BinnedDataset(self.0.bin_by(rust_variable, bins, range)))
}
+ /// Generate a new bootstrapped Dataset by randomly resampling the original with replacement
+ ///
+ /// The new Dataset is resampled with a random generator seeded by the provided `seed`
+ ///
+ /// Parameters
+ /// ----------
+ /// seed : int
+ /// The random seed used in the resampling process
+ ///
+ /// Returns
+ /// -------
+ /// Dataset
+ /// A bootstrapped Dataset
+ ///
fn bootstrap(&self, seed: usize) -> Dataset {
Dataset(self.0.bootstrap(seed))
}
}
+ /// A collection of Datasets binned by a Variable
+ ///
+ /// BinnedDatasets can be indexed directly to access the underlying Datasets by bin
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Dataset.bin_by
+ ///
#[pyclass]
struct BinnedDataset(rust::data::BinnedDataset);
@@ -689,17 +769,29 @@ pub(crate) mod laddu {
fn __len__(&self) -> usize {
self.0.len()
}
+ /// Get the number of bins in the BinnedDataset
+ ///
+ /// Returns
+ /// -------
+ /// n : int
+ /// The number of bins
fn len(&self) -> usize {
self.0.len()
}
+ /// The number of bins in the BinnedDataset
+ ///
#[getter]
fn bins(&self) -> usize {
self.0.bins()
}
+ /// The minimum and maximum values of the binning Variable used to create this BinnedDataset
+ ///
#[getter]
fn range(&self) -> (Float, Float) {
self.0.range()
}
+ /// The edges of each bin in the BinnedDataset
+ ///
#[getter]
fn edges<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.edges())
@@ -712,11 +804,48 @@ pub(crate) mod laddu {
}
}
+ /// Open a Dataset from a file
+ ///
+ /// Data should be stored in Parquet format with each column being filled with 32-bit floats
+ ///
+ /// Valid/required column names have the following formats:
+ ///
+ /// ``p4_{particle index}_{E|Px|Py|Pz}`` (four-momentum components for each particle)
+ ///
+ /// ``eps_{particle index}_{x|y|z}`` (polarization/helicity vectors for each particle)
+ ///
+ /// ``weight`` (the weight of the Event)
+ ///
+ /// For example, the four-momentum of the 0th particle in the event would be stored in columns
+ /// with the names ``p4_0_E``, ``p4_0_Px``, ``p4_0_Py``, and ``p4_0_Pz``. That particle's
+ /// polarization could be stored in the columns ``eps_0_x``, ``eps_0_y``, and ``eps_0_z``. This
+ /// could continue for an arbitrary number of particles. The ``weight`` column is always
+ /// required.
+ ///
#[pyfunction]
fn open(path: &str) -> PyResult {
Ok(Dataset(rust::data::open(path)?))
}
+ /// The invariant mass of an arbitrary combination of constituent particles in an Event
+ ///
+ /// This variable is calculated by summing up the 4-momenta of each particle listed by index in
+ /// `constituents` and taking the invariant magnitude of the resulting 4-vector.
+ ///
+ /// Parameters
+ /// ----------
+ /// constituents : list of int
+ /// The indices of particles to combine to create the final 4-momentum
+ ///
+ /// Returns
+ /// -------
+ /// mass_variable : Mass
+ /// A Variable that corresponds to the mass of the constituent particles
+ ///
+ /// See Also
+ /// --------
+ /// laddu.utils.vectors.Vector4.m
+ ///
#[pyclass]
#[derive(Clone)]
struct Mass(rust::utils::variables::Mass);
@@ -727,14 +856,82 @@ pub(crate) mod laddu {
fn new(constituents: Vec) -> Self {
Self(rust::utils::variables::Mass::new(&constituents))
}
+ /// The value of this Variable for the given Event
+ ///
+ /// Parameters
+ /// ----------
+ /// event : Event
+ /// The Event upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// value : float
+ /// The value of the Variable for the given `event`
+ ///
fn value(&self, event: &Event) -> Float {
self.0.value(&event.0)
}
+ /// All values of this Variable on the given Dataset
+ ///
+ /// Parameters
+ /// ----------
+ /// dataset : Dataset
+ /// The Dataset upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// values : array_like
+ /// The values of the Variable for each Event in the given `dataset`
+ ///
fn value_on<'py>(&self, py: Python<'py>, dataset: &Dataset) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.value_on(&dataset.0))
}
}
+ /// The cosine of the polar decay angle in the rest frame of the given `resonance`
+ ///
+ /// This Variable is calculated by forming the given frame (helicity or Gottfried-Jackson) and
+ /// calculating the spherical angles according to one of the decaying `daughter` particles.
+ ///
+ /// The helicity frame is defined in terms of the following Cartesian axes in the rest frame of
+ /// the `resonance`:
+ ///
+ /// .. math:: \hat{z} \propto -\vec{p}'_{\text{recoil}}
+ /// .. math:: \hat{y} \propto \vec{p}_{\text{beam}} \times (-\vec{p}_{\text{recoil}})
+ /// .. math:: \hat{x} = \hat{y} \times \hat{z}
+ ///
+ /// where primed vectors are in the rest frame of the `resonance` and unprimed vectors are in
+ /// the center-of-momentum frame.
+ ///
+ /// The Gottfried-Jackson frame differs only in the definition of :math:`\hat{z}`:
+ ///
+ /// .. math:: \hat{z} \propto \vec{p}'_{\text{beam}}
+ ///
+ /// Parameters
+ /// ----------
+ /// beam : int
+ /// The index of the `beam` particle
+ /// recoil : list of int
+ /// Indices of particles which are combined to form the recoiling particle (particles which
+ /// are not `beam` or part of the `resonance`)
+ /// daughter : list of int
+ /// Indices of particles which are combined to form one of the decay products of the
+ /// `resonance`
+ /// resonance : list of int
+ /// Indices of particles which are combined to form the `resonance`
+ /// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
+ /// The frame to use in the calculation
+ ///
+ ///
+ /// Returns
+ /// -------
+ /// costheta : CosTheta
+ /// A Variable that corresponds to the cosine of the polar decay angle in the given frame
+ ///
+ /// See Also
+ /// --------
+ /// laddu.utils.vectors.Vector3.costheta
+ ///
#[pyclass]
#[derive(Clone)]
struct CosTheta(rust::utils::variables::CosTheta);
@@ -758,14 +955,82 @@ pub(crate) mod laddu {
frame.parse().unwrap(),
))
}
+ /// The value of this Variable for the given Event
+ ///
+ /// Parameters
+ /// ----------
+ /// event : Event
+ /// The Event upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// value : float
+ /// The value of the Variable for the given `event`
+ ///
fn value(&self, event: &Event) -> Float {
self.0.value(&event.0)
}
+ /// All values of this Variable on the given Dataset
+ ///
+ /// Parameters
+ /// ----------
+ /// dataset : Dataset
+ /// The Dataset upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// values : array_like
+ /// The values of the Variable for each Event in the given `dataset`
+ ///
fn value_on<'py>(&self, py: Python<'py>, dataset: &Dataset) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.value_on(&dataset.0))
}
}
+ /// The aziumuthal decay angle in the rest frame of the given `resonance`
+ ///
+ /// This Variable is calculated by forming the given frame (helicity or Gottfried-Jackson) and
+ /// calculating the spherical angles according to one of the decaying `daughter` particles.
+ ///
+ /// The helicity frame is defined in terms of the following Cartesian axes in the rest frame of
+ /// the `resonance`:
+ ///
+ /// .. math:: \hat{z} \propto -\vec{p}'_{\text{recoil}}
+ /// .. math:: \hat{y} \propto \vec{p}_{\text{beam}} \times (-\vec{p}_{\text{recoil}})
+ /// .. math:: \hat{x} = \hat{y} \times \hat{z}
+ ///
+ /// where primed vectors are in the rest frame of the `resonance` and unprimed vectors are in
+ /// the center-of-momentum frame.
+ ///
+ /// The Gottfried-Jackson frame differs only in the definition of :math:`\hat{z}`:
+ ///
+ /// .. math:: \hat{z} \propto \vec{p}'_{\text{beam}}
+ ///
+ /// Parameters
+ /// ----------
+ /// beam : int
+ /// The index of the `beam` particle
+ /// recoil : list of int
+ /// Indices of particles which are combined to form the recoiling particle (particles which
+ /// are not `beam` or part of the `resonance`)
+ /// daughter : list of int
+ /// Indices of particles which are combined to form one of the decay products of the
+ /// `resonance`
+ /// resonance : list of int
+ /// Indices of particles which are combined to form the `resonance`
+ /// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
+ /// The frame to use in the calculation
+ ///
+ ///
+ /// Returns
+ /// -------
+ /// phi : Phi
+ /// A Variable that corresponds to the azimuthal decay angle in the given frame
+ ///
+ /// See Also
+ /// --------
+ /// laddu.utils.vectors.Vector3.phi
+ ///
#[pyclass]
#[derive(Clone)]
struct Phi(rust::utils::variables::Phi);
@@ -789,14 +1054,69 @@ pub(crate) mod laddu {
frame.parse().unwrap(),
))
}
+ /// The value of this Variable for the given Event
+ ///
+ /// Parameters
+ /// ----------
+ /// event : Event
+ /// The Event upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// value : float
+ /// The value of the Variable for the given `event`
+ ///
fn value(&self, event: &Event) -> Float {
self.0.value(&event.0)
}
+ /// All values of this Variable on the given Dataset
+ ///
+ /// Parameters
+ /// ----------
+ /// dataset : Dataset
+ /// The Dataset upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// values : array_like
+ /// The values of the Variable for each Event in the given `dataset`
+ ///
fn value_on<'py>(&self, py: Python<'py>, dataset: &Dataset) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.value_on(&dataset.0))
}
}
+ /// A Variable used to define both spherical decay angles in the given frame
+ ///
+ /// This class combines ``laddu.CosTheta`` and ``laddu.Phi`` into a single
+ /// object
+ ///
+ /// Parameters
+ /// ----------
+ /// beam : int
+ /// The index of the `beam` particle
+ /// recoil : list of int
+ /// Indices of particles which are combined to form the recoiling particle (particles which
+ /// are not `beam` or part of the `resonance`)
+ /// daughter : list of int
+ /// Indices of particles which are combined to form one of the decay products of the
+ /// `resonance`
+ /// resonance : list of int
+ /// Indices of particles which are combined to form the `resonance`
+ /// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
+ /// The frame to use in the calculation
+ ///
+ /// Returns
+ /// -------
+ /// angles : Angles
+ /// A set of Variables corresponding to the spherical decay angles of a particle in the
+ /// given frame
+ ///
+ /// See Also
+ /// --------
+ /// laddu.CosTheta
+ /// laddu.Phi
+ ///
#[pyclass]
#[derive(Clone)]
struct Angles(rust::utils::variables::Angles);
@@ -829,6 +1149,25 @@ pub(crate) mod laddu {
}
}
+ /// The polar angle of the given polarization vector with respect to the production plane
+ ///
+ /// The `beam` and `recoil` particles define the plane of production, and this Variable
+ /// describes the polar angle of the `beam` relative to this plane
+ ///
+ /// Parameters
+ /// ----------
+ /// beam : int
+ /// The index of the `beam` particle
+ /// recoil : list of int
+ /// Indices of particles which are combined to form the recoiling particle (particles which
+ /// are not `beam` or part of the `resonance`)
+ ///
+ /// Returns
+ /// -------
+ /// pol_angle : PolAngle
+ /// A Variable describing the polar angle of the polarization vector with respect to the
+ /// production plane
+ ///
#[pyclass]
#[derive(Clone)]
struct PolAngle(rust::utils::variables::PolAngle);
@@ -839,14 +1178,57 @@ pub(crate) mod laddu {
fn new(beam: usize, recoil: Vec) -> Self {
Self(rust::utils::variables::PolAngle::new(beam, &recoil))
}
+ /// The value of this Variable for the given Event
+ ///
+ /// Parameters
+ /// ----------
+ /// event : Event
+ /// The Event upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// value : float
+ /// The value of the Variable for the given `event`
+ ///
fn value(&self, event: &Event) -> Float {
self.0.value(&event.0)
}
+ /// All values of this Variable on the given Dataset
+ ///
+ /// Parameters
+ /// ----------
+ /// dataset : Dataset
+ /// The Dataset upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// values : array_like
+ /// The values of the Variable for each Event in the given `dataset`
+ ///
fn value_on<'py>(&self, py: Python<'py>, dataset: &Dataset) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.value_on(&dataset.0))
}
}
+ /// The magnitude of the given particle's polarization vector
+ ///
+ /// This Variable simply represents the magnitude of the polarization vector of the particle
+ /// with the index `beam`
+ ///
+ /// Parameters
+ /// ----------
+ /// beam : int
+ /// The index of the `beam` particle
+ ///
+ /// Returns
+ /// -------
+ /// pol_mag : PolMagnitude
+ /// A Variable representing the magnitude of the given polarization vector
+ ///
+ /// See Also
+ /// --------
+ /// laddu.utils.vectors.Vector3.mag
+ ///
#[pyclass]
#[derive(Clone)]
struct PolMagnitude(rust::utils::variables::PolMagnitude);
@@ -857,14 +1239,61 @@ pub(crate) mod laddu {
fn new(beam: usize) -> Self {
Self(rust::utils::variables::PolMagnitude::new(beam))
}
+ /// The value of this Variable for the given Event
+ ///
+ /// Parameters
+ /// ----------
+ /// event : Event
+ /// The Event upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// value : float
+ /// The value of the Variable for the given `event`
+ ///
fn value(&self, event: &Event) -> Float {
self.0.value(&event.0)
}
+ /// All values of this Variable on the given Dataset
+ ///
+ /// Parameters
+ /// ----------
+ /// dataset : Dataset
+ /// The Dataset upon which the Variable is calculated
+ ///
+ /// Returns
+ /// -------
+ /// values : array_like
+ /// The values of the Variable for each Event in the given `dataset`
+ ///
fn value_on<'py>(&self, py: Python<'py>, dataset: &Dataset) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.value_on(&dataset.0))
}
}
+ /// A Variable used to define both the polarization angle and magnitude of the given particle``
+ ///
+ /// This class combines ``laddu.PolAngle`` and ``laddu.PolMagnitude`` into a single
+ /// object
+ ///
+ /// Parameters
+ /// ----------
+ /// beam : int
+ /// The index of the `beam` particle
+ /// recoil : list of int
+ /// Indices of particles which are combined to form the recoiling particle (particles which
+ /// are not `beam` or part of the `resonance`)
+ ///
+ /// Returns
+ /// -------
+ /// polarization : Polarization
+ /// A set of Variables corresponding to the polarization angle and magnitude of the `beam`
+ ///
+ /// See Also
+ /// --------
+ /// laddu.PolAngle
+ /// laddu.PolMagnitude
+ ///
#[pyclass]
#[derive(Clone)]
struct Polarization(rust::utils::variables::Polarization);
@@ -884,22 +1313,53 @@ pub(crate) mod laddu {
}
}
+ /// An object which holds a registered ``Amplitude``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager.register
+ ///
#[pyclass]
#[derive(Clone)]
struct AmplitudeID(rust::amplitudes::AmplitudeID);
+ /// A mathematical expression formed from AmplitudeIDs
+ ///
#[pyclass]
#[derive(Clone)]
pub(crate) struct Expression(pub(crate) rust::amplitudes::Expression);
#[pymethods]
impl AmplitudeID {
+ /// The real part of a complex Amplitude
+ ///
+ /// Returns
+ /// -------
+ /// Expression
+ /// The real part of the given Amplitude
+ ///
fn real(&self) -> Expression {
Expression(self.0.real())
}
+ /// The imaginary part of a complex Amplitude
+ ///
+ /// Returns
+ /// -------
+ /// Expression
+ /// The imaginary part of the given Amplitude
+ ///
fn imag(&self) -> Expression {
Expression(self.0.imag())
}
+ /// The norm-squared of a complex Amplitude
+ ///
+ /// This is computed as :math:`AA^*` where :math:`A^*` is the complex conjugate
+ ///
+ /// Returns
+ /// -------
+ /// Expression
+ /// The norm-squared of the given Amplitude
+ ///
fn norm_sqr(&self) -> Expression {
Expression(self.0.norm_sqr())
}
@@ -931,12 +1391,35 @@ pub(crate) mod laddu {
#[pymethods]
impl Expression {
+ /// The real part of a complex Expression
+ ///
+ /// Returns
+ /// -------
+ /// Expression
+ /// The real part of the given Expression
+ ///
fn real(&self) -> Expression {
Expression(self.0.real())
}
+ /// The imaginary part of a complex Expression
+ ///
+ /// Returns
+ /// -------
+ /// Expression
+ /// The imaginary part of the given Expression
+ ///
fn imag(&self) -> Expression {
Expression(self.0.imag())
}
+ /// The norm-squared of a complex Expression
+ ///
+ /// This is computed as :math:`AA^*` where :math:`A^*` is the complex conjugate
+ ///
+ /// Returns
+ /// -------
+ /// Expression
+ /// The norm-squared of the given Expression
+ ///
fn norm_sqr(&self) -> Expression {
Expression(self.0.norm_sqr())
}
@@ -966,9 +1449,17 @@ pub(crate) mod laddu {
}
}
+ /// A class which can be used to register Amplitudes and store precalculated data
+ ///
#[pyclass]
struct Manager(rust::amplitudes::Manager);
+ /// An Amplitude which can be registered by a Manager
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
#[pyclass]
struct Amplitude(Box);
@@ -978,24 +1469,89 @@ pub(crate) mod laddu {
fn new() -> Self {
Self(rust::amplitudes::Manager::default())
}
+ /// Register an Amplitude with the Manager
+ ///
+ /// Parameters
+ /// ----------
+ /// amplitude : Amplitude
+ /// The Amplitude to register
+ ///
+ /// Returns
+ /// -------
+ /// AmplitudeID
+ /// A reference to the registered `amplitude` that can be used to form complex
+ /// Expressions
+ ///
+ /// Raises
+ /// ------
+ /// ValueError
+ /// If the name of the `amplitude` has already been registered
+ ///
fn register(&mut self, amplitude: &Amplitude) -> PyResult {
Ok(AmplitudeID(self.0.register(amplitude.0.clone())?))
}
+ /// Load an Expression by precalculating each term over the given Dataset
+ ///
+ /// Parameters
+ /// ----------
+ /// dataset : Dataset
+ /// The Dataset to use in precalculation
+ /// expression : Expression
+ /// The expression to use in precalculation
+ ///
+ /// Returns
+ /// -------
+ /// Evaluator
+ /// An object that can be used to evaluate the `expression` over each event in the
+ /// `dataset`
+ ///
+ /// Notes
+ /// -----
+ /// While the given `expression` will be the one evaluated in the end, all registered
+ /// Amplitudes will be loaded, and all of their parameters will be included in the final
+ /// expression. These parameters will have no effect on evaluation, but they must be
+ /// included in function calls.
+ ///
fn load(&self, dataset: &Dataset, expression: &Expression) -> Evaluator {
Evaluator(self.0.load(&dataset.0, &expression.0))
}
}
+ /// A class which can be used to evaluate a stored Expression
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager.load
+ ///
#[pyclass]
#[derive(Clone)]
struct Evaluator(rust::amplitudes::Evaluator);
#[pymethods]
impl Evaluator {
+ /// The free parameters used by the Evaluator
+ ///
+ /// Returns
+ /// -------
+ /// parameters : list of str
+ /// The list of parameter names
+ ///
#[getter]
fn parameters(&self) -> Vec {
self.0.parameters()
}
+ /// Activates Amplitudes in the Expression by name
+ ///
+ /// Parameters
+ /// ----------
+ /// arg : str or list of str
+ /// Names of Amplitudes to be activated
+ ///
+ /// Raises
+ /// ------
+ /// TypeError
+ /// If `arg` is not a str or list of str
+ ///
fn activate(&self, arg: &Bound<'_, PyAny>) -> PyResult<()> {
if let Ok(string_arg) = arg.extract::() {
self.0.activate(&string_arg);
@@ -1009,9 +1565,25 @@ pub(crate) mod laddu {
}
Ok(())
}
+ /// Activates all Amplitudes in the Expression
+ ///
fn activate_all(&self) {
self.0.activate_all();
}
+ /// Deactivates Amplitudes in the Expression by name
+ ///
+ /// Deactivated Amplitudes act as zeros in the Expression
+ ///
+ /// Parameters
+ /// ----------
+ /// arg : str or list of str
+ /// Names of Amplitudes to be deactivated
+ ///
+ /// Raises
+ /// ------
+ /// TypeError
+ /// If `arg` is not a str or list of str
+ ///
fn deactivate(&self, arg: &Bound<'_, PyAny>) -> PyResult<()> {
if let Ok(string_arg) = arg.extract::() {
self.0.deactivate(&string_arg);
@@ -1025,9 +1597,25 @@ pub(crate) mod laddu {
}
Ok(())
}
+ /// Deactivates all Amplitudes in the Expression
+ ///
fn deactivate_all(&self) {
self.0.deactivate_all();
}
+ /// Isolates Amplitudes in the Expression by name
+ ///
+ /// Activates the Amplitudes given in `arg` and deactivates the rest
+ ///
+ /// Parameters
+ /// ----------
+ /// arg : str or list of str
+ /// Names of Amplitudes to be isolated
+ ///
+ /// Raises
+ /// ------
+ /// TypeError
+ /// If `arg` is not a str or list of str
+ ///
fn isolate(&self, arg: &Bound<'_, PyAny>) -> PyResult<()> {
if let Ok(string_arg) = arg.extract::() {
self.0.isolate(&string_arg);
@@ -1041,6 +1629,18 @@ pub(crate) mod laddu {
}
Ok(())
}
+ /// Evaluate the stored Expression over the stored Dataset
+ ///
+ /// Parameters
+ /// ----------
+ /// parameters : list of float
+ /// The values to use for the free parameters
+ ///
+ /// Returns
+ /// -------
+ /// result : array_like
+ /// A ``numpy`` array of complex values for each Event in the Dataset
+ ///
fn evaluate<'py>(
&self,
py: Python<'py>,
@@ -1215,6 +1815,23 @@ pub(crate) mod laddu {
Ok(options)
}
+ /// A (extended) negative log-likelihood evaluator
+ ///
+ /// Parameters
+ /// ----------
+ /// manager : Manager
+ /// The Manager to use for precalculation
+ /// ds_data : Dataset
+ /// A Dataset representing true signal data
+ /// ds_mc : Dataset
+ /// A Dataset of physically flat Monte Carlo data used for normalization
+ /// expression : Expression
+ /// The Expression to evaluate
+ ///
+ /// Returns
+ /// -------
+ /// NLL
+ /// The negative log-likelihood evaluator
#[pyclass]
#[derive(Clone)]
struct NLL(Box);
From ffd1b03c61d61cc3cf2f5085368ba2fe19356c72 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Thu, 7 Nov 2024 13:04:43 -0500
Subject: [PATCH 21/26] feat: add methods to serialize/deserialize fit results
---
Cargo.toml | 6 ++-
python/laddu/likelihoods/__init__.pyi | 7 +++-
src/data.rs | 4 +-
src/lib.rs | 18 ++++++---
src/likelihoods.rs | 54 ++++++++++++++++++++++++++-
src/python.rs | 9 +++++
6 files changed, 86 insertions(+), 12 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 4aeb238..133b038 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,10 +34,12 @@ pyo3 = { version = "0.22.5", optional = true, features = [
"abi3-py37",
] }
numpy = { version = "0.22.0", optional = true, features = ["nalgebra"] }
-ganesh = "0.12.2"
-thiserror = "1.0.64"
+ganesh = "0.13.0"
+thiserror = "2.0.0"
shellexpand = "3.1.0"
accurate = "0.4.1"
+serde = "1.0.214"
+serde-pickle = "1.1.1"
[dev-dependencies]
approx = "0.5.1"
diff --git a/python/laddu/likelihoods/__init__.pyi b/python/laddu/likelihoods/__init__.pyi
index 976a67b..e94dda4 100644
--- a/python/laddu/likelihoods/__init__.pyi
+++ b/python/laddu/likelihoods/__init__.pyi
@@ -1,4 +1,5 @@
-from typing import Literal, Sequence
+from collections.abc import Sequence
+from typing import Literal
import numpy as np
import numpy.typing as npt
@@ -74,6 +75,10 @@ class Status:
n_f_evals: int
n_g_evals: int
+ def save_as(self, path: str): ...
+ @staticmethod
+ def load(path: str) -> Status: ...
+
class Bound:
lower: float
upper: float
diff --git a/src/data.rs b/src/data.rs
index f3ddeb3..619ade3 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -386,8 +386,8 @@ fn batch_to_event(batch: &RecordBatch, row: usize) -> Event {
/// Open a Parquet file and read the data into a [`Dataset`].
#[cfg(feature = "rayon")]
-pub fn open(file_path: &str) -> Result, LadduError> {
- let file_path = Path::new(&*shellexpand::full(file_path)?).canonicalize()?;
+pub fn open>(file_path: T) -> Result, LadduError> {
+ let file_path = Path::new(&*shellexpand::full(file_path.as_ref())?).canonicalize()?;
let file = File::open(file_path)?;
let builder = ParquetRecordBatchReaderBuilder::try_new(file)?;
let reader = builder.build()?;
diff --git a/src/lib.rs b/src/lib.rs
index 46a256a..c45988e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -290,7 +290,7 @@ pub mod prelude {
pub use crate::data::{open, BinnedDataset, Dataset, Event};
pub use crate::likelihoods::{
LikelihoodEvaluator, LikelihoodExpression, LikelihoodID, LikelihoodManager, LikelihoodTerm,
- MinimizerOptions, NLL,
+ MinimizerOptions, ReadWrite, NLL,
};
pub use crate::resources::{
Cache, ComplexMatrixID, ComplexScalarID, ComplexVectorID, MatrixID, ParameterID,
@@ -302,6 +302,7 @@ pub mod prelude {
};
pub use crate::utils::vectors::{FourMomentum, FourVector, ThreeMomentum, ThreeVector};
pub use crate::{Float, LadduError, PI};
+ pub use ganesh::Status;
pub use nalgebra::{DVector, Vector3, Vector4};
pub use num::Complex;
}
@@ -348,6 +349,9 @@ pub enum LadduError {
/// Name of amplitude which is already registered
name: String,
},
+ /// An error returned by the Python pickle (de)serializer
+ #[error("Pickle conversion error: {0}")]
+ PickleError(#[from] serde_pickle::Error),
/// A custom fallback error for errors too complex or too infrequent to warrant their own error
/// category.
#[error("{0}")]
@@ -360,11 +364,13 @@ impl From for PyErr {
use pyo3::exceptions::*;
let err_string = err.to_string();
match err {
- LadduError::ParquetError(_) => PyIOError::new_err(err_string),
- LadduError::ArrowError(_) => PyIOError::new_err(err_string),
- LadduError::IOError(_) => PyIOError::new_err(err_string),
- LadduError::LookupError(_) => PyValueError::new_err(err_string),
- LadduError::RegistrationError { .. } => PyValueError::new_err(err_string),
+ LadduError::LookupError(_) | LadduError::RegistrationError { .. } => {
+ PyValueError::new_err(err_string)
+ }
+ LadduError::ParquetError(_)
+ | LadduError::ArrowError(_)
+ | LadduError::IOError(_)
+ | LadduError::PickleError(_) => PyIOError::new_err(err_string),
LadduError::Custom(_) => PyException::new_err(err_string),
}
}
diff --git a/src/likelihoods.rs b/src/likelihoods.rs
index 5431ac7..f020167 100644
--- a/src/likelihoods.rs
+++ b/src/likelihoods.rs
@@ -2,6 +2,9 @@ use std::{
collections::HashMap,
convert::Infallible,
fmt::{Debug, Display},
+ fs::File,
+ io::{BufReader, BufWriter},
+ path::Path,
sync::Arc,
};
@@ -9,7 +12,7 @@ use crate::{
amplitudes::{AmplitudeValues, Evaluator, Expression, GradientValues, Manager},
data::Dataset,
resources::Parameters,
- Float,
+ Float, LadduError,
};
use accurate::{sum::Klein, traits::*};
use auto_ops::*;
@@ -22,6 +25,55 @@ use num::Complex;
#[cfg(feature = "rayon")]
use rayon::prelude::*;
+use serde::{de::DeserializeOwned, Serialize};
+
+/// A trait which allows structs with [`Serialize`] and [`Deserialize`](`serde::Deserialize`) to be
+/// written and read from files with a certain set of types/extensions.
+///
+/// Currently, Python's pickle format is supported supported, since it's an easy-to-parse standard
+/// that supports floating point values better that JSON or TOML
+pub trait ReadWrite: Serialize + DeserializeOwned {
+ /// Save a [`serde`]-object to a file path, using the extension to determine the file format
+ fn save_as>(&self, file_path: T) -> Result<(), LadduError> {
+ let expanded_path = shellexpand::full(file_path.as_ref())?;
+ let file_path = Path::new(expanded_path.as_ref());
+ let extension = file_path
+ .extension()
+ .and_then(|ext| ext.to_str().map(|ext| ext.to_string()))
+ .unwrap_or("".to_string());
+ let file = File::create(file_path)?;
+ let mut writer = BufWriter::new(file);
+ match extension.as_str() {
+ "pkl" | "pickle" => serde_pickle::to_writer(&mut writer, self, Default::default())?,
+ _ => {
+ return Err(LadduError::Custom(format!(
+ "Unsupported file extension: {}\nValid options are \".pkl\" or \".pickle\"",
+ extension
+ )))
+ }
+ };
+ Ok(())
+ }
+ /// Load a [`serde`]-object from a file path, using the extension to determine the file format
+ fn load>(file_path: T) -> Result {
+ let file_path = Path::new(&*shellexpand::full(file_path.as_ref())?).canonicalize()?;
+ let extension = file_path
+ .extension()
+ .and_then(|ext| ext.to_str().map(|ext| ext.to_string()))
+ .unwrap_or("".to_string());
+ let file = File::open(file_path.clone())?;
+ let reader = BufReader::new(file);
+ match extension.as_str() {
+ "pkl" | "pickle" => Ok(serde_pickle::from_reader(reader, Default::default())?),
+ _ => Err(LadduError::Custom(format!(
+ "Unsupported file extension: {}\nValid options are \".pkl\" or \".pickle\"",
+ extension
+ ))),
+ }
+ }
+}
+
+impl ReadWrite for Status {}
/// A trait which describes a term that can be used like a likelihood (more correctly, a negative
/// log-likelihood) in a minimization.
diff --git a/src/python.rs b/src/python.rs
index e7b6262..9f21969 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -16,6 +16,7 @@ pub(crate) mod laddu {
use crate as rust;
use crate::likelihoods::LikelihoodTerm as RustLikelihoodTerm;
use crate::likelihoods::MinimizerOptions;
+ use crate::prelude::ReadWrite;
use crate::utils::variables::Variable;
use crate::utils::vectors::{FourMomentum, FourVector, ThreeMomentum, ThreeVector};
use crate::Float;
@@ -2180,6 +2181,14 @@ pub(crate) mod laddu {
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
+ fn save_as(&self, path: &str) -> PyResult<()> {
+ self.0.save_as(path)?;
+ Ok(())
+ }
+ #[staticmethod]
+ fn load(path: &str) -> PyResult {
+ Ok(Status(ganesh::Status::load(path)?))
+ }
}
#[pyclass]
From f547445a2e70c441b494946883b75d69e193107d Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Thu, 7 Nov 2024 13:05:10 -0500
Subject: [PATCH 22/26] style: resolve lint warning of `len` without `is_empty`
---
src/resources.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/resources.rs b/src/resources.rs
index 26e525f..ba4ae97 100644
--- a/src/resources.rs
+++ b/src/resources.rs
@@ -35,6 +35,7 @@ impl<'a> Parameters<'a> {
}
/// The number of free parameters.
+ #[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.parameters.len()
}
From 2e2d5730d7e29093724aa16f99e6db82fc4daa81 Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Thu, 7 Nov 2024 15:26:06 -0500
Subject: [PATCH 23/26] docs: fix typo in K-Matrix Rust docs
---
src/amplitudes/kmatrix.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/amplitudes/kmatrix.rs b/src/amplitudes/kmatrix.rs
index 2c24fbf..7f8522c 100644
--- a/src/amplitudes/kmatrix.rs
+++ b/src/amplitudes/kmatrix.rs
@@ -702,7 +702,7 @@ impl KopfKMatrixRho {
/// | ------------- | ------- |
/// | 0 | $`\pi\pi`$ |
/// | 1 | $`2\pi 2\pi`$ |
- /// | 1 | $`K\bar{K}`$ |
+ /// | 2 | $`K\bar{K}`$ |
///
/// | Pole names |
/// | ---------- |
From 247300e31c0bd534984cf4752daf0b2cce52757b Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Thu, 7 Nov 2024 15:26:37 -0500
Subject: [PATCH 24/26] docs: finish first pass documenting Python API
This is just rough documentation, but I think it's better to at least have something out there
---
src/python.rs | 987 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 928 insertions(+), 59 deletions(-)
diff --git a/src/python.rs b/src/python.rs
index 9f21969..a0385ed 100644
--- a/src/python.rs
+++ b/src/python.rs
@@ -42,11 +42,6 @@ pub(crate) mod laddu {
/// px, py, pz : float
/// The Cartesian components of the 3-vector
///
- /// Returns
- /// -------
- /// Vector3
- /// A new 3-momentum vector made from the given components
- ///
#[pyclass]
#[derive(Clone)]
struct Vector3(nalgebra::Vector3);
@@ -260,10 +255,6 @@ pub(crate) mod laddu {
/// px, py, pz : float
/// The Cartesian components of the 3-vector
///
- /// Returns
- /// -------
- /// Vector4
- /// A new 4-momentum vector made from the given components
///
#[pyclass]
#[derive(Clone)]
@@ -575,11 +566,6 @@ pub(crate) mod laddu {
/// weight : float
/// The weight associated with this event
///
- /// Returns
- /// -------
- /// event : Event
- /// An event formed from the given components
- ///
#[pyclass]
#[derive(Clone)]
struct Event(Arc);
@@ -838,11 +824,6 @@ pub(crate) mod laddu {
/// constituents : list of int
/// The indices of particles to combine to create the final 4-momentum
///
- /// Returns
- /// -------
- /// mass_variable : Mass
- /// A Variable that corresponds to the mass of the constituent particles
- ///
/// See Also
/// --------
/// laddu.utils.vectors.Vector4.m
@@ -923,12 +904,6 @@ pub(crate) mod laddu {
/// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
/// The frame to use in the calculation
///
- ///
- /// Returns
- /// -------
- /// costheta : CosTheta
- /// A Variable that corresponds to the cosine of the polar decay angle in the given frame
- ///
/// See Also
/// --------
/// laddu.utils.vectors.Vector3.costheta
@@ -1022,12 +997,6 @@ pub(crate) mod laddu {
/// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
/// The frame to use in the calculation
///
- ///
- /// Returns
- /// -------
- /// phi : Phi
- /// A Variable that corresponds to the azimuthal decay angle in the given frame
- ///
/// See Also
/// --------
/// laddu.utils.vectors.Vector3.phi
@@ -1107,12 +1076,6 @@ pub(crate) mod laddu {
/// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
/// The frame to use in the calculation
///
- /// Returns
- /// -------
- /// angles : Angles
- /// A set of Variables corresponding to the spherical decay angles of a particle in the
- /// given frame
- ///
/// See Also
/// --------
/// laddu.CosTheta
@@ -1140,10 +1103,22 @@ pub(crate) mod laddu {
frame.parse().unwrap(),
))
}
+ /// The Variable representing the cosine of the polar spherical decay angle
+ ///
+ /// Returns
+ /// -------
+ /// CosTheta
+ ///
#[getter]
fn costheta(&self) -> CosTheta {
CosTheta(self.0.costheta.clone())
}
+ // The Variable representing the polar azimuthal decay angle
+ //
+ // Returns
+ // -------
+ // Phi
+ //
#[getter]
fn phi(&self) -> Phi {
Phi(self.0.phi.clone())
@@ -1163,12 +1138,6 @@ pub(crate) mod laddu {
/// Indices of particles which are combined to form the recoiling particle (particles which
/// are not `beam` or part of the `resonance`)
///
- /// Returns
- /// -------
- /// pol_angle : PolAngle
- /// A Variable describing the polar angle of the polarization vector with respect to the
- /// production plane
- ///
#[pyclass]
#[derive(Clone)]
struct PolAngle(rust::utils::variables::PolAngle);
@@ -1221,11 +1190,6 @@ pub(crate) mod laddu {
/// beam : int
/// The index of the `beam` particle
///
- /// Returns
- /// -------
- /// pol_mag : PolMagnitude
- /// A Variable representing the magnitude of the given polarization vector
- ///
/// See Also
/// --------
/// laddu.utils.vectors.Vector3.mag
@@ -1285,11 +1249,6 @@ pub(crate) mod laddu {
/// Indices of particles which are combined to form the recoiling particle (particles which
/// are not `beam` or part of the `resonance`)
///
- /// Returns
- /// -------
- /// polarization : Polarization
- /// A set of Variables corresponding to the polarization angle and magnitude of the `beam`
- ///
/// See Also
/// --------
/// laddu.PolAngle
@@ -1304,10 +1263,22 @@ pub(crate) mod laddu {
fn new(beam: usize, recoil: Vec) -> Self {
Polarization(rust::utils::variables::Polarization::new(beam, &recoil))
}
+ /// The Variable representing the magnitude of the polarization vector
+ ///
+ /// Returns
+ /// -------
+ /// PolMagnitude
+ ///
#[getter]
fn pol_magnitude(&self) -> PolMagnitude {
PolMagnitude(self.0.pol_magnitude)
}
+ /// The Variable representing the polar angle of the polarization vector
+ ///
+ /// Returns
+ /// -------
+ /// PolAngle
+ ///
#[getter]
fn pol_angle(&self) -> PolAngle {
PolAngle(self.0.pol_angle.clone())
@@ -1486,7 +1457,7 @@ pub(crate) mod laddu {
/// Raises
/// ------
/// ValueError
- /// If the name of the `amplitude` has already been registered
+ /// If the name of the ``amplitude`` has already been registered
///
fn register(&mut self, amplitude: &Amplitude) -> PyResult {
Ok(AmplitudeID(self.0.register(amplitude.0.clone())?))
@@ -1829,10 +1800,6 @@ pub(crate) mod laddu {
/// expression : Expression
/// The Expression to evaluate
///
- /// Returns
- /// -------
- /// NLL
- /// The negative log-likelihood evaluator
#[pyclass]
#[derive(Clone)]
struct NLL(Box);
@@ -1853,21 +1820,58 @@ pub(crate) mod laddu {
&expression.0,
))
}
+ /// The underlying signal dataset used in calculating the NLL
+ ///
+ /// Returns
+ /// -------
+ /// Dataset
+ ///
#[getter]
fn data(&self) -> Dataset {
Dataset(self.0.data_evaluator.dataset.clone())
}
+ /// The underlying Monte Carlo dataset used in calculating the NLL
+ ///
+ /// Returns
+ /// -------
+ /// Dataset
+ ///
#[getter]
fn mc(&self) -> Dataset {
Dataset(self.0.mc_evaluator.dataset.clone())
}
+ /// Turn an ``NLL`` into a term that can be used by a ``LikelihoodManager``
+ ///
+ /// Returns
+ /// -------
+ /// term : LikelihoodTerm
+ /// The isolated NLL which can be used to build more complex models
+ ///
fn as_term(&self) -> LikelihoodTerm {
LikelihoodTerm(self.0.clone())
}
+ /// The names of the free parameters used to evaluate the NLL
+ ///
+ /// Returns
+ /// -------
+ /// parameters : list of str
+ ///
#[getter]
fn parameters(&self) -> Vec {
self.0.parameters()
}
+ /// Activates Amplitudes in the NLL by name
+ ///
+ /// Parameters
+ /// ----------
+ /// arg : str or list of str
+ /// Names of Amplitudes to be activated
+ ///
+ /// Raises
+ /// ------
+ /// TypeError
+ /// If `arg` is not a str or list of str
+ ///
fn activate(&self, arg: &Bound<'_, PyAny>) -> PyResult<()> {
if let Ok(string_arg) = arg.extract::() {
self.0.activate(&string_arg);
@@ -1881,9 +1885,25 @@ pub(crate) mod laddu {
}
Ok(())
}
+ /// Activates all Amplitudes in the JNLL
+ ///
fn activate_all(&self) {
self.0.activate_all();
}
+ /// Deactivates Amplitudes in the NLL by name
+ ///
+ /// Deactivated Amplitudes act as zeros in the NLL
+ ///
+ /// Parameters
+ /// ----------
+ /// arg : str or list of str
+ /// Names of Amplitudes to be deactivated
+ ///
+ /// Raises
+ /// ------
+ /// TypeError
+ /// If `arg` is not a str or list of str
+ ///
fn deactivate(&self, arg: &Bound<'_, PyAny>) -> PyResult<()> {
if let Ok(string_arg) = arg.extract::() {
self.0.deactivate(&string_arg);
@@ -1897,9 +1917,25 @@ pub(crate) mod laddu {
}
Ok(())
}
+ /// Deactivates all Amplitudes in the NLL
+ ///
fn deactivate_all(&self) {
self.0.deactivate_all();
}
+ /// Isolates Amplitudes in the NLL by name
+ ///
+ /// Activates the Amplitudes given in `arg` and deactivates the rest
+ ///
+ /// Parameters
+ /// ----------
+ /// arg : str or list of str
+ /// Names of Amplitudes to be isolated
+ ///
+ /// Raises
+ /// ------
+ /// TypeError
+ /// If `arg` is not a str or list of str
+ ///
fn isolate(&self, arg: &Bound<'_, PyAny>) -> PyResult<()> {
if let Ok(string_arg) = arg.extract::() {
self.0.isolate(&string_arg);
@@ -1913,9 +1949,41 @@ pub(crate) mod laddu {
}
Ok(())
}
+ /// Evaluate the extended negative log-likelihood over the stored Datasets
+ ///
+ /// This is defined as
+ ///
+ /// .. math:: NLL(\vec{p}; D, MC) = -2 \left( \sum_{e \in D} (e_w \log(\mathcal{L}(e) / N_D)) - \frac{1}{N_{MC}} \sum_{e \in MC} (e_w \mathcal{L}(e)) \right)
+ ///
+ /// Parameters
+ /// ----------
+ /// parameters : list of float
+ /// The values to use for the free parameters
+ ///
+ /// Returns
+ /// -------
+ /// result : float
+ /// The total negative log-likelihood
+ ///
fn evaluate(&self, parameters: Vec) -> Float {
self.0.evaluate(¶meters)
}
+ /// Project the model over the Monte Carlo dataset with the given parameter values
+ ///
+ /// This is defined as
+ ///
+ /// .. math:: e_w(\vec{p}) = \frac{e_w}{N_{MC}} \mathcal{L}(e)
+ ///
+ /// Parameters
+ /// ----------
+ /// parameters : list of float
+ /// The values to use for the free parameters
+ ///
+ /// Returns
+ /// -------
+ /// result : array_like
+ /// Weights for every Monte Carlo event which represent the fit to data
+ ///
fn project<'py>(
&self,
py: Python<'py>,
@@ -1923,6 +1991,69 @@ pub(crate) mod laddu {
) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, &self.0.project(¶meters))
}
+
+ /// Minimize the NLL with respect to the free parameters in the model
+ ///
+ /// This method "runs the fit". Given an initial position `p0` and optional `bounds`, this
+ /// method performs a minimization over the negative log-likelihood, optimizing the model
+ /// over the stored signal data and Monte Carlo.
+ ///
+ /// Parameters
+ /// ----------
+ /// p0 : array_like
+ /// The initial parameters at the start of optimization
+ /// bounds : list of tuple of float, optional
+ /// A list of lower and upper bound pairs for each parameter (use ``None`` for no bound)
+ /// method : {'lbfgsb', 'nelder-mead', 'nelder_mead'}
+ /// The minimization algorithm to use (see additional parameters for fine-tuning)
+ /// max_steps : int, default=4000
+ /// The maximum number of algorithm steps to perform
+ /// debug : bool, default=False
+ /// Set to ``True`` to print out debugging information at each step
+ /// verbose : bool, default=False
+ /// Set to ``True`` to print verbose information at each step
+ /// show_step : bool, default=True
+ /// Include step number in verbose output
+ /// show_x : bool, default=True
+ /// Include current best position in verbose output
+ /// show_fx : bool, default=True
+ /// Include current best NLL in verbose output
+ /// observers : Observer or list of Observers
+ /// Callback functions which are applied after every algorithm step
+ /// tol_x_rel : float
+ /// The relative position tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_x_abs : float
+ /// The absolute position tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_f_rel : float
+ /// The relative function tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_f_abs : float
+ /// The absolute function tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_g_abs : float
+ /// The absolute gradient tolerance used by termination methods (default is the cube
+ /// root of machine epsilon)
+ /// g_tolerance : float, default=1e-5
+ /// Another gradient tolerance used by termination methods (particularly L-BFGS-B)
+ /// adaptive : bool, default=False
+ /// Use adaptive values for Nelder-Mead parameters
+ /// alpha : float, optional
+ /// Overwrite the default :math:`\alpha` parameter in the Nelder-Mead algorithm
+ /// beta : float, optional
+ /// Overwrite the default :math:`\beta` parameter in the Nelder-Mead algorithm
+ /// gamma : float, optional
+ /// Overwrite the default :math:`\gamma` parameter in the Nelder-Mead algorithm
+ /// delta : float, optional
+ /// Overwrite the default :math:`\delta` parameter in the Nelder-Mead algorithm
+ /// simplex_expansion_method : {'greedy_minimization', 'greedy_expansion'}
+ /// The expansion method used by the Nelder-Mead algorithm
+ /// nelder_mead_f_terminator : {'stddev', 'absolute', 'stddev', 'none'}
+ /// The function terminator used by the Nelder-Mead algorithm
+ /// nelder_mead_x_terminator : {'singer', 'diameter', 'rowan', 'higham', 'none'}
+ /// The positional terminator used by the Nelder-Mead algorithm
+ ///
#[pyo3(signature = (p0, bounds=None, method="lbfgsb", max_steps=4000, debug=false, verbose=false, **kwargs))]
#[allow(clippy::too_many_arguments)]
fn minimize(
@@ -1954,14 +2085,28 @@ pub(crate) mod laddu {
}
}
+ /// A term in an expression with multiple likelihood components
+ ///
+ /// See Also
+ /// --------
+ /// NLL.as_term
+ ///
#[pyclass]
#[derive(Clone)]
struct LikelihoodTerm(Box);
+ /// An object which holds a registered ``LikelihoodTerm``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.LikelihoodManager.register
+ ///
#[pyclass]
#[derive(Clone)]
struct LikelihoodID(rust::likelihoods::LikelihoodID);
+ /// A mathematical expression formed from LikelihoodIDs
+ ///
#[pyclass]
#[derive(Clone)]
struct LikelihoodExpression(rust::likelihoods::LikelihoodExpression);
@@ -2022,6 +2167,8 @@ pub(crate) mod laddu {
}
}
+ /// A class which can be used to register LikelihoodTerms and store precalculated data
+ ///
#[pyclass]
#[derive(Clone)]
struct LikelihoodManager(rust::likelihoods::LikelihoodManager);
@@ -2032,29 +2179,156 @@ pub(crate) mod laddu {
fn new() -> Self {
Self(rust::likelihoods::LikelihoodManager::default())
}
+ /// Register a LikelihoodTerm with the LikelihoodManager
+ ///
+ /// Parameters
+ /// ----------
+ /// term : LikelihoodTerm
+ /// The LikelihoodTerm to register
+ ///
+ /// Returns
+ /// -------
+ /// LikelihoodID
+ /// A reference to the registered ``likelihood`` that can be used to form complex
+ /// LikelihoodExpressions
+ ///
fn register(&mut self, likelihood_term: &LikelihoodTerm) -> LikelihoodID {
LikelihoodID(self.0.register(likelihood_term.0.clone()))
}
+ /// The free parameters used by all terms in the LikelihoodManager
+ ///
+ /// Returns
+ /// -------
+ /// parameters : list of str
+ /// The list of parameter names
+ ///
fn parameters(&self) -> Vec {
self.0.parameters()
}
+ /// Load a LikelihoodExpression by precalculating each term over their internal Datasets
+ ///
+ /// Parameters
+ /// ----------
+ /// likelihood_expression : LikelihoodExpression
+ /// The expression to use in precalculation
+ ///
+ /// Returns
+ /// -------
+ /// LikelihoodEvaluator
+ /// An object that can be used to evaluate the `likelihood_expression` over all managed
+ /// terms
+ ///
+ /// Notes
+ /// -----
+ /// While the given `likelihood_expression` will be the one evaluated in the end, all registered
+ /// Amplitudes will be loaded, and all of their parameters will be included in the final
+ /// expression. These parameters will have no effect on evaluation, but they must be
+ /// included in function calls.
+ ///
+ /// See Also
+ /// --------
+ /// LikelihoodManager.parameters
+ ///
fn load(&self, likelihood_expression: &LikelihoodExpression) -> LikelihoodEvaluator {
LikelihoodEvaluator(self.0.load(&likelihood_expression.0))
}
}
+ /// A class which can be used to evaluate a collection of LikelihoodTerms managed by a
+ /// LikelihoodManager
+ ///
#[pyclass]
struct LikelihoodEvaluator(rust::likelihoods::LikelihoodEvaluator);
#[pymethods]
impl LikelihoodEvaluator {
+ /// A list of the names of the free parameters across all terms in all models
+ ///
+ /// Returns
+ /// -------
+ /// parameters : list of str
+ ///
#[getter]
fn parameters(&self) -> Vec {
self.0.parameters()
}
+ /// Evaluate the sum of all terms in the evaluator
+ ///
+ /// Parameters
+ /// ----------
+ /// parameters : list of float
+ /// The values to use for the free parameters
+ ///
+ /// Returns
+ /// -------
+ /// result : float
+ /// The total negative log-likelihood summed over all terms
+ ///
fn evaluate(&self, parameters: Vec) -> Float {
self.0.evaluate(¶meters)
}
+ /// Minimize all LikelihoodTerms with respect to the free parameters in the model
+ ///
+ /// This method "runs the fit". Given an initial position `p0` and optional `bounds`, this
+ /// method performs a minimization over the tatal negative log-likelihood, optimizing the model
+ /// over the stored signal data and Monte Carlo.
+ ///
+ /// Parameters
+ /// ----------
+ /// p0 : array_like
+ /// The initial parameters at the start of optimization
+ /// bounds : list of tuple of float, optional
+ /// A list of lower and upper bound pairs for each parameter (use ``None`` for no bound)
+ /// method : {'lbfgsb', 'nelder-mead', 'nelder_mead'}
+ /// The minimization algorithm to use (see additional parameters for fine-tuning)
+ /// max_steps : int, default=4000
+ /// The maximum number of algorithm steps to perform
+ /// debug : bool, default=False
+ /// Set to ``True`` to print out debugging information at each step
+ /// verbose : bool, default=False
+ /// Set to ``True`` to print verbose information at each step
+ /// show_step : bool, default=True
+ /// Include step number in verbose output
+ /// show_x : bool, default=True
+ /// Include current best position in verbose output
+ /// show_fx : bool, default=True
+ /// Include current best NLL in verbose output
+ /// observers : Observer or list of Observers
+ /// Callback functions which are applied after every algorithm step
+ /// tol_x_rel : float
+ /// The relative position tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_x_abs : float
+ /// The absolute position tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_f_rel : float
+ /// The relative function tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_f_abs : float
+ /// The absolute function tolerance used by termination methods (default is machine
+ /// epsilon)
+ /// tol_g_abs : float
+ /// The absolute gradient tolerance used by termination methods (default is the cube
+ /// root of machine epsilon)
+ /// g_tolerance : float, default=1e-5
+ /// Another gradient tolerance used by termination methods (particularly L-BFGS-B)
+ /// adaptive : bool, default=False
+ /// Use adaptive values for Nelder-Mead parameters
+ /// alpha : float, optional
+ /// Overwrite the default :math:`\alpha` parameter in the Nelder-Mead algorithm
+ /// beta : float, optional
+ /// Overwrite the default :math:`\beta` parameter in the Nelder-Mead algorithm
+ /// gamma : float, optional
+ /// Overwrite the default :math:`\gamma` parameter in the Nelder-Mead algorithm
+ /// delta : float, optional
+ /// Overwrite the default :math:`\delta` parameter in the Nelder-Mead algorithm
+ /// simplex_expansion_method : {'greedy_minimization', 'greedy_expansion'}
+ /// The expansion method used by the Nelder-Mead algorithm
+ /// nelder_mead_f_terminator : {'stddev', 'absolute', 'stddev', 'none'}
+ /// The function terminator used by the Nelder-Mead algorithm
+ /// nelder_mead_x_terminator : {'singer', 'diameter', 'rowan', 'higham', 'none'}
+ /// The positional terminator used by the Nelder-Mead algorithm
+ ///
#[pyo3(signature = (p0, bounds=None, method="lbfgsb", max_steps=4000, debug=false, verbose=false, **kwargs))]
#[allow(clippy::too_many_arguments)]
fn minimize(
@@ -2086,6 +2360,17 @@ pub(crate) mod laddu {
}
}
+ /// A parameterized scalar term which can be added to a LikelihoodManager
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The name of the new scalar parameter
+ ///
+ /// Returns
+ /// -------
+ /// LikelihoodTerm
+ ///
#[pyfunction]
fn LikelihoodScalar(name: String) -> LikelihoodTerm {
LikelihoodTerm(rust::likelihoods::LikelihoodScalar::new(name))
@@ -2103,15 +2388,30 @@ pub(crate) mod laddu {
}
}
+ /// The status/result of a minimization
+ ///
+ ///
#[pyclass]
#[derive(Clone)]
pub(crate) struct Status(pub(crate) ganesh::Status);
#[pymethods]
impl Status {
+ /// The current best position in parameter space
+ ///
+ /// Returns
+ /// -------
+ /// array_like
+ ///
#[getter]
fn x<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, self.0.x.as_slice())
}
+ /// The uncertainty on each parameter (``None`` if it wasn't calculated)
+ ///
+ /// Returns
+ /// -------
+ /// array_like or None
+ ///
#[getter]
fn err<'py>(&self, py: Python<'py>) -> Option>> {
self.0
@@ -2119,14 +2419,32 @@ pub(crate) mod laddu {
.clone()
.map(|err| PyArray1::from_slice_bound(py, err.as_slice()))
}
+ /// The initial position at the start of the minimization
+ ///
+ /// Returns
+ /// -------
+ /// array_like
+ ///
#[getter]
fn x0<'py>(&self, py: Python<'py>) -> Bound<'py, PyArray1> {
PyArray1::from_slice_bound(py, self.0.x0.as_slice())
}
+ /// The optimized value of the objective function
+ ///
+ /// Returns
+ /// -------
+ /// float
+ ///
#[getter]
fn fx(&self) -> Float {
self.0.fx
}
+ /// The covariance matrix (``None`` if it wasn't calculated)
+ ///
+ /// Returns
+ /// -------
+ /// array_like or None
+ ///
#[getter]
fn cov<'py>(&self, py: Python<'py>) -> Option>> {
self.0.cov.clone().map(|cov| {
@@ -2139,6 +2457,12 @@ pub(crate) mod laddu {
.unwrap()
})
}
+ /// The Hessian matrix (``None`` if it wasn't calculated)
+ ///
+ /// Returns
+ /// -------
+ /// array_like or None
+ ///
#[getter]
fn hess<'py>(&self, py: Python<'py>) -> Option>> {
self.0.hess.clone().map(|hess| {
@@ -2152,14 +2476,32 @@ pub(crate) mod laddu {
.unwrap()
})
}
+ /// A status message from the optimizer at the end of the algorithm
+ ///
+ /// Returns
+ /// -------
+ /// str
+ ///
#[getter]
fn message(&self) -> String {
self.0.message.clone()
}
+ /// The state of the optimizer's convergence conditions
+ ///
+ /// Returns
+ /// -------
+ /// bool
+ ///
#[getter]
fn converged(&self) -> bool {
self.0.converged
}
+ /// Parameter bounds which were applied to the fitting algorithm
+ ///
+ /// Returns
+ /// -------
+ /// list of Bound or None
+ ///
#[getter]
fn bounds(&self) -> Option> {
self.0
@@ -2167,10 +2509,22 @@ pub(crate) mod laddu {
.clone()
.map(|bounds| bounds.iter().map(|bound| ParameterBound(*bound)).collect())
}
+ /// The number of times the objective function was evaluated
+ ///
+ /// Returns
+ /// -------
+ /// int
+ ///
#[getter]
fn n_f_evals(&self) -> usize {
self.0.n_f_evals
}
+ /// The number of times the gradient of the objective function was evaluated
+ ///
+ /// Returns
+ /// -------
+ /// int
+ ///
#[getter]
fn n_g_evals(&self) -> usize {
self.0.n_g_evals
@@ -2181,51 +2535,176 @@ pub(crate) mod laddu {
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
+ /// Save the fit result to a file
+ ///
+ /// Parameters
+ /// ----------
+ /// path : str
+ /// The path of the new file (overwrites if the file exists!)
+ ///
+ /// Raises
+ /// ------
+ /// IOError
+ /// If anything fails when trying to write the file
+ ///
+ /// Notes
+ /// -----
+ /// Valid file path names must have either the ".pickle" or ".pkl" extension
+ ///
fn save_as(&self, path: &str) -> PyResult<()> {
self.0.save_as(path)?;
Ok(())
}
+ /// Load a fit result from a file
+ ///
+ /// Parameters
+ /// ----------
+ /// path : str
+ /// The path of the existing fit file
+ ///
+ /// Returns
+ /// -------
+ /// Status
+ /// The fit result contained in the file
+ ///
+ /// Raises
+ /// ------
+ /// IOError
+ /// If anything fails when trying to read the file
+ ///
+ /// Notes
+ /// -----
+ /// Valid file path names must have either the ".pickle" or ".pkl" extension
+ ///
#[staticmethod]
fn load(path: &str) -> PyResult {
Ok(Status(ganesh::Status::load(path)?))
}
}
+ /// A class representing a lower and upper bound on a free parameter
+ ///
#[pyclass]
#[derive(Clone)]
#[pyo3(name = "Bound")]
struct ParameterBound(ganesh::Bound);
#[pymethods]
impl ParameterBound {
+ /// The lower bound
+ ///
+ /// Returns
+ /// -------
+ /// float
+ ///
#[getter]
fn lower(&self) -> Float {
self.0.lower()
}
+ /// The upper bound
+ ///
+ /// Returns
+ /// -------
+ /// float
+ ///
#[getter]
fn upper(&self) -> Float {
self.0.upper()
}
}
+ /// A class, typically used to allow Amplitudes to take either free parameters or constants as
+ /// inputs
+ ///
+ /// See Also
+ /// --------
+ /// laddu.parameter
+ /// laddu.constant
+ ///
#[pyclass]
#[derive(Clone)]
struct ParameterLike(rust::amplitudes::ParameterLike);
+ /// A free parameter which floats during an optimization
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The name of the free parameter
+ ///
+ /// Returns
+ /// -------
+ /// ParameterLike
+ /// An object that can be used as the input for many Amplitude constructors
+ ///
+ /// Notes
+ /// -----
+ /// Two free parameters with the same name are shared in a fit
+ ///
#[pyfunction]
fn parameter(name: &str) -> ParameterLike {
ParameterLike(rust::amplitudes::parameter(name))
}
+ /// A term which stays constant during an optimization
+ ///
+ /// Parameters
+ /// ----------
+ /// value : float
+ /// The numerical value of the constant
+ ///
+ /// Returns
+ /// -------
+ /// ParameterLike
+ /// An object that can be used as the input for many Amplitude constructors
+ ///
#[pyfunction]
fn constant(value: Float) -> ParameterLike {
ParameterLike(rust::amplitudes::constant(value))
}
+ /// An Amplitude which represents a single scalar value
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// value : ParameterLike
+ /// The scalar parameter contained in the Amplitude
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
#[pyfunction]
fn Scalar(name: &str, value: ParameterLike) -> Amplitude {
Amplitude(rust::amplitudes::common::Scalar::new(name, value.0))
}
+ /// An Amplitude which represents a complex scalar value
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// re: ParameterLike
+ /// The real part of the complex value contained in the Amplitude
+ /// im: ParameterLike
+ /// The imaginary part of the complex value contained in the Amplitude
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
#[pyfunction]
fn ComplexScalar(name: &str, re: ParameterLike, im: ParameterLike) -> Amplitude {
Amplitude(rust::amplitudes::common::ComplexScalar::new(
@@ -2233,6 +2712,26 @@ pub(crate) mod laddu {
))
}
+ /// An Amplitude which represents a complex scalar value in polar form
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// r: ParameterLike
+ /// The magnitude of the complex value contained in the Amplitude
+ /// theta: ParameterLike
+ /// The argument of the complex value contained in the Amplitude
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
#[pyfunction]
fn PolarComplexScalar(name: &str, r: ParameterLike, theta: ParameterLike) -> Amplitude {
Amplitude(rust::amplitudes::common::PolarComplexScalar::new(
@@ -2240,11 +2739,70 @@ pub(crate) mod laddu {
))
}
+ /// An spherical harmonic Amplitude
+ ///
+ /// Computes a spherical harmonic (:math:`Y_{\ell}^m(\theta, \varphi)`)
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// l : int
+ /// The total orbital momentum (:math:`l \geq 0`)
+ /// m : int
+ /// The orbital moment (:math:`-l \leq m \leq l`)
+ /// angles : Angles
+ /// The spherical angles to use in the calculation
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
#[pyfunction]
fn Ylm(name: &str, l: usize, m: isize, angles: &Angles) -> Amplitude {
Amplitude(rust::amplitudes::ylm::Ylm::new(name, l, m, &angles.0))
}
+ /// An spherical harmonic Amplitude for polarized beam experiments
+ ///
+ /// Computes a polarized spherical harmonic (:math:`Z_{\ell}^{(r)m}(\theta, \varphi; P_\gamma, \Phi)`) with additional
+ /// polarization-related factors (see notes)
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// l : int
+ /// The total orbital momentum (:math:`l \geq 0`)
+ /// m : int
+ /// The orbital moment (:math:`-l \leq m \leq l`)
+ /// r : {'+', 'plus', 'pos', 'positive', '-', 'minus', 'neg', 'negative'}
+ /// The reflectivity (related to naturality of parity exchange)
+ /// angles : Angles
+ /// The spherical angles to use in the calculation
+ /// polarization : Polarization
+ /// The beam polarization to use in the calculation
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
+ /// Notes
+ /// -----
+ /// This amplitude is described in [Mathieu]_
+ ///
+ /// .. [Mathieu] Mathieu, V., Albaladejo, M., Fernández-Ramírez, C., Jackura, A. W., Mikhasenko, M., Pilloni, A., & Szczepaniak, A. P. (2019). Moments of angular distribution and beam asymmetries in :math:`\eta\pi^0` photoproduction at GlueX. Physical Review D, 100(5). `doi:10.1103/physrevd.100.054017 `_
+ ///
#[pyfunction]
fn Zlm(
name: &str,
@@ -2264,6 +2822,36 @@ pub(crate) mod laddu {
))
}
+ /// An relativistic Breit-Wigner Amplitude
+ ///
+ /// This Amplitude represents a relativistic Breit-Wigner with known angular momentum
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// mass : ParameterLike
+ /// The mass of the resonance
+ /// width : ParameterLike
+ /// The (nonrelativistic) width of the resonance
+ /// l : int
+ /// The total orbital momentum (:math:`l > 0`)
+ /// daughter_1_mass : Mass
+ /// The mass of the first decay product
+ /// daughter_2_mass : Mass
+ /// The mass of the second decay product
+ /// resonance_mass: Mass
+ /// The total mass of the resonance
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
#[pyfunction]
fn BreitWigner(
name: &str,
@@ -2285,6 +2873,63 @@ pub(crate) mod laddu {
))
}
+ /// A fixed K-Matrix Amplitude for :math:`f_0` mesons
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// couplings : list of list of ParameterLike
+ /// Each initial-state coupling (as a list of pairs of real and imaginary parts)
+ /// channel : int
+ /// The channel onto which the K-Matrix is projected
+ /// mass: Mass
+ /// The total mass of the resonance
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
+ /// Notes
+ /// -----
+ /// This Amplitude follows the prescription of [Kopf]_ and fixes the K-Matrix to data
+ /// from that paper, leaving the couplings to the initial state free
+ ///
+ /// +---------------+-------------------+
+ /// | Channel index | Channel |
+ /// +===============+===================+
+ /// | 0 | :math:`\pi\pi` |
+ /// +---------------+-------------------+
+ /// | 1 | :math:`2\pi 2\pi` |
+ /// +---------------+-------------------+
+ /// | 2 | :math:`K\bar{K}` |
+ /// +---------------+-------------------+
+ /// | 3 | :math:`\eta\eta` |
+ /// +---------------+-------------------+
+ /// | 4 | :math:`\eta\eta'` |
+ /// +---------------+-------------------+
+ ///
+ /// +-------------------+
+ /// | Pole names |
+ /// +===================+
+ /// | :math:`f_0(500)` |
+ /// +-------------------+
+ /// | :math:`f_0(980)` |
+ /// +-------------------+
+ /// | :math:`f_0(1370)` |
+ /// +-------------------+
+ /// | :math:`f_0(1500)` |
+ /// +-------------------+
+ /// | :math:`f_0(1710)` |
+ /// +-------------------+
+ ///
+ /// .. [Kopf] Kopf, B., Albrecht, M., Koch, H., Küßner, M., Pychy, J., Qin, X., & Wiedner, U. (2021). Investigation of the lightest hybrid meson candidate with a coupled-channel analysis of :math:`\bar{p}p`-, :math:`\pi^- p`- and :math:`\pi \pi`-Data. The European Physical Journal C, 81(12). `doi:10.1140/epjc/s10052-021-09821-2 `__
+ ///
#[pyfunction]
fn KopfKMatrixF0(
name: &str,
@@ -2300,6 +2945,57 @@ pub(crate) mod laddu {
))
}
+ /// A fixed K-Matrix Amplitude for :math:`f_2` mesons
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// couplings : list of list of ParameterLike
+ /// Each initial-state coupling (as a list of pairs of real and imaginary parts)
+ /// channel : int
+ /// The channel onto which the K-Matrix is projected
+ /// mass: Mass
+ /// The total mass of the resonance
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
+ /// Notes
+ /// -----
+ /// This Amplitude follows the prescription of [Kopf]_ and fixes the K-Matrix to data
+ /// from that paper, leaving the couplings to the initial state free
+ ///
+ /// +---------------+-------------------+
+ /// | Channel index | Channel |
+ /// +===============+===================+
+ /// | 0 | :math:`\pi\pi` |
+ /// +---------------+-------------------+
+ /// | 1 | :math:`2\pi 2\pi` |
+ /// +---------------+-------------------+
+ /// | 2 | :math:`K\bar{K}` |
+ /// +---------------+-------------------+
+ /// | 3 | :math:`\eta\eta` |
+ /// +---------------+-------------------+
+ ///
+ /// +---------------------+
+ /// | Pole names |
+ /// +=====================+
+ /// | :math:`f_2(1270)` |
+ /// +---------------------+
+ /// | :math:`f_2'(1525)` |
+ /// +---------------------+
+ /// | :math:`f_2(1810)` |
+ /// +---------------------+
+ /// | :math:`f_2(1950)` |
+ /// +---------------------+
+ ///
#[pyfunction]
fn KopfKMatrixF2(
name: &str,
@@ -2315,6 +3011,49 @@ pub(crate) mod laddu {
))
}
+ /// A fixed K-Matrix Amplitude for :math:`a_0` mesons
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// couplings : list of list of ParameterLike
+ /// Each initial-state coupling (as a list of pairs of real and imaginary parts)
+ /// channel : int
+ /// The channel onto which the K-Matrix is projected
+ /// mass: Mass
+ /// The total mass of the resonance
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
+ /// Notes
+ /// -----
+ /// This Amplitude follows the prescription of [Kopf]_ and fixes the K-Matrix to data
+ /// from that paper, leaving the couplings to the initial state free
+ ///
+ /// +---------------+-------------------+
+ /// | Channel index | Channel |
+ /// +===============+===================+
+ /// | 0 | :math:`\pi\eta` |
+ /// +---------------+-------------------+
+ /// | 1 | :math:`K\bar{K}` |
+ /// +---------------+-------------------+
+ ///
+ /// +-------------------+
+ /// | Pole names |
+ /// +===================+
+ /// | :math:`a_0(980)` |
+ /// +-------------------+
+ /// | :math:`a_0(1450)` |
+ /// +-------------------+
+ ///
#[pyfunction]
fn KopfKMatrixA0(
name: &str,
@@ -2330,6 +3069,51 @@ pub(crate) mod laddu {
))
}
+ /// A fixed K-Matrix Amplitude for :math:`a_2` mesons
+ ///
+ /// This Amplitude follows the prescription of [Kopf]_ and fixes the K-Matrix to data
+ /// from that paper, leaving the couplings to the initial state free
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// couplings : list of list of ParameterLike
+ /// Each initial-state coupling (as a list of pairs of real and imaginary parts)
+ /// channel : int
+ /// The channel onto which the K-Matrix is projected
+ /// mass: Mass
+ /// The total mass of the resonance
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
+ /// Notes
+ /// -----
+ /// +---------------+-------------------+
+ /// | Channel index | Channel |
+ /// +===============+===================+
+ /// | 0 | :math:`\pi\eta` |
+ /// +---------------+-------------------+
+ /// | 1 | :math:`K\bar{K}` |
+ /// +---------------+-------------------+
+ /// | 2 | :math:`\pi\eta'` |
+ /// +---------------+-------------------+
+ ///
+ /// +-------------------+
+ /// | Pole names |
+ /// +===================+
+ /// | :math:`a_2(1320)` |
+ /// +-------------------+
+ /// | :math:`a_2(1700)` |
+ /// +-------------------+
+ ///
#[pyfunction]
fn KopfKMatrixA2(
name: &str,
@@ -2345,6 +3129,51 @@ pub(crate) mod laddu {
))
}
+ /// A fixed K-Matrix Amplitude for :math:`\rho` mesons
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// couplings : list of list of ParameterLike
+ /// Each initial-state coupling (as a list of pairs of real and imaginary parts)
+ /// channel : int
+ /// The channel onto which the K-Matrix is projected
+ /// mass: Mass
+ /// The total mass of the resonance
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
+ /// Notes
+ /// -----
+ /// This Amplitude follows the prescription of [Kopf]_ and fixes the K-Matrix to data
+ /// from that paper, leaving the couplings to the initial state free
+ ///
+ /// +---------------+-------------------+
+ /// | Channel index | Channel |
+ /// +===============+===================+
+ /// | 0 | :math:`\pi\pi` |
+ /// +---------------+-------------------+
+ /// | 1 | :math:`2\pi 2\pi` |
+ /// +---------------+-------------------+
+ /// | 2 | :math:`K\bar{K}` |
+ /// +---------------+-------------------+
+ ///
+ /// +--------------------+
+ /// | Pole names |
+ /// +====================+
+ /// | :math:`\rho(770)` |
+ /// +--------------------+
+ /// | :math:`\rho(1700)` |
+ /// +--------------------+
+ ///
#[pyfunction]
fn KopfKMatrixRho(
name: &str,
@@ -2359,7 +3188,47 @@ pub(crate) mod laddu {
&mass.0,
))
}
-
+ /// A fixed K-Matrix Amplitude for the :math:`\pi_1(1600)` hybrid meson
+ ///
+ /// Parameters
+ /// ----------
+ /// name : str
+ /// The Amplitude name
+ /// couplings : list of list of ParameterLike
+ /// Each initial-state coupling (as a list of pairs of real and imaginary parts)
+ /// channel : int
+ /// The channel onto which the K-Matrix is projected
+ /// mass: Mass
+ /// The total mass of the resonance
+ ///
+ /// Returns
+ /// -------
+ /// Amplitude
+ /// An Amplitude which can be registered by a ``Manager``
+ ///
+ /// See Also
+ /// --------
+ /// laddu.Manager
+ ///
+ /// Notes
+ /// -----
+ /// This Amplitude follows the prescription of [Kopf]_ and fixes the K-Matrix to data
+ /// from that paper, leaving the couplings to the initial state free
+ ///
+ /// +---------------+-------------------+
+ /// | Channel index | Channel |
+ /// +===============+===================+
+ /// | 0 | :math:`\pi\eta` |
+ /// +---------------+-------------------+
+ /// | 1 | :math:`\pi\eta'` |
+ /// +---------------+-------------------+
+ ///
+ /// +---------------------+
+ /// | Pole names |
+ /// +=====================+
+ /// | :math:`\pi_1(1600)` |
+ /// +---------------------+
+ ///
#[pyfunction]
fn KopfKMatrixPi1(
name: &str,
From a054510b18012d73122a10f19f40d66185112c7e Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Thu, 7 Nov 2024 15:28:51 -0500
Subject: [PATCH 25/26] ci: separate command for rebuilding docs and making
docfiles
---
.justfile | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/.justfile b/.justfile
index db1b84f..e8f3033 100644
--- a/.justfile
+++ b/.justfile
@@ -4,11 +4,15 @@ default:
develop:
CARGO_INCREMENTAL=true maturin develop -r --uv --strip
-makedocs:
+builddocs:
CARGO_INCREMENTAL=true maturin build -r --strip
uv pip install ./target/wheels/*
make -C docs clean
make -C docs html
+makedocs:
+ make -C docs clean
+ make -C docs html
+
odoc:
firefox ./docs/build/html/index.html
From d73052c46b267cc3a25bb2c42e673f558dfe6b2e Mon Sep 17 00:00:00 2001
From: denehoffman
Date: Thu, 7 Nov 2024 15:37:50 -0500
Subject: [PATCH 26/26] docs: add RTDs documentation badge to README and link
to repo in docs
---
README.md | 2 ++
docs/source/index.rst | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7565ff4..80db038 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,8 @@
+
+
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 00e03d6..1dcb286 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -3,9 +3,11 @@ laddu
``laddu`` is an library for analyzing particle physics data. It is written in Rust with bindings to Python which are documented here.
+Right now, this page just documents the basic Python API of the library, but the plan is to eventually write a tutorial here for general use. See the `GitHub Repository `_ for more details.
+
.. toctree::
- :hidden:
:caption: API Reference
+ :maxdepth: 1
:name: sec-api
modules/index