lockkernel 1.1.1__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lockkernel-1.1.1/src/lockkernel.egg-info → lockkernel-1.3.0}/PKG-INFO +483 -46
- {lockkernel-1.1.1 → lockkernel-1.3.0}/README.md +482 -45
- {lockkernel-1.1.1 → lockkernel-1.3.0}/pyproject.toml +1 -1
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel/__init__.py +5 -4
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel/exact.py +27 -6
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel/kernels.py +81 -6
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel/lineshapes.py +14 -0
- lockkernel-1.3.0/src/lockkernel/measured.py +628 -0
- lockkernel-1.3.0/src/lockkernel/parametric.py +513 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0/src/lockkernel.egg-info}/PKG-INFO +483 -46
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel.egg-info/SOURCES.txt +1 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/tests/conftest.py +1 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/tests/test_cumulant.py +47 -0
- lockkernel-1.3.0/tests/test_measured.py +415 -0
- lockkernel-1.3.0/tests/test_onset.py +458 -0
- lockkernel-1.1.1/src/lockkernel/measured.py +0 -271
- lockkernel-1.1.1/src/lockkernel/parametric.py +0 -277
- lockkernel-1.1.1/tests/test_measured.py +0 -132
- {lockkernel-1.1.1 → lockkernel-1.3.0}/LICENSE +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/MANIFEST.in +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/NOTICE +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/setup.cfg +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel/cumulant.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel/ensemble.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel.egg-info/dependency_links.txt +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel.egg-info/requires.txt +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/src/lockkernel.egg-info/top_level.txt +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/tests/test_meanfield.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/tests/test_parametric.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.3.0}/tests/test_universality.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lockkernel
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Locking kernel universality: exact thresholds and exponents of synchronization transitions, and fits of measured branches with error bars and refusals
|
|
5
5
|
Author: Tanvir Mahmud Mahim
|
|
6
6
|
License: Apache-2.0
|
|
@@ -59,10 +59,13 @@ The collective motion must be the one the oscillators themselves
|
|
|
59
59
|
produce (the "self-consistency condition"); one substitution turns
|
|
60
60
|
that condition into an exact formula (a "parametric solution") valid
|
|
61
61
|
for any frequency spread and any kernel. The exponent depends only on how fast the kernel falls off
|
|
62
|
-
far from the
|
|
62
|
+
far from the center (its **tail**). The **lab half** (`lockkernel.measured`)
|
|
63
63
|
works the other way round: it fits measured points for the threshold
|
|
64
64
|
and the exponent, reads the kernel's tail back off the exponent, and
|
|
65
|
-
plans how many points a target error bar costs.
|
|
65
|
+
plans how many points a target error bar costs. It also tests whether
|
|
66
|
+
the data are consistent with the power law at their stated noise, and
|
|
67
|
+
carries the uncertainties of the measured couplings and of calibration
|
|
68
|
+
scales into the error bars. When the data cannot
|
|
66
69
|
support the number asked for, it stops with an error message that says
|
|
67
70
|
why, rather than returning a number that looks fine but is not.
|
|
68
71
|
|
|
@@ -119,7 +122,12 @@ why, rather than returning a number that looks fine but is not.
|
|
|
119
122
|
`beta = 1/(s-1)` for `1 < s < 3` and `beta = 1/2` for `s >= 3` or
|
|
120
123
|
for a kernel with no algebraic tail. So a measured `beta` above 1/2
|
|
121
124
|
names the tail, `s = 1 + 1/beta`, while `beta = 1/2` only says
|
|
122
|
-
"`s >= 3`" and cannot name one value.
|
|
125
|
+
"`s >= 3`" and cannot name one value. The value 1/2 needs a line
|
|
126
|
+
with a rounded top at its centre (`p''(0) < 0`, true for every
|
|
127
|
+
shipped line except `box`). On a line that is flat at the centre,
|
|
128
|
+
such as `box`, a tail gives `beta = 1/(s-1)` for every `s > 1`
|
|
129
|
+
(so below 1/2 once `s > 3`), and a kernel with no tail gives a jump
|
|
130
|
+
instead of a power law (see [Limits](#limits)).
|
|
123
131
|
- **Order of the onset** -- for the conservative kernel, the sign of
|
|
124
132
|
one number `c` (an integral over the line shape) decides it:
|
|
125
133
|
`c > 0` gives a smooth (continuous) onset, `c < 0` a jump with
|
|
@@ -144,7 +152,10 @@ and mpmath 1.2 or newer, and nothing else.
|
|
|
144
152
|
- **Precision.** The theory half (`parametric`, `kernels`,
|
|
145
153
|
`lineshapes`) works in mpmath's arbitrary precision and returns
|
|
146
154
|
mpmath numbers. Set the working precision with `mpmath.mp.dps`
|
|
147
|
-
(decimal digits). The
|
|
155
|
+
(decimal digits). The reduced coupling `eps` keeps that precision
|
|
156
|
+
however close to the threshold it is (since 1.2.0), so the working
|
|
157
|
+
precision does not need to exceed the number of decades you step
|
|
158
|
+
towards the onset. The lab half and the dynamics work in ordinary
|
|
148
159
|
floating point with NumPy.
|
|
149
160
|
- **Line shapes** are normalised probability densities; `lorentzian`
|
|
150
161
|
and `gaussian` take a full width at half maximum, `box` a half
|
|
@@ -153,8 +164,9 @@ and mpmath 1.2 or newer, and nothing else.
|
|
|
153
164
|
## Examples
|
|
154
165
|
|
|
155
166
|
Each example below runs as written, and the output shown is what it
|
|
156
|
-
printed with lockkernel 1.
|
|
157
|
-
|
|
167
|
+
printed with lockkernel 1.3.0 (examples 1 to 9 print the same as with
|
|
168
|
+
1.2.0). Line widths, couplings and noise levels are illustrative
|
|
169
|
+
values, not taken from any experiment.
|
|
158
170
|
|
|
159
171
|
### 1. Threshold and exponent from the theory
|
|
160
172
|
|
|
@@ -253,9 +265,12 @@ For conservative spins the exponent is 1, so near the onset
|
|
|
253
265
|
`R = A eps`, with `A = pi p(0)^2 / c`. This example runs at mpmath's
|
|
254
266
|
default 15 digits; before version 1.1.1 `amplitude` and
|
|
255
267
|
`c_coefficient` needed about 30 digits to be right (see
|
|
256
|
-
[Corrections](#corrections-in-earlier-versions)).
|
|
257
|
-
|
|
258
|
-
|
|
268
|
+
[Corrections](#corrections-in-earlier-versions)). For two unit-width
|
|
269
|
+
Gaussian peaks at `+-a`, `c = (1 - 2x D(x))/pi` with `x = a/sqrt(2)`
|
|
270
|
+
and `D` Dawson's function, and the tests hold `c_coefficient` to this
|
|
271
|
+
formula. So the onset turns first order when the peaks are more than
|
|
272
|
+
2.61386 widths apart, where Dawson's function has its maximum.
|
|
273
|
+
Example 8 follows the first-order case through its hysteresis loop.
|
|
259
274
|
|
|
260
275
|
### 4. Fit a measured branch
|
|
261
276
|
|
|
@@ -289,7 +304,7 @@ print(f"kernel tail s = {s:.2f} +- {s_err:.2f} (true 2.5)")
|
|
|
289
304
|
```
|
|
290
305
|
|
|
291
306
|
```
|
|
292
|
-
threshold chi_c = 0.59441 +- 3.
|
|
307
|
+
threshold chi_c = 0.59441 +- 3.8e-09 (exact 0.59441)
|
|
293
308
|
exponent beta = 0.673 +- 0.003 (exact 0.667)
|
|
294
309
|
amplitude A = 0.652 +- 0.019
|
|
295
310
|
eps range 1.56e-07 .. 1.09e-03, rms log residual 0.011
|
|
@@ -302,7 +317,9 @@ couplings and order parameters, and a `reference` that says where
|
|
|
302
317
|
they come from (it is required). The error bars are the standard
|
|
303
318
|
asymptotic ones of a least-squares fit: they are right when the power
|
|
304
319
|
law holds over the fitted range and `sigma_r` is right. Without
|
|
305
|
-
`sigma_r`, the scatter of the points sets them.
|
|
320
|
+
`sigma_r`, the scatter of the points sets them. Since 1.2.0 they are
|
|
321
|
+
built from exact derivatives; 1.1.1 printed `3.5e-09` for the
|
|
322
|
+
threshold here (see [Corrections](#corrections-in-earlier-versions)). Here the fitted
|
|
306
323
|
exponent is 2.4 of its own error bars from the exact 2/3; the tests
|
|
307
324
|
allow four (see [How the results are checked](#how-the-results-are-checked)).
|
|
308
325
|
`kernel_tail_from_beta` turns the exponent into the kernel's tail
|
|
@@ -311,7 +328,8 @@ exponent `s = 1 + 1/beta`, with error `sigma_beta / beta^2`.
|
|
|
311
328
|
### 5. Plan the measurement, and a refusal
|
|
312
329
|
|
|
313
330
|
```python
|
|
314
|
-
from lockkernel import beta_relative_sigma, points_for_beta, kernel_tail_from_beta
|
|
331
|
+
from lockkernel import (beta_relative_sigma, points_for_beta, kernel_tail_from_beta,
|
|
332
|
+
plan_fit, points_for_fit)
|
|
315
333
|
|
|
316
334
|
# How many points, spread evenly in log(eps) over 2 decades, with 5 %
|
|
317
335
|
# scatter in R, for an error bar of 0.01 on beta (threshold known)?
|
|
@@ -319,6 +337,15 @@ n, achieved = points_for_beta(0.01, decades=2.0, sigma_log=0.05)
|
|
|
319
337
|
print(f"points needed: {n} (error bar {achieved:.5f}); "
|
|
320
338
|
f"with {n - 1}: {beta_relative_sigma(n - 1, 2.0, 0.05):.5f}")
|
|
321
339
|
|
|
340
|
+
# The same range (eps = 1e-4 .. 1e-2), but with the threshold fitted too,
|
|
341
|
+
# as fit_branch does it:
|
|
342
|
+
plan = plan_fit(n, 1e-4, 1e-2, beta=2/3, sigma_log=0.05)
|
|
343
|
+
print(f"{n} points, threshold fitted: beta +- {plan.sigma_beta:.4f}, "
|
|
344
|
+
f"chi_c +- {plan.sigma_chi_c_rel:.1e} (relative)")
|
|
345
|
+
n_fit, plan = points_for_fit(0.01, 1e-4, 1e-2, beta=2/3, sigma_log=0.05)
|
|
346
|
+
print(f"points needed with the threshold fitted: {n_fit} "
|
|
347
|
+
f"(error bar {plan.sigma_beta:.5f})")
|
|
348
|
+
|
|
322
349
|
# A fitted beta = 0.52 +- 0.02 cannot name a kernel tail:
|
|
323
350
|
try:
|
|
324
351
|
kernel_tail_from_beta(0.52, 0.02)
|
|
@@ -328,15 +355,28 @@ except ValueError as err:
|
|
|
328
355
|
|
|
329
356
|
```
|
|
330
357
|
points needed: 12 (error bar 0.00999); with 11: 0.01035
|
|
358
|
+
12 points, threshold fitted: beta +- 0.0197, chi_c +- 1.4e-05 (relative)
|
|
359
|
+
points needed with the threshold fitted: 57 (error bar 0.00995)
|
|
331
360
|
refused: beta = 0.52 +- 0.02 is consistent with 1/2, which identifies only the CLASS s >= 3 (compact support or decay faster than |u|^-3); no single tail exponent can be named from it
|
|
332
361
|
```
|
|
333
362
|
|
|
334
363
|
`beta_relative_sigma` gives the error bar of a straight-line slope on a
|
|
335
364
|
log-log plot. Despite its name, the result is the absolute error of
|
|
336
|
-
`beta`, not a relative one. It assumes the threshold is known,
|
|
337
|
-
`
|
|
338
|
-
|
|
339
|
-
|
|
365
|
+
`beta`, not a relative one. It assumes the threshold is known, and
|
|
366
|
+
`points_for_beta` finds the smallest number of points that meets the
|
|
367
|
+
target on that assumption.
|
|
368
|
+
|
|
369
|
+
`fit_branch` fits the threshold as well, and that costs precision:
|
|
370
|
+
the same 12 points give an error bar about twice as large. `plan_fit`
|
|
371
|
+
gives the error bars `fit_branch` will report (for `beta`, and
|
|
372
|
+
relative ones for `chi_c` and `A`), before any data are taken, from
|
|
373
|
+
the planned range of `eps` and the expected `beta`. `points_for_fit`
|
|
374
|
+
turns that into a number of points: 57 instead of 12 here. How much
|
|
375
|
+
fitting the threshold costs depends on how many decades the points
|
|
376
|
+
span: for 12 points the error bar grows about 2.0 times over two
|
|
377
|
+
decades, 1.6 over three, and 1.3 over five. These are the standard
|
|
378
|
+
asymptotic error bars, held in the tests against 300 seeded simulated
|
|
379
|
+
fits (within 12 %).
|
|
340
380
|
|
|
341
381
|
### 6. A spread of coupling strengths changes the exponent
|
|
342
382
|
|
|
@@ -369,7 +409,86 @@ oscillator's own kernel has none: `s = min(s0, (gamma-2)/eta)`, with
|
|
|
369
409
|
`s0` the tail of the single-oscillator kernel (infinite for Kuramoto).
|
|
370
410
|
Here `s = 1.8`, so `beta = 1/(s-1) = 1.25`, which is `1/(gamma-3)`.
|
|
371
411
|
|
|
372
|
-
### 7. The
|
|
412
|
+
### 7. The amplitude when beta = 1/2
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
import mpmath as mp
|
|
416
|
+
from lockkernel.lineshapes import lorentzian, gaussian
|
|
417
|
+
from lockkernel.kernels import kuramoto, gaussian_kernel
|
|
418
|
+
from lockkernel.parametric import amplitude_curvature, branch_point
|
|
419
|
+
|
|
420
|
+
mp.mp.dps = 20
|
|
421
|
+
for line, kern in [(lorentzian(1.0), kuramoto()), (gaussian(1.0), kuramoto()),
|
|
422
|
+
(gaussian(1.0), gaussian_kernel())]:
|
|
423
|
+
A = amplitude_curvature(line, kern)
|
|
424
|
+
_, R, eps = branch_point(line, kern, mp.mpf("1e-6"))
|
|
425
|
+
print(f"{kern.name:9s} kernel, {line.name:10s} line: A = {mp.nstr(A, 12)}, "
|
|
426
|
+
f"R/sqrt(eps) at Omega = 1e-6: {mp.nstr(R / mp.sqrt(eps), 12)}")
|
|
427
|
+
print("sqrt(pi) =", mp.nstr(mp.sqrt(mp.pi), 12))
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
kuramoto kernel, lorentzian line: A = 1.0, R/sqrt(eps) at Omega = 1e-6: 0.999999999999
|
|
432
|
+
kuramoto kernel, gaussian line: A = 1.77245385091, R/sqrt(eps) at Omega = 1e-6: 1.7724538509
|
|
433
|
+
gaussian kernel, gaussian line: A = 1.41421356237, R/sqrt(eps) at Omega = 1e-6: 1.41421356237
|
|
434
|
+
sqrt(pi) = 1.77245385091
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
For a kernel with no tail, or a tail `s > 3`, the onset is
|
|
438
|
+
`R = A eps^(1/2)`. The first correction to the self-consistency then
|
|
439
|
+
comes from the curvature of the line at its centre, `p''(0)`, and
|
|
440
|
+
`amplitude_curvature` returns
|
|
441
|
+
`A = G0^(3/2) sqrt(2 / (-p''(0) M2))`, where `G0 = p(0) m` and
|
|
442
|
+
`M2 = integral u^2 W(u) du` is the kernel's second moment
|
|
443
|
+
(`Kernel.second_moment()`). For the Kuramoto kernel on a Lorentzian
|
|
444
|
+
line this is 1, the closed form `R = sqrt(1 - chiN_c/chiN)`. On a
|
|
445
|
+
Gaussian line the width cancels, which leaves `sqrt(pi)` (Kuramoto
|
|
446
|
+
kernel) and `sqrt(2)` (Gaussian kernel) at any width. The branch
|
|
447
|
+
approaches these values with corrections of relative size
|
|
448
|
+
`Omega^2`, or `Omega^(s-3)` for a tail `3 < s < 5`.
|
|
449
|
+
|
|
450
|
+
### 8. A first-order onset: the hysteresis loop
|
|
451
|
+
|
|
452
|
+
```python
|
|
453
|
+
import mpmath as mp
|
|
454
|
+
from lockkernel.lineshapes import bimodal_gaussian
|
|
455
|
+
from lockkernel.kernels import conservative
|
|
456
|
+
from lockkernel.parametric import fold_interval, threshold, extract_beta
|
|
457
|
+
|
|
458
|
+
line = bimodal_gaussian(4.0) # two Gaussian peaks (width 1) at -2 and +2
|
|
459
|
+
f = fold_interval(line, n=30, lo=-2, hi=1)
|
|
460
|
+
print("threshold :", mp.nstr(threshold(line, conservative()), 10))
|
|
461
|
+
print("hysteresis from chiN =", mp.nstr(f["chiN_lo"], 10), "to", mp.nstr(f["chiN_hi"], 10))
|
|
462
|
+
print("R jumps from 0 to :", mp.nstr(f["R_jump"], 10))
|
|
463
|
+
print("R where the high branch ends:", mp.nstr(f["R_high_at_lo"], 10))
|
|
464
|
+
try:
|
|
465
|
+
extract_beta(line, conservative(), [-3, -4, -5])
|
|
466
|
+
except ValueError as err:
|
|
467
|
+
print("refused:", str(err)[:72], "...")
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
threshold : 5.89561378
|
|
472
|
+
hysteresis from chiN = 3.493404803 to 5.89561378
|
|
473
|
+
R jumps from 0 to : 0.8480505911
|
|
474
|
+
R where the high branch ends: 0.3342759883
|
|
475
|
+
refused: eps = -0.0016464 <= 0 at Omega = 10^-3: the branch is at or below the th ...
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
With the peaks 4 widths apart `c < 0` (example 3). The branch leaves
|
|
479
|
+
the threshold backwards, then turns round at `chiN = 3.4934`. In the
|
|
480
|
+
usual reading of such a fold (the package computes where the
|
|
481
|
+
solutions are, not whether they are stable), raising the coupling
|
|
482
|
+
keeps the unsynchronized state up to the threshold, where `R` jumps to
|
|
483
|
+
0.848; lowering it again keeps the synchronized state down to
|
|
484
|
+
`chiN = 3.4934` (where `R = 0.334`) before it collapses. `fold_interval` samples the branch on `n` points
|
|
485
|
+
between `Omega = 10^lo` and `10^hi` and then finds the turning points
|
|
486
|
+
and the jump exactly. The tests check every number it returns against
|
|
487
|
+
an independent closed form (the Voigt profile). There is no exponent
|
|
488
|
+
on such a branch, and `extract_beta` says so instead of returning
|
|
489
|
+
one.
|
|
490
|
+
|
|
491
|
+
### 9. The quantum spin model against exact diagonalisation
|
|
373
492
|
|
|
374
493
|
```python
|
|
375
494
|
import numpy as np
|
|
@@ -410,6 +529,98 @@ should. `xi^2` is the **Wineland spin-squeezing parameter**:
|
|
|
410
529
|
1 for uncorrelated spins, below 1 when the spins are entangled in a way
|
|
411
530
|
that improves phase measurements.
|
|
412
531
|
|
|
532
|
+
### 10. Are the data consistent with the power law?
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
import numpy as np
|
|
536
|
+
from lockkernel import fit_branch
|
|
537
|
+
|
|
538
|
+
# Conservative spins on a Lorentzian line of FWHM 1: chi_c = 1/2 and,
|
|
539
|
+
# exactly, R = eps/(1+eps). 14 points from eps = 1e-4, 1 % seeded noise.
|
|
540
|
+
rng = np.random.default_rng(4)
|
|
541
|
+
for eps_max in (0.01, 0.1, 0.5):
|
|
542
|
+
eps = np.geomspace(1e-4, eps_max, 14)
|
|
543
|
+
r = eps / (1 + eps) * np.exp(rng.normal(0.0, 0.01, eps.size))
|
|
544
|
+
fit = fit_branch(0.5 * (1 + eps), r, sigma_r=0.01 * r,
|
|
545
|
+
reference="illustrative: closed form + 1 % noise")
|
|
546
|
+
print(f"eps up to {eps_max:4}: beta = {fit.beta:.4f} +- {fit.sigma_beta:.4f}, "
|
|
547
|
+
f"chi2 = {fit.chi2:5.1f} for {fit.dof} dof, p = {fit.p_value:.2g}")
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
```
|
|
551
|
+
eps up to 0.01: beta = 1.0001 +- 0.0037, chi2 = 11.1 for 11 dof, p = 0.44
|
|
552
|
+
eps up to 0.1: beta = 0.9833 +- 0.0019, chi2 = 48.3 for 11 dof, p = 1.3e-06
|
|
553
|
+
eps up to 0.5: beta = 0.9546 +- 0.0014, chi2 = 584.2 for 11 dof, p = 3.4e-118
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
The true exponent is 1. When the points reach far above the threshold,
|
|
557
|
+
the branch bends away from the power law, and the fit returns a biased
|
|
558
|
+
exponent with a small error bar: 0.9833 +- 0.0019 is 9 error bars from
|
|
559
|
+
1. The error bar alone does not show this. The chi-square test does.
|
|
560
|
+
When `sigma_r` is given, `fit_branch` also returns `chi2` (the weighted
|
|
561
|
+
sum of squared misfits in `log R`), its degrees of freedom `dof`
|
|
562
|
+
(points minus 3) and `p_value`, the chance of a misfit at least this
|
|
563
|
+
large if the law and the stated errors were right. A very small
|
|
564
|
+
`p_value` means one of the two is wrong: drop the points farthest from
|
|
565
|
+
the threshold, or check `sigma_r`. Without `sigma_r` the scatter of the
|
|
566
|
+
points sets the error scale, so there is nothing to test, and `chi2`
|
|
567
|
+
and `p_value` are `None`.
|
|
568
|
+
|
|
569
|
+
### 11. Errors in the couplings, and calibration
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
import numpy as np
|
|
573
|
+
from lockkernel import fit_branch, with_calibration
|
|
574
|
+
|
|
575
|
+
# Stand-in measurement: R = 0.65 eps^(2/3) above chi_c = 0.5, at 14
|
|
576
|
+
# recorded couplings. Each true coupling differs from the recorded one by
|
|
577
|
+
# a seeded error of 5e-6 (a tenth of the distance to the threshold at the
|
|
578
|
+
# first point), and R carries 1 % seeded noise.
|
|
579
|
+
rng = np.random.default_rng(2)
|
|
580
|
+
chi = 0.5 * (1 + np.geomspace(1e-4, 1e-1, 14))
|
|
581
|
+
sigma_chi = np.full(chi.size, 5e-6)
|
|
582
|
+
chi_true = chi + rng.normal(0.0, 1.0, chi.size) * sigma_chi
|
|
583
|
+
r = 0.65 * (chi_true / 0.5 - 1) ** (2 / 3) * np.exp(rng.normal(0.0, 0.01, chi.size))
|
|
584
|
+
|
|
585
|
+
ref = "illustrative: power law + seeded noise"
|
|
586
|
+
plain = fit_branch(chi, r, ref, sigma_r=0.01 * r)
|
|
587
|
+
full = fit_branch(chi, r, ref, sigma_r=0.01 * r, sigma_chi=sigma_chi)
|
|
588
|
+
for name, f in (("errors of R only:", plain), ("errors of R and chi:", full)):
|
|
589
|
+
print(f"{name:20s} beta = {f.beta:.4f} +- {f.sigma_beta:.4f}, "
|
|
590
|
+
f"chi_c = {f.chi_c:.7f} +- {f.sigma_chi_c:.1e}, p = {f.p_value:.2g}")
|
|
591
|
+
|
|
592
|
+
# The couplings share a 2 % calibration uncertainty, R a 5 % one:
|
|
593
|
+
cal = with_calibration(full, chi_scale_rel=0.02, r_scale_rel=0.05)
|
|
594
|
+
print(f"with calibration: chi_c +- {cal.sigma_chi_c:.1e}, "
|
|
595
|
+
f"A = {cal.amplitude:.3f} +- {cal.sigma_amplitude:.3f}, "
|
|
596
|
+
f"beta +- {cal.sigma_beta:.4f} (unchanged)")
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
```
|
|
600
|
+
errors of R only: beta = 0.6698 +- 0.0020, chi_c = 0.4999987 +- 1.2e-06, p = 0.00051
|
|
601
|
+
errors of R and chi: beta = 0.6658 +- 0.0026, chi_c = 0.5000034 +- 3.7e-06, p = 0.15
|
|
602
|
+
with calibration: chi_c +- 1.0e-02, A = 0.649 +- 0.033, beta +- 0.0026 (unchanged)
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Near the threshold `R` rises steeply with the coupling, so a small
|
|
606
|
+
error in the coupling moves `R` a lot: by `beta sigma_chi / (chi -
|
|
607
|
+
chi_c)` in `log R`. Pass the coupling errors as `sigma_chi` and
|
|
608
|
+
`fit_branch` adds this term to each point's error (to first order, and
|
|
609
|
+
repeats the fit until the weights settle). Here, without it, the error
|
|
610
|
+
bars are too small and the chi-square test says so (`p = 0.00051`).
|
|
611
|
+
With it the fit is consistent (`p = 0.15`) and the exponent lands within
|
|
612
|
+
its error bar of 2/3. `fit_branch` refuses `sigma_chi` larger than a
|
|
613
|
+
quarter of `chi - chi_c` at any point, where first order is not enough.
|
|
614
|
+
|
|
615
|
+
`with_calibration` adds the uncertainty of an overall scale factor: one
|
|
616
|
+
common to all couplings (for example the conversion of a laser power
|
|
617
|
+
into `chiN`), and one common to all `R` (for example a detection
|
|
618
|
+
efficiency). Scaling every coupling scales the fitted threshold by the
|
|
619
|
+
same factor and leaves `beta` and `A` exactly as they were; scaling
|
|
620
|
+
`R` scales only `A`. So the two calibrations add to the error bars of
|
|
621
|
+
`chi_c` and `A` in quadrature, and the exponent is immune to both. The
|
|
622
|
+
tests check both statements by refitting scaled data.
|
|
623
|
+
|
|
413
624
|
## What is in the package
|
|
414
625
|
|
|
415
626
|
The top level imports the `measured` names below and the submodules
|
|
@@ -424,12 +635,14 @@ directly.
|
|
|
424
635
|
Makers: `lorentzian`, `gaussian`, `student_t`, `box`,
|
|
425
636
|
`bimodal_gaussian`; `LINESHAPES` maps names to them.
|
|
426
637
|
- `Kernel` -- an even kernel with `W(0) = 1`, its tail exponent, its
|
|
427
|
-
support,
|
|
638
|
+
support, `mass()` and `second_moment()` (the integral of
|
|
639
|
+
`u^2 W(u)`, finite only for compact support, fast decay or `s > 3`).
|
|
640
|
+
Makers: `conservative`, `kuramoto`,
|
|
428
641
|
`power_tail(s)`, `gaussian_kernel`; `KERNELS` maps names to them.
|
|
429
642
|
- `heterogeneous(base, degree_exponent, eta=1, k_min=1)` -- the kernel
|
|
430
643
|
averaged over a power-law spread of coupling strengths (example 6).
|
|
431
644
|
- `predicted_beta(s)` -- the exponent the rule gives for tail `s`
|
|
432
|
-
(`None` meaning no algebraic tail).
|
|
645
|
+
(`None` meaning no algebraic tail), for a line with a rounded top.
|
|
433
646
|
|
|
434
647
|
**The exact solution** (`lockkernel.parametric`, mpmath precision)
|
|
435
648
|
|
|
@@ -439,27 +652,45 @@ directly.
|
|
|
439
652
|
- `threshold`, `branch_point`, `sweep`, `extract_beta` -- the
|
|
440
653
|
threshold, one point `(chiN, R, eps)` of the branch, the branch at
|
|
441
654
|
`Omega = 10^e` for a list of `e`, and the local exponents along it.
|
|
655
|
+
`eps` is computed from `G(0) - G(Omega)` directly, so it keeps the
|
|
656
|
+
working precision however small it is.
|
|
442
657
|
- `c_coefficient`, `amplitude` -- for the conservative kernel, the
|
|
443
658
|
number `c` whose sign sets the order of the onset, and the amplitude
|
|
444
659
|
`A = pi p(0)^2 / c`.
|
|
445
660
|
- `tail_integral`, `amplitude_general` -- the amplitude for a kernel
|
|
446
661
|
with tail exponent `1 < s < 3`.
|
|
662
|
+
- `amplitude_curvature` -- the amplitude when `beta = 1/2` (no tail,
|
|
663
|
+
or a tail `s > 3`, on a line with a rounded top; example 7).
|
|
447
664
|
- `fold_interval` -- samples the branch and, if it folds back, returns
|
|
448
|
-
the coupling range of the hysteresis
|
|
449
|
-
(`None` if the branch does not fold
|
|
665
|
+
the coupling range of the hysteresis, the turning points and the
|
|
666
|
+
jump in `R` (`None` if the branch does not fold; example 8).
|
|
450
667
|
|
|
451
668
|
**Fitting measured data** (`lockkernel.measured`, also at the top level)
|
|
452
669
|
|
|
453
|
-
- `fit_branch(chi, r, reference, sigma_r=None, min_decade=1.0
|
|
454
|
-
returns a `BranchFit` with `chi_c`, `beta`,
|
|
455
|
-
errors, `n_points`, `eps_range`,
|
|
456
|
-
root-mean-square misfit in `log R`) and
|
|
670
|
+
- `fit_branch(chi, r, reference, sigma_r=None, min_decade=1.0,
|
|
671
|
+
sigma_chi=None)` -- returns a `BranchFit` with `chi_c`, `beta`,
|
|
672
|
+
`amplitude`, their errors, `n_points`, `eps_range`,
|
|
673
|
+
`residual_rms_log` (the root-mean-square misfit in `log R`) and
|
|
674
|
+
`reference`. Since 1.3.0 it also has `dof`, `chi2` and `p_value`
|
|
675
|
+
(the last two `None` without `sigma_r`; example 10),
|
|
676
|
+
`sigma_chi_used` and `calibration_rel`. `sigma_chi` gives the errors
|
|
677
|
+
of the couplings (example 11).
|
|
678
|
+
- `with_calibration(fit, chi_scale_rel=0, r_scale_rel=0)` -- the same
|
|
679
|
+
fit with the relative uncertainties of an overall coupling scale and
|
|
680
|
+
an overall `R` scale added to the error bars of `chi_c` and `A`
|
|
681
|
+
(example 11).
|
|
457
682
|
- `kernel_tail_from_beta(beta, sigma_beta=0, n_sigma=2)` -- the tail
|
|
458
683
|
exponent `s` and its error.
|
|
459
684
|
- `beta_relative_sigma(n_points, decades, sigma_log)`,
|
|
460
685
|
`points_for_beta(target_sigma_beta, decades, sigma_log)` -- the
|
|
461
|
-
error bar of `beta` for a planned measurement
|
|
462
|
-
points for a target error bar.
|
|
686
|
+
error bar of `beta` for a planned measurement with the threshold
|
|
687
|
+
known, and the number of points for a target error bar.
|
|
688
|
+
- `plan_fit(n_points, eps_min, eps_max, beta, sigma_log,
|
|
689
|
+
fit_threshold=True)`, `points_for_fit(target_sigma_beta, eps_min,
|
|
690
|
+
eps_max, beta, sigma_log)` -- the same for the fit `fit_branch`
|
|
691
|
+
actually does, with the threshold fitted: `plan_fit` returns a
|
|
692
|
+
`FitPlan` with `sigma_beta`, `sigma_chi_c_rel` and
|
|
693
|
+
`sigma_amplitude_rel` (example 5).
|
|
463
694
|
|
|
464
695
|
**Quantum spin dynamics** (`lockkernel.cumulant`, `lockkernel.ensemble`,
|
|
465
696
|
`lockkernel.exact`)
|
|
@@ -479,7 +710,7 @@ directly.
|
|
|
479
710
|
- `symmetric_exact` (all detunings equal, any number of emitters),
|
|
480
711
|
`full_exact` (full quantum problem, up to about twelve emitters) and
|
|
481
712
|
`class_exact` (emitters grouped by detuning, a few tens) -- exact
|
|
482
|
-
references. `class_exact` is
|
|
713
|
+
references. `class_exact` is tested against `full_exact` since 1.3.0.
|
|
483
714
|
|
|
484
715
|
Each function's docstring (`help(lockkernel.fit_branch)`, for example)
|
|
485
716
|
gives its inputs and conventions.
|
|
@@ -492,12 +723,19 @@ gives its inputs and conventions.
|
|
|
492
723
|
saying where they come from;
|
|
493
724
|
- fewer than 6 points are given, or `chi` and `r` differ in length
|
|
494
725
|
(three parameters with error bars need more);
|
|
495
|
-
- the couplings are not finite and strictly increasing, or
|
|
496
|
-
parameter is not finite and positive (points below the
|
|
497
|
-
`R = 0`, carry no exponent information; drop them);
|
|
498
|
-
- `sigma_r` is not positive or not on the same grid as `r`;
|
|
726
|
+
- the couplings are not finite, positive and strictly increasing, or
|
|
727
|
+
an order parameter is not finite and positive (points below the
|
|
728
|
+
threshold, `R = 0`, carry no exponent information; drop them);
|
|
729
|
+
- `sigma_r` is not finite and positive, or not on the same grid as `r`;
|
|
499
730
|
- the fit does not converge, or its covariance is singular (the data
|
|
500
731
|
cannot pin down the three parameters separately);
|
|
732
|
+
- the fitted exponent runs into the limits of the search, 0.05 or 20
|
|
733
|
+
(new in 1.3.0; the data are not a power law of this kind, for
|
|
734
|
+
example because `R` hardly changes, as at a jump);
|
|
735
|
+
- `sigma_chi` is given without `sigma_r`, is negative, or is larger
|
|
736
|
+
than a quarter of `chi - chi_c` at some point (new in 1.3.0);
|
|
737
|
+
- `with_calibration` gets a negative uncertainty, or a fit that
|
|
738
|
+
already includes one;
|
|
501
739
|
- the fitted threshold is indistinguishable from the smallest
|
|
502
740
|
coupling: the data do not reach the onset;
|
|
503
741
|
- the fitted points span less than `min_decade` decades of `eps`
|
|
@@ -506,13 +744,32 @@ gives its inputs and conventions.
|
|
|
506
744
|
- a fitted `beta` is consistent with 1/2 (only the class `s >= 3` is
|
|
507
745
|
identified), clearly below 1/2 (no kernel gives that; the fit has
|
|
508
746
|
probably left the near-threshold range), or not positive;
|
|
509
|
-
- a planned measurement has fewer than 3 points
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
747
|
+
- a planned measurement has fewer than 3 points (6 with the threshold
|
|
748
|
+
fitted, as `fit_branch` needs), a number of points that is not a
|
|
749
|
+
whole number, a non-positive or reversed range, a non-positive
|
|
750
|
+
scatter or `beta`, a non-positive target, or would need more than
|
|
751
|
+
10^7 points;
|
|
752
|
+
- `extract_beta` meets a point with `eps <= 0`: the branch is at or
|
|
753
|
+
below the threshold there, because it bends back (a first-order
|
|
754
|
+
onset, example 8) or is flat (`R` jumps at the threshold), and there
|
|
755
|
+
is no exponent to measure;
|
|
756
|
+
- `amplitude_curvature` is asked for a line with `p''(0) >= 0` (a flat
|
|
757
|
+
top or a dip at the centre) or a kernel with tail `s <= 3`, and
|
|
758
|
+
`second_moment` for a kernel with tail `s <= 3` (it diverges);
|
|
759
|
+
- `fold_interval` finds a fold on a line with compact support (`box`),
|
|
760
|
+
which it cannot refine, or cannot bracket a turning point on its
|
|
761
|
+
grid;
|
|
762
|
+
- `power_tail(s)` or `predicted_beta(s)` is asked for `s <= 1` (the
|
|
763
|
+
kernel mass would be infinite), or `heterogeneous` for `gamma <= 2`,
|
|
764
|
+
a non-positive `eta` or `k_min`, or a resulting tail `s <= 1`;
|
|
765
|
+
- a line shape is given a width that is not finite and positive;
|
|
514
766
|
- `tail_integral` is asked for `s` outside `1 < s < 3`, where it
|
|
515
767
|
diverges;
|
|
768
|
+
- `amplitude_general` is asked for a kernel without a tail in
|
|
769
|
+
`1 < s < 3`, or (new in 1.3.0) its default tail amplitude `C`, read
|
|
770
|
+
off the kernel at `|u| = 10^6`, differs by more than a relative 1e-6
|
|
771
|
+
from the value at `10^9` (pass `C` yourself then);
|
|
772
|
+
- `class_exact` gets class populations that are not whole numbers;
|
|
516
773
|
- a line shape without a floating-point distribution
|
|
517
774
|
(`bimodal_gaussian`) is asked for `cdf`, `ppf` or a class table;
|
|
518
775
|
- the solver of the differential equations fails in `evolve` or
|
|
@@ -520,7 +777,7 @@ gives its inputs and conventions.
|
|
|
520
777
|
|
|
521
778
|
## How the results are checked
|
|
522
779
|
|
|
523
|
-
|
|
780
|
+
128 automated tests run on every push and pull request, on Python 3.9
|
|
524
781
|
to 3.14, and once more on Python 3.10 with the oldest NumPy (1.22.0),
|
|
525
782
|
SciPy (1.8.0) and mpmath (1.2.1) the package allows. The numerical
|
|
526
783
|
checks compare the package with something independent of it: a closed
|
|
@@ -549,6 +806,79 @@ the package does not import matplotlib. The main checks:
|
|
|
549
806
|
`Omega` = 1e-6 and 2e-6 to a relative 1e-9, on four lines; `R/eps` at `Omega = 1e-6` is
|
|
550
807
|
within a relative 1e-5 of the amplitude.
|
|
551
808
|
|
|
809
|
+
**New in 1.2.0** (15 digits unless stated; the references are closed
|
|
810
|
+
forms evaluated separately from the package's integrals)
|
|
811
|
+
|
|
812
|
+
- `eps`, `chiN` and `R` on the Lorentzian line match `eps = Omega/a`,
|
|
813
|
+
`chiN = a + Omega`, `R = Omega/(a + Omega)` to a relative 1e-13 for
|
|
814
|
+
`Omega` from 1e-6 to 1e-14, and to 1e-28 at 30 digits down to
|
|
815
|
+
`Omega` = 1e-20.
|
|
816
|
+
- Box line, kernels `1/(1+|u|^s)` with `s` = 1.5, 2.5, 4 and 6: `G`
|
|
817
|
+
and `eps` match the closed form `G = X 2F1(1, 1/s; 1+1/s; -X^s)`,
|
|
818
|
+
`X = 1/Omega`, to a relative 1e-13 at `Omega` = 1e-1, 1e-3 and 1e-5
|
|
819
|
+
(down to `eps` of about 1e-26). On this flat-topped line
|
|
820
|
+
`extract_beta` gives `1/(s-1)` = 1/3 and 1/5 for `s` = 4 and 6, within
|
|
821
|
+
1e-6.
|
|
822
|
+
- Two-peaked line: `c_coefficient` matches `(1 - 2x D(x))/pi` to 1e-13
|
|
823
|
+
for separations 1, 2 and 4; the sign of `c` flips across the
|
|
824
|
+
separation 2.61386 (the maximum of Dawson's function, located by root
|
|
825
|
+
finding); `G` matches the Voigt closed form to a relative 1e-13.
|
|
826
|
+
- `fold_interval`: every number it returns (couplings, order
|
|
827
|
+
parameters, `Omega` of the turning points and of the jump) matches
|
|
828
|
+
the Voigt closed form, with turning points found by root finding on
|
|
829
|
+
its derivative, to a relative 1e-12, for a branch that leaves the
|
|
830
|
+
threshold backwards (two peaks 4 widths apart) and for an S-shaped
|
|
831
|
+
branch (a three-peak line); it returns `None` for a monotonic branch.
|
|
832
|
+
- `extract_beta` refuses the backward branch and the flat one (box
|
|
833
|
+
line, Kuramoto kernel, where `eps` is exactly 0).
|
|
834
|
+
- `second_moment` matches `pi/8` (Kuramoto), `sqrt(pi)/2` (Gaussian
|
|
835
|
+
kernel) and `2 (pi/s)/sin(3 pi/s)` (`s` = 4, 6) to 1e-12.
|
|
836
|
+
`amplitude_curvature` is 1 for the Kuramoto kernel on a Lorentzian
|
|
837
|
+
line and `sqrt(pi)`, `sqrt(2)` on Gaussian lines of two widths, to
|
|
838
|
+
1e-12; at 20 digits it matches `R/sqrt(eps)` at `Omega = 1e-6` to a
|
|
839
|
+
relative 1e-10 for three kernel-line pairs, and to 2e-6 for `s = 4`.
|
|
840
|
+
|
|
841
|
+
**New in 1.3.0** (15 digits unless stated; every reference is a closed
|
|
842
|
+
form or a calculation done another way)
|
|
843
|
+
|
|
844
|
+
- Kernel masses for slow tails, `power_tail(s)` with `s` = 1.05, 1.1,
|
|
845
|
+
1.25 and 1.5, against `2 (pi/s)/sin(pi/s)` to a relative 1e-14, and
|
|
846
|
+
1e-28 at 30 digits. The averaged Kuramoto kernel of example 6 with
|
|
847
|
+
`gamma` = 3.4, 3.8 and 4.4 against its mass
|
|
848
|
+
`(pi/2)(gamma-2)/(gamma-3)` to 1e-13. `second_moment` for `s` = 3.1,
|
|
849
|
+
3.25 and 3.5 against `2 (pi/s)/sin(3 pi/s)` to 1e-13.
|
|
850
|
+
- For `s = 1.1` on a Lorentzian line the threshold equals
|
|
851
|
+
`s sin(pi/s)/4` to 1e-13, and `chiN/chiN_c - 1` on the branch equals
|
|
852
|
+
`eps` from its separate integral to 1e-10.
|
|
853
|
+
- `tail_integral` against the closed forms on Lorentzian and Gaussian
|
|
854
|
+
lines of widths 0.001, 1 and 1000, for `s` from 1.1 to 2.99, to a
|
|
855
|
+
relative 1e-13 (and 1e-27 at 30 digits). `amplitude_general` against
|
|
856
|
+
its closed form on the Lorentzian line for `s` = 1.1, 1.5, 2.5 and
|
|
857
|
+
2.9, to 1e-12.
|
|
858
|
+
- `amplitude_general` refuses the conservative kernel averaged over
|
|
859
|
+
coupling strengths, whose tail amplitude (a closed form, also checked
|
|
860
|
+
by quadrature) is not reached at `|u| = 10^6`, and accepts the
|
|
861
|
+
averaged Kuramoto kernel, whose tail is an exact power law (its
|
|
862
|
+
default `C` matches the closed form `(gamma-2) B(s/2, 3/2)/2` to give
|
|
863
|
+
the same amplitude to 1e-12).
|
|
864
|
+
- `class_exact` matches `full_exact` (8 emitters, two sets of classes)
|
|
865
|
+
to 1e-10 in `R` and 1e-9 in `xi^2`, with the times in any order.
|
|
866
|
+
- `fit_branch`: `chi2` matches the sum recomputed from the returned
|
|
867
|
+
parameters; over 300 seeded fits under the model the p-values pass a
|
|
868
|
+
Kolmogorov-Smirnov test for uniformity and `chi2` averages to `dof`
|
|
869
|
+
within four standard errors; on the exact conservative branch with
|
|
870
|
+
1 % noise, points up to `eps = 0.01` give `p > 0.05` and `beta`
|
|
871
|
+
within 3 error bars of 1, points up to `eps = 0.5` give
|
|
872
|
+
`p < 1e-10` and `beta` more than 10 error bars off.
|
|
873
|
+
- With coupling noise of a tenth of `chi - chi_c` at the first point,
|
|
874
|
+
the error bars of `chi_c`, `beta` and `A` with `sigma_chi` match the
|
|
875
|
+
scatter of 300 seeded fits within 12 %, while without it `sigma_beta`
|
|
876
|
+
is more than 15 % too small. `sigma_chi = 0` gives exactly the plain
|
|
877
|
+
fit. A fit refuses data generated with `beta` = 0.02, 0.04 and 25.
|
|
878
|
+
- Refitting data with all couplings scaled by 1.07 scales `chi_c` by
|
|
879
|
+
1.07 to 1e-9 and leaves `beta` and `A` unchanged to 1e-7; scaling `R`
|
|
880
|
+
scales only `A`. `with_calibration` adds in quadrature to 1e-12.
|
|
881
|
+
|
|
552
882
|
**The exponent rule** (25 digits, Gaussian line, `Omega` down to 1e-6)
|
|
553
883
|
|
|
554
884
|
- `beta = 1/(s-1)` or 1/2 for the kernel family with `s` = 1.5, 1.8,
|
|
@@ -577,6 +907,17 @@ the package does not import matplotlib. The main checks:
|
|
|
577
907
|
- The planning formula matches 6000 seeded simulated fits within 5 %,
|
|
578
908
|
and `points_for_beta` returns the smallest `n` that meets the target
|
|
579
909
|
(checked on both sides).
|
|
910
|
+
- (New in 1.2.0) On noiseless data reaching `eps = 1e-7`, the error
|
|
911
|
+
bars `fit_branch` reports, and those `plan_fit` predicts, equal the
|
|
912
|
+
ones built from mpmath's numerical derivatives at 30 digits to a
|
|
913
|
+
relative 1e-6. With the threshold known, `plan_fit` equals
|
|
914
|
+
`beta_relative_sigma` to 1e-12. Over 300 seeded noisy fits (1 %
|
|
915
|
+
scatter), the scatter of `chi_c`, `beta` and `A` matches `plan_fit`,
|
|
916
|
+
and the median reported error bar matches the scatter, within 12 %.
|
|
917
|
+
`points_for_fit` is checked on both sides, and the error bar is
|
|
918
|
+
checked to fall with every added point from 6 to 400. `sigma_beta`
|
|
919
|
+
and `sigma_A` from `plan_fit` do not depend on `beta` and
|
|
920
|
+
`sigma_chi_c` scales as `1/beta` (to 1e-12).
|
|
580
921
|
|
|
581
922
|
**Dynamics and discretisation**
|
|
582
923
|
|
|
@@ -596,11 +937,80 @@ the package does not import matplotlib. The main checks:
|
|
|
596
937
|
within 5e-3 for couplings `r` = 1.2, 2 and 3 times the threshold,
|
|
597
938
|
and stays below 0.05 at 0.6 times the threshold.
|
|
598
939
|
|
|
599
|
-
Not covered by tests: `
|
|
600
|
-
`class_exact`, `physicality` and `valid_window`.
|
|
940
|
+
Not covered by tests: `physicality` and `valid_window`.
|
|
601
941
|
|
|
602
942
|
## Corrections in earlier versions
|
|
603
943
|
|
|
944
|
+
**1.3.0 fixed five silent errors.** Numbers are at mpmath's default 15
|
|
945
|
+
digits unless stated.
|
|
946
|
+
|
|
947
|
+
- `Kernel.mass()` integrated a slow tail directly out to infinity. For
|
|
948
|
+
`power_tail(1.1)` the mass was 20.0312 instead of 20.2745 (1.2 % low;
|
|
949
|
+
3.7e-4 at 30 digits), so the threshold on a Lorentzian line of FWHM 1
|
|
950
|
+
was 0.0784174 instead of 0.0774765, and did not match the branch's own
|
|
951
|
+
`eps`. `second_moment()` had the same problem just above `s = 3`
|
|
952
|
+
(19.7910 instead of 20.0343 for `s = 3.1`). The tail is now mapped
|
|
953
|
+
onto a finite interval, as `G(Omega)` already was, and both match
|
|
954
|
+
their closed forms to the working precision.
|
|
955
|
+
- `tail_integral` integrated a singularity `delta^(2-s)` at the center
|
|
956
|
+
directly. On a Lorentzian line of FWHM 1, `I_s` was 47.063 instead
|
|
957
|
+
of 47.715 at `s = 2.9` and 86.97 instead of 98.49 at `s = 2.95`. A
|
|
958
|
+
change of variables now removes the singularity.
|
|
959
|
+
- Through these two, `amplitude_general` was wrong at the edges of
|
|
960
|
+
`1 < s < 3`, where the power `1/(s-1)` amplifies errors: on a
|
|
961
|
+
Gaussian line 8.4219 instead of 9.6179 for `s = 1.1`, and 0.45574
|
|
962
|
+
instead of 0.45258 for `s = 2.9`. Its default tail amplitude was also
|
|
963
|
+
used without a check: for the conservative kernel averaged over
|
|
964
|
+
coupling strengths (`gamma = 3.8`) it was 6.2 % low, and the
|
|
965
|
+
amplitude 8.3 % high. It now refuses such a kernel unless `C` is
|
|
966
|
+
given.
|
|
967
|
+
- `fit_branch` returned the limit of its search as the exponent when
|
|
968
|
+
the best fit lay beyond it: for `R = 0.8 eps^0.04` it reported
|
|
969
|
+
`beta = 0.0500 +- 0.0040`, and for `eps^25`, `beta = 20 +- 1.5`. It
|
|
970
|
+
now refuses.
|
|
971
|
+
- `class_exact` skipped a time earlier than the one before it and
|
|
972
|
+
returned the later state: for times `[1.0, 0.2]` (the system of
|
|
973
|
+
example 9) both entries gave `R = 0.677579`; the right value at
|
|
974
|
+
`t = 0.2` is 0.984316. It now accepts times in any order.
|
|
975
|
+
|
|
976
|
+
All earlier tests pass unchanged, and examples 1 to 9 print the same
|
|
977
|
+
as with 1.2.0.
|
|
978
|
+
|
|
979
|
+
**1.2.0 fixed four silent inaccuracies.** Numbers below are at
|
|
980
|
+
mpmath's default 15 digits unless stated.
|
|
981
|
+
|
|
982
|
+
- `branch_point` formed `eps` as `chiN/chiN_c - 1` and lost about
|
|
983
|
+
`log10(1/eps)` digits: on the Lorentzian line `eps` was 2e-4
|
|
984
|
+
(relative) off at `Omega = 1e-9` and wrong by a factor of about 220 at
|
|
985
|
+
`1e-12`. `G_of_Omega` itself was 4e-10 off at `Omega = 1e-12`,
|
|
986
|
+
because a segment spanning many decades was integrated on a linear
|
|
987
|
+
scale. On the box line with `s = 4`, `extract_beta` quoted 0.3194
|
|
988
|
+
instead of 1/3, and with `s = 6` it raised `ZeroDivisionError`. Both
|
|
989
|
+
quantities now keep the working precision (see the new checks above).
|
|
990
|
+
The earlier tests and examples ran at 20 to 30 digits with `eps` no
|
|
991
|
+
smaller than about 1e-12, where the loss did not show: all of them
|
|
992
|
+
still pass unchanged, and README examples 1, 2, 3, 6 and 9 print the
|
|
993
|
+
same as before.
|
|
994
|
+
- `extract_beta` returned slopes close to 1 (1.0016, 1.00016, 1.000016
|
|
995
|
+
for `Omega` = 1e-3 down to 1e-6, at 20 digits) for the backward
|
|
996
|
+
branch of the two-peaked line (a first-order onset, `eps < 0`), and
|
|
997
|
+
raised `ZeroDivisionError` on a flat branch. It now refuses both.
|
|
998
|
+
- `fit_branch` took its derivatives by finite differences, which near
|
|
999
|
+
the threshold are not small steps: with points down to `eps = 1e-7`
|
|
1000
|
+
it reported `sigma_chi_c` about 17 % and `sigma_beta` about 2 % too
|
|
1001
|
+
small, and on example 4's data the fit stopped marginally short of
|
|
1002
|
+
the least-squares minimum. It now uses exact derivatives. In example 4 the threshold
|
|
1003
|
+
error bar goes from 3.5e-9 to 3.8e-9 (beta 0.67322 -> 0.67325, its
|
|
1004
|
+
error bar 0.00275 -> 0.00279).
|
|
1005
|
+
- `fold_interval`, for a branch that leaves the threshold backwards,
|
|
1006
|
+
returned the first grid point instead of the threshold as the end of
|
|
1007
|
+
the low branch (5.89464 instead of 5.89561 in example 8, and so a
|
|
1008
|
+
jump to 0.84800 instead of 0.84805). A turning point it could not
|
|
1009
|
+
refine was silently replaced by a grid point; it now raises. It is
|
|
1010
|
+
also much faster: on the line of example 8 with `n = 60` and the
|
|
1011
|
+
default range it took 79 s in 1.1.1 and takes 3.5 s now (17 s at the
|
|
1012
|
+
default `n = 400`).
|
|
1013
|
+
|
|
604
1014
|
**1.1.1 fixed the amplitude at ordinary precision.**
|
|
605
1015
|
`c_coefficient` (and so `amplitude`) lost about 40 digits to a
|
|
606
1016
|
cancellation near the centre of the line. At mpmath's default 15
|
|
@@ -626,6 +1036,16 @@ CI did not run Python 3.10. The full history is in
|
|
|
626
1036
|
right when the power law holds and the noise estimate is right.
|
|
627
1037
|
- `extract_beta` converges slowly near `s = 3`, where the exponent
|
|
628
1038
|
carries logarithmic corrections.
|
|
1039
|
+
- The rule `beta = 1/2` for `s >= 3` (and `predicted_beta`,
|
|
1040
|
+
`kernel_tail_from_beta`, `amplitude_curvature`) assumes a line with a
|
|
1041
|
+
rounded top, `p''(0) < 0`. On a line that is flat at the centre, as
|
|
1042
|
+
`box` is, a tail gives `beta = 1/(s-1)` for every `s > 1`, and a
|
|
1043
|
+
kernel without a tail gives a jump. A measured `beta` below 1/2 is
|
|
1044
|
+
refused by `kernel_tail_from_beta`, although a flat-topped line can
|
|
1045
|
+
produce one.
|
|
1046
|
+
- `plan_fit` gives the asymptotic error bars, exact to first order in
|
|
1047
|
+
the noise. They were checked against simulated fits at 1 % scatter;
|
|
1048
|
+
at much larger scatter the real fit can do worse.
|
|
629
1049
|
- In the undamped spin model the truncated cumulant equations become
|
|
630
1050
|
unstable at long times; check `physicality` / `valid_window` before
|
|
631
1051
|
trusting a long run. The time-averaged kernel is an assumption,
|
|
@@ -635,9 +1055,23 @@ CI did not run Python 3.10. The full history is in
|
|
|
635
1055
|
digits at most, however high `mp.dps` is set: the integral leaves
|
|
636
1056
|
out the first 1e-20 line widths next to the centre, a piece of
|
|
637
1057
|
relative size about 1e-20.
|
|
638
|
-
- `fold_interval`
|
|
639
|
-
|
|
640
|
-
|
|
1058
|
+
- `fold_interval` finds a fold only if its grid shows it: structure
|
|
1059
|
+
below `Omega = 10^lo` or narrower than the grid spacing is missed, and
|
|
1060
|
+
with several folds only the first and last turning points are used.
|
|
1061
|
+
It needs a differentiable line. It costs `n` plus about 40
|
|
1062
|
+
high-precision integrals (about 17 s at the default `n = 400` on the
|
|
1063
|
+
two-peaked line).
|
|
1064
|
+
- `fit_branch` propagates coupling errors `sigma_chi` to first order.
|
|
1065
|
+
The tests check this at a tenth of `chi - chi_c`, and hand checks
|
|
1066
|
+
with 1000 seeded fits held to within 6 % up to three tenths. It is
|
|
1067
|
+
refused above a quarter.
|
|
1068
|
+
- `with_calibration` treats calibration as one scale factor for all
|
|
1069
|
+
couplings and one for all `R`. An offset (a zero error) is not a
|
|
1070
|
+
scale: it changes `eps` and so the exponent, and is not covered.
|
|
1071
|
+
- The default tail amplitude of `amplitude_general` is read off the
|
|
1072
|
+
kernel at `|u| = 10^6`; for `power_tail(s)` it is low by a relative
|
|
1073
|
+
`10^(-6 s)`, which the power `1/(s-1)` multiplies (2.5e-6 in `A` for
|
|
1074
|
+
`s = 1.1`). Pass `C` for more digits.
|
|
641
1075
|
- `build_system`'s docstring refers to a convergence check in
|
|
642
1076
|
`scripts/vlasov_check.py`; that script belongs to the research
|
|
643
1077
|
repository and is not part of this package.
|
|
@@ -651,8 +1085,11 @@ concept DOI
|
|
|
651
1085
|
[10.5281/zenodo.22696369](https://doi.org/10.5281/zenodo.22696369)),
|
|
652
1086
|
whose scripts, archived run records and figures remain with the
|
|
653
1087
|
study. The core modules were carried over unchanged in v1.1.0 (1.1.1
|
|
654
|
-
changes only `c_coefficient
|
|
655
|
-
|
|
1088
|
+
changes only `c_coefficient`; 1.2.0 changes how `G`, `eps` and the
|
|
1089
|
+
fold are computed, and adds `amplitude_curvature` and
|
|
1090
|
+
`Kernel.second_moment`; 1.3.0 changes how the kernel mass, the second
|
|
1091
|
+
moment and `tail_integral` are integrated, and how `class_exact` steps
|
|
1092
|
+
through time); the `measured` module and the packaging are new here. Copyright as in [NOTICE](NOTICE).
|
|
656
1093
|
|
|
657
1094
|
## Citing, support and license
|
|
658
1095
|
|