Metadata-Version: 2.4
Name: pmns-transforms
Version: 1.1.0
Summary: PMNS matrix parameterisation conversions for neutrino oscillation analysis as described in https://arxiv.org/abs/2507.02101.
Author-email: Andres Lopez Moreno <andres.lopezmoreno@lapp.in2p3.fr>
License: GPL-3.0-or-later
Keywords: neutrino,pmns,physics,reparameterization,reparameterisation,jarlskog
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.21
Provides-Extra: examples
Requires-Dist: matplotlib>=3.5; extra == "examples"
Dynamic: license-file

# pmns-transforms

A lightweight scientific Python library to build, transform, and analyse PMNS mixing matrices across nine Tait–Bryan parameterisations that privilege specific flavour/mass symmetries.

This codebase is based on and aligned with the framework developed in **Testing T2K’s Bayesian constraints with priors in alternate parameterisations (arXiv:2507.02101).**

The library implements a numerically stable, vectorised API to:
- construct a PMNS matrix for any of the nine parameterisations;
- extract the mixing angles and Dirac CP phase from a PMNS matrix under a target parameterisation;
- transform parameters from one parameterisation to another (composition of the above two);
- compute the Jacobian of the parameterisation transform via central finite differences;
- compute importance weights for reweighting a uniform-in-sin²θ prior from one parameterisation to another;
- compute the Jarlskog invariant.

## Parameterisation overview

- Parameterisations are identified by strings in {e, mu, tau}{1,2,3}, for example: e3 (canonical/PDG-like), mu1, tau2, etc.
- Each string indicates the “single element” index (the one equal to s13 e^{-i δ}) and thereby fixes the row/column symmetries privileged by that parameterisation (as in the paper’s promoted row–column symmetry).
- **See appendix A of arXiv:2507.02101 to identify parameterisations with the corresponding row/column symmetries and their matrix form.**

## Conventions and ranges

- All angles are in radians.
- Angle ranges: θ12, θ23, θ13 ∈ [0, π/2]; δ ∈ (−π, π].
- Single-element convention: U_single = s13 · exp(−i δ) at the position set by the chosen parameterisation.
- Broadcasting: all public functions accept scalars or array-like inputs and broadcast them with NumPy semantics. Scalars yield 3×3 arrays; arrays yield outputs with leading (3,3) followed by the broadcast shape.

### Non-identifiable regimes and NaN policy

Certain inverse mappings (matrix → angles) become singular or numerically ill-conditioned:
- When c13 ≈ 0, θ12 and θ23 are not identifiable under this chart; they are returned as NaN.
- When the denominator for cos δ (2 s12 c12 s23 c23 s13) is ill-conditioned, δ is returned as NaN.
- When sin δ ≈ 0, the sign of δ is resolved deterministically via cos δ: δ = 0 if cos δ ≥ 0, else δ = π.

This mirrors the parameterisation singularities discussed in the paper and yields stable, deterministic behaviour.

## Installation

Requirements
- Python ≥ 3.8
- NumPy ≥ 1.21

Install from source
- Standard install:
  - `pip install .`
- Editable install (development):
  - `pip install -e .`

## Quick start

```python
import pmns_transforms as pmns

# Example angles (radians)
th12, th23, th13 = 0.59, 0.79, 0.15
dcp = 0.30

# 1) Build the PMNS matrix in the canonical e3 parameterisation
U = pmns.get_mixing_matrix('e3', th12, th23, th13, dcp)  # shape (3, 3)

# 2) Extract angles under a different parameterisation (e.g., mu1)
#    original_parameterisation informs the phase/sign convention of U
rec_th12, rec_th23, rec_th13, rec_dcp = pmns.get_parameters('mu1', U, original_parameterisation='e3')

# 3) Transform angles directly between parameterisations
new_th12, new_th23, new_th13, new_dcp = pmns.transform('e3', 'mu1', th12, th23, th13, dcp)

# 4) Compute the Jarlskog invariant (broadcasted over inputs)
Jcp = pmns.get_Jarlskog(th12, th23, th13, dcp)

# 5) Compute the 4×4 Jacobian ∂(new params)/∂(old params) via central differences
J = pmns.get_jacobian('e3', 'mu1', th12, th23, th13, dcp)  # shape (4, 4)

# 6) Compute importance weights to reweight a uniform-in-sin²θ sample from e3 to mu1
#    Equivalent to |det J_{sin²}| at each point; used for prior reweighting
import numpy as np
th12_arr = np.linspace(0.2, 1.3, 500)
th23_arr, th13_arr, dcp_arr = 0.80, 0.15, 0.30
weights = pmns.get_weights('e3', 'mu1', th12_arr, th23_arr, th13_arr, dcp_arr)
```

Vectorised example (the user constructs arrays with NumPy):
```python
import numpy as np
import pmns_transforms as pmns

# Vectorised inputs broadcast automatically
th12 = np.linspace(0.2, 1.3, 1000)
th23 = 0.80
th13 = 0.15
dcp  = np.linspace(-np.pi, np.pi, 1000)

U_all = pmns.get_mixing_matrix('e2', th12, th23, th13, dcp)  # shape (3, 3, 1000)
```

## API summary

- get_mixing_matrix(parameterisation, th12, th23, th13, dcp) → ndarray
  - Builds U under the chosen parameterisation. Returns (3,3) or (3,3,*broadcast_shape).

- get_parameters(target_parameterisation, mixing_matrix, original_parameterisation='e3') → (th12, th23, th13, dcp)
  - Extracts angles/phase under target_parameterisation, assuming mixing_matrix was built using original_parameterisation. Handles non-identifiable regimes via NaN, and sign choices as documented below.

- transform(original_parameterisation, target_parameterisation, th12, th23, th13, dcp) → (new_th12, new_th23, new_th13, new_dcp)
  - Convenience wrapper: build → extract.

- get_jacobian(original_parameterisation, target_parameterisation, th12, th23, th13, dcp, h=1e-5) → ndarray
  - Computes the 4×4 Jacobian ∂(θ12, θ23, θ13, δ)_target / ∂(θ12, θ23, θ13, δ)_orig via central finite differences. Returns shape (4, 4) for scalar inputs or (4, 4, *broadcast_shape) for arrays. Results are unreliable near c13 ≈ 0 or δ ≈ 0, ±π.

- get_weights(original_parameterisation, target_parameterisation, th12, th23, th13, dcp, h=1e-5) → ndarray
  - Returns |det J_{sin²}| at each point, where J_{sin²} is the Jacobian of (sin²θ12, sin²θ23, sin²θ13, δ)_orig → (sin²θ12, sin²θ23, sin²θ13, δ)_target. Samples drawn uniformly in sin²θ in the original parameterisation and reweighted by these values represent a uniform distribution in the target parameterisation. Values of inf or NaN indicate singular or ill-conditioned points.

- get_Jarlskog(th12, th23, th13, dcp) → ndarray
  - Computes J = s12 c12 s23 c23 s13 c13² sin δ. Returns a real array matching the broadcast shape.


## Numerical notes

- Inverse stability:
  - The sine and cosine of the angles are clipped for numerical safety before square roots/acos.
  - If cos(θ13) is below a small epsilon: θ12 and θ23 → NaN.
  - If the denominator of the expression for cos δ is ill-conditioned: δ → NaN.

- Shape checks: get_parameters coerces the input matrix to an ndarray and validates a leading 3×3 shape.


## Example scripts

The package comes with example scripts that require matplotlib.

**`examples/plot_taitBryanPriors.py`** — reproduces figure 6 of arXiv:2507.02101. Draws samples uniform in sin²θ, treats each sample as belonging to every non-e3 parameterisation in turn, transforms to e3, and overlays the resulting 1D marginals.
- To run: `python examples/plot_taitBryanPriors.py`
- Output: `dists_overlaid_to_standard.png`

**`examples/plot_reweighted_marginals.py`** — demonstrates `get_weights`. Draws samples uniform in sin²θ under e3, transforms to each of the nine parameterisations, and applies importance weights. Flat weighted histograms (blue) confirm the weights are correct; orange shows the raw unweighted distribution.
- To run: `python examples/plot_reweighted_marginals.py`
- Output: `figures/reweighted_marginals.png`

**`scripts/plot_parameterisation_distributions.py`** — overlays the sin²θ distributions of all nine parameterisations on a single figure, without reweighting, to visualise the support of each prior.
- To run: `python scripts/plot_parameterisation_distributions.py`
- Output: `figures/dists_overlaid.png`

## Citing

If you use this package in your work, please cite following paper:
- Testing T2K’s Bayesian constraints with priors in alternate parameterisations: arXiv:2507.02101.

## License

GPL-3.0-or-later. See LICENSE for details.
