Metadata-Version: 2.4
Name: lockkernel
Version: 1.1.0
Summary: Locking kernel universality: exact thresholds and exponents of synchronization transitions, and fits of measured branches with error bars and refusals
Author: Tanvir Mahmud Mahim
License: Apache-2.0
Project-URL: Homepage, https://github.com/TaN-MM-Org/lockkernel
Project-URL: Issues, https://github.com/TaN-MM-Org/lockkernel/issues
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: numpy>=1.22
Requires-Dist: scipy>=1.8
Requires-Dist: mpmath>=1.2
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Dynamic: license-file

# lockkernel

[![PyPI](https://img.shields.io/pypi/v/lockkernel.svg)](https://pypi.org/project/lockkernel/) [![tests](https://github.com/TaN-MM-Org/lockkernel/actions/workflows/ci.yml/badge.svg)](https://github.com/TaN-MM-Org/lockkernel/actions)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)

A population of oscillators with spread frequencies begins to
synchronize when the coupling passes a threshold, and the order
parameter then grows from zero as a power of the distance above it.
That exponent has been derived separately for each case in which it
has been asked. This package computes it from one statement: the
quantity deciding the onset is the **locking kernel** W(u) -- the
time-averaged projection a single oscillator keeps on the collective
field, as a function of its detuning in units of the locked region's
width -- and one substitution turns the self-consistency into an
**exact parametric solution**,

```
chiN(Omega) = Omega / H(Omega),     R(Omega) = H(Omega),
H(Omega)    = Omega * integral p(Omega u) W(u) du,
```

for ANY frequency distribution and ANY kernel: threshold
chiN_c = 1/(p(0) m) with m the kernel mass, exponent beta = 1/(s-1)
for a kernel tail W ~ |u|^-s with 1 < s < 3 and beta = 1/2 for
s >= 3, the amplitude in closed form, and the order of the transition
from the sign of one nonlocal integral. Sweeping Omega traces the
branch with no root finding and no cancellation near threshold, so
exponents come out at tens of digits, not two.

Version 1.1 adds the lab half, `lockkernel.measured`: given a
MEASURED synchronization branch -- from an experiment, or from a
simulation whose kernel is not known -- it fits threshold, exponent
and amplitude together with error bars, reads the kernel tail back
off the exponent through the same dictionary the theory half proves,
plans how many points a target error bar costs, and refuses, with an
explanation, whenever the data cannot support the number asked of
them.

## Install

```
pip install lockkernel        # NumPy + SciPy + mpmath
```

## The two halves in one example

```python
import numpy as np
import mpmath as mp
from lockkernel import fit_branch, kernel_tail_from_beta, points_for_beta
from lockkernel.kernels import conservative, power_tail, predicted_beta
from lockkernel.lineshapes import lorentzian, gaussian
from lockkernel.parametric import threshold, sweep, extract_beta

mp.mp.dps = 25

# THEORY: exact threshold and exponent for any line and kernel.
line = lorentzian()
print(threshold(line, conservative()))       # chiN_c = 1/(p(0) pi)
print(extract_beta(line, power_tail(2.5),
                   np.linspace(-6, -3, 7))[-1])   # -> 2/3 = 1/(s-1)

# LAB: fit a measured branch -- here, points generated by the exact
# solution stand in for your data; pass your own (chi, R) instead.
pts = sweep(line, conservative(), np.linspace(-4.3, -2.0, 12))
chi = np.array([float(p[0]) for p in pts])
r = np.array([float(p[1]) for p in pts])
fit = fit_branch(chi, r, reference="cavity ensemble run 17, 2026-09-18")
print(fit.chi_c, "+-", fit.sigma_chi_c)     # the measured threshold
print(fit.beta, "+-", fit.sigma_beta)       # the measured exponent

# ... and what the exponent says about the kernel the system has:
s, s_sigma = kernel_tail_from_beta(fit.beta, max(fit.sigma_beta, 0.01))

# Plan the next measurement: points needed for a 1 % exponent error
# bar over two decades of reduced coupling at 5 % scatter.
n, achieved = points_for_beta(0.01, decades=2.0, sigma_log=0.05)
```

## What is inside

- **`lineshapes` / `kernels`**: frequency distributions (Lorentzian,
  Gaussian, Student-t, box, and your own) and locking kernels
  (conservative 1/(1+u^2), Kuramoto, algebraic tails, Gaussian,
  coupling-spread-averaged heterogeneous kernels) as small declared
  objects with masses, tails and supports the quadrature can use.
- **`parametric`**: the exact parametric solution -- `threshold`,
  `branch_point`, `sweep`, `extract_beta`, the closed-form amplitude
  along the whole line of exponents, the order-of-transition
  criterion `c_coefficient` and `fold_interval` for hysteretic
  onsets -- in mpmath arbitrary precision.
- **`measured`** (new in 1.1): `fit_branch` for measured (chi, R)
  points with mandatory provenance -- threshold, exponent and
  amplitude with asymptotic error bars and residual diagnostics;
  `kernel_tail_from_beta`, the exact dictionary inverted with error
  propagation, refusing to name a single tail where beta is
  consistent with 1/2 and only the class s >= 3 is identified;
  `beta_relative_sigma` / `points_for_beta`, the closed-form error
  bar of a log-log slope and its exact inversion into measurement
  effort.
- **`cumulant` / `ensemble` / `exact`**: the class-resolved
  second-cumulant dynamics of the underlying spin problem, principled
  discretisation of a line into detuning classes (algebraic tails
  kept, stiff far tail propagated exactly), and exact
  small-ensemble references (full Hamiltonian, collective-spin and
  class-symmetric diagonalisation) the solver is tested against.

## Refusals, not guesses

Uncited measured data, too few points, couplings out of order, less
than a decade of reduced coupling, a threshold the data do not
resolve, an exponent consistent with the unidentifiable beta = 1/2
class, an exponent below every kernel's reach: each is refused with
the reason and, where one exists, the remedy.

## How it is checked

64 tests (Python 3.9-3.14, run in CI on every push). The theory half
is held against closed forms at high precision: the parametric branch
against two independent closed forms to better than 1e-28 over six
decades of locking bandwidth; the exponent formula along a line of
kernel tails; closed amplitude values (1, pi/2, 4/3, pi^2/4) on four
line shapes; the coefficient c computed two independent ways to
1e-11; the class-resolved solver against exact diagonalisation of
the full Hamiltonian. The lab half is checked BY the theory half:
`fit_branch` must recover the threshold and the exponent of branches
generated by the exact parametric solution -- conservative kernel
(beta = 1) and algebraic tail s = 2.5 (beta = 2/3) -- noiselessly and
under seeded noise within its own reported error bars; the planning
closed form is held against seeded simulation and its inversion is
asserted on both sides.

## Honest limits

Recorded rather than hidden: the near-threshold power law is the
LEADING behavior, and data taken far above the onset bend away from
it -- the decade refusal and the residual diagnostics guard the fit,
not the user's range choice; the fit's error bars are the standard
asymptotic covariance, honest when the model holds and the noise
estimate is right, and stated as such. In the underlying undamped
spin problem the truncated cumulant hierarchy is secularly unstable
at long times (the module carries physicality diagnostics any
long-time use must be gated on), and the time-averaged kernel is an
assumption checked against the full dynamics, not a theorem.

## Provenance

This package is the maintained distribution of the reference
implementation for the locking-kernel universality study
([Tanvir-Mahmud-Mahim/locking-kernel-universality](https://github.com/Tanvir-Mahmud-Mahim/locking-kernel-universality),
concept DOI
[10.5281/zenodo.22696369](https://doi.org/10.5281/zenodo.22696369)),
whose scripts, archived run records and figures remain with the
study. The core modules are carried over unchanged in v1.1.0; the
`measured` module and the packaging are new here. Copyright as in
[NOTICE](NOTICE).

## Support and governance

Written and maintained by Tanvir Mahmud Mahim (Department of
Electrical and Electronic Engineering, BRAC University), who reviews
every change and takes the final decision on scope and releases.
Design questions are discussed in the open in issues and pull
requests, and the standing rule of
[CONTRIBUTING.md](CONTRIBUTING.md) binds the maintainer exactly as it
binds contributors: a change that touches the physics arrives with a
test, and a claim arrives with its source.

Support runs through the
[issue tracker](https://github.com/TaN-MM-Org/lockkernel/issues).
Usage questions are welcome alongside bug reports; a docstring that
left a unit or a convention unclear is treated as a documentation
bug, not user error.

## License

Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
