lockkernel 1.1.1__tar.gz → 1.2.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.2.0}/PKG-INFO +267 -31
- {lockkernel-1.1.1 → lockkernel-1.2.0}/README.md +266 -30
- {lockkernel-1.1.1 → lockkernel-1.2.0}/pyproject.toml +1 -1
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel/__init__.py +5 -4
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel/kernels.py +25 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel/measured.py +162 -10
- lockkernel-1.2.0/src/lockkernel/parametric.py +461 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0/src/lockkernel.egg-info}/PKG-INFO +267 -31
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel.egg-info/SOURCES.txt +1 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/tests/conftest.py +1 -0
- lockkernel-1.2.0/tests/test_measured.py +249 -0
- lockkernel-1.2.0/tests/test_onset.py +276 -0
- 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.2.0}/LICENSE +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/MANIFEST.in +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/NOTICE +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/setup.cfg +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel/cumulant.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel/ensemble.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel/exact.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel/lineshapes.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel.egg-info/dependency_links.txt +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel.egg-info/requires.txt +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/src/lockkernel.egg-info/top_level.txt +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/tests/test_cumulant.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/tests/test_meanfield.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.0}/tests/test_parametric.py +0 -0
- {lockkernel-1.1.1 → lockkernel-1.2.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.2.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
|
|
@@ -119,7 +119,12 @@ why, rather than returning a number that looks fine but is not.
|
|
|
119
119
|
`beta = 1/(s-1)` for `1 < s < 3` and `beta = 1/2` for `s >= 3` or
|
|
120
120
|
for a kernel with no algebraic tail. So a measured `beta` above 1/2
|
|
121
121
|
names the tail, `s = 1 + 1/beta`, while `beta = 1/2` only says
|
|
122
|
-
"`s >= 3`" and cannot name one value.
|
|
122
|
+
"`s >= 3`" and cannot name one value. The value 1/2 needs a line
|
|
123
|
+
with a rounded top at its centre (`p''(0) < 0`, true for every
|
|
124
|
+
shipped line except `box`). On a line that is flat at the centre,
|
|
125
|
+
such as `box`, a tail gives `beta = 1/(s-1)` for every `s > 1`
|
|
126
|
+
(so below 1/2 once `s > 3`), and a kernel with no tail gives a jump
|
|
127
|
+
instead of a power law (see [Limits](#limits)).
|
|
123
128
|
- **Order of the onset** -- for the conservative kernel, the sign of
|
|
124
129
|
one number `c` (an integral over the line shape) decides it:
|
|
125
130
|
`c > 0` gives a smooth (continuous) onset, `c < 0` a jump with
|
|
@@ -144,7 +149,10 @@ and mpmath 1.2 or newer, and nothing else.
|
|
|
144
149
|
- **Precision.** The theory half (`parametric`, `kernels`,
|
|
145
150
|
`lineshapes`) works in mpmath's arbitrary precision and returns
|
|
146
151
|
mpmath numbers. Set the working precision with `mpmath.mp.dps`
|
|
147
|
-
(decimal digits). The
|
|
152
|
+
(decimal digits). The reduced coupling `eps` keeps that precision
|
|
153
|
+
however close to the threshold it is (since 1.2.0), so the working
|
|
154
|
+
precision does not need to exceed the number of decades you step
|
|
155
|
+
towards the onset. The lab half and the dynamics work in ordinary
|
|
148
156
|
floating point with NumPy.
|
|
149
157
|
- **Line shapes** are normalised probability densities; `lorentzian`
|
|
150
158
|
and `gaussian` take a full width at half maximum, `box` a half
|
|
@@ -153,7 +161,7 @@ and mpmath 1.2 or newer, and nothing else.
|
|
|
153
161
|
## Examples
|
|
154
162
|
|
|
155
163
|
Each example below runs as written, and the output shown is what it
|
|
156
|
-
printed with lockkernel 1.
|
|
164
|
+
printed with lockkernel 1.2.0. Line widths, couplings and noise levels
|
|
157
165
|
are illustrative values, not taken from any experiment.
|
|
158
166
|
|
|
159
167
|
### 1. Threshold and exponent from the theory
|
|
@@ -253,9 +261,12 @@ For conservative spins the exponent is 1, so near the onset
|
|
|
253
261
|
`R = A eps`, with `A = pi p(0)^2 / c`. This example runs at mpmath's
|
|
254
262
|
default 15 digits; before version 1.1.1 `amplitude` and
|
|
255
263
|
`c_coefficient` needed about 30 digits to be right (see
|
|
256
|
-
[Corrections](#corrections-in-earlier-versions)).
|
|
257
|
-
|
|
258
|
-
|
|
264
|
+
[Corrections](#corrections-in-earlier-versions)). For two unit-width
|
|
265
|
+
Gaussian peaks at `+-a`, `c = (1 - 2x D(x))/pi` with `x = a/sqrt(2)`
|
|
266
|
+
and `D` Dawson's function, and the tests hold `c_coefficient` to this
|
|
267
|
+
formula. So the onset turns first order when the peaks are more than
|
|
268
|
+
2.61386 widths apart, where Dawson's function has its maximum.
|
|
269
|
+
Example 8 follows the first-order case through its hysteresis loop.
|
|
259
270
|
|
|
260
271
|
### 4. Fit a measured branch
|
|
261
272
|
|
|
@@ -289,7 +300,7 @@ print(f"kernel tail s = {s:.2f} +- {s_err:.2f} (true 2.5)")
|
|
|
289
300
|
```
|
|
290
301
|
|
|
291
302
|
```
|
|
292
|
-
threshold chi_c = 0.59441 +- 3.
|
|
303
|
+
threshold chi_c = 0.59441 +- 3.8e-09 (exact 0.59441)
|
|
293
304
|
exponent beta = 0.673 +- 0.003 (exact 0.667)
|
|
294
305
|
amplitude A = 0.652 +- 0.019
|
|
295
306
|
eps range 1.56e-07 .. 1.09e-03, rms log residual 0.011
|
|
@@ -302,7 +313,9 @@ couplings and order parameters, and a `reference` that says where
|
|
|
302
313
|
they come from (it is required). The error bars are the standard
|
|
303
314
|
asymptotic ones of a least-squares fit: they are right when the power
|
|
304
315
|
law holds over the fitted range and `sigma_r` is right. Without
|
|
305
|
-
`sigma_r`, the scatter of the points sets them.
|
|
316
|
+
`sigma_r`, the scatter of the points sets them. Since 1.2.0 they are
|
|
317
|
+
built from exact derivatives; 1.1.1 printed `3.5e-09` for the
|
|
318
|
+
threshold here (see [Corrections](#corrections-in-earlier-versions)). Here the fitted
|
|
306
319
|
exponent is 2.4 of its own error bars from the exact 2/3; the tests
|
|
307
320
|
allow four (see [How the results are checked](#how-the-results-are-checked)).
|
|
308
321
|
`kernel_tail_from_beta` turns the exponent into the kernel's tail
|
|
@@ -311,7 +324,8 @@ exponent `s = 1 + 1/beta`, with error `sigma_beta / beta^2`.
|
|
|
311
324
|
### 5. Plan the measurement, and a refusal
|
|
312
325
|
|
|
313
326
|
```python
|
|
314
|
-
from lockkernel import beta_relative_sigma, points_for_beta, kernel_tail_from_beta
|
|
327
|
+
from lockkernel import (beta_relative_sigma, points_for_beta, kernel_tail_from_beta,
|
|
328
|
+
plan_fit, points_for_fit)
|
|
315
329
|
|
|
316
330
|
# How many points, spread evenly in log(eps) over 2 decades, with 5 %
|
|
317
331
|
# scatter in R, for an error bar of 0.01 on beta (threshold known)?
|
|
@@ -319,6 +333,15 @@ n, achieved = points_for_beta(0.01, decades=2.0, sigma_log=0.05)
|
|
|
319
333
|
print(f"points needed: {n} (error bar {achieved:.5f}); "
|
|
320
334
|
f"with {n - 1}: {beta_relative_sigma(n - 1, 2.0, 0.05):.5f}")
|
|
321
335
|
|
|
336
|
+
# The same range (eps = 1e-4 .. 1e-2), but with the threshold fitted too,
|
|
337
|
+
# as fit_branch does it:
|
|
338
|
+
plan = plan_fit(n, 1e-4, 1e-2, beta=2/3, sigma_log=0.05)
|
|
339
|
+
print(f"{n} points, threshold fitted: beta +- {plan.sigma_beta:.4f}, "
|
|
340
|
+
f"chi_c +- {plan.sigma_chi_c_rel:.1e} (relative)")
|
|
341
|
+
n_fit, plan = points_for_fit(0.01, 1e-4, 1e-2, beta=2/3, sigma_log=0.05)
|
|
342
|
+
print(f"points needed with the threshold fitted: {n_fit} "
|
|
343
|
+
f"(error bar {plan.sigma_beta:.5f})")
|
|
344
|
+
|
|
322
345
|
# A fitted beta = 0.52 +- 0.02 cannot name a kernel tail:
|
|
323
346
|
try:
|
|
324
347
|
kernel_tail_from_beta(0.52, 0.02)
|
|
@@ -328,15 +351,28 @@ except ValueError as err:
|
|
|
328
351
|
|
|
329
352
|
```
|
|
330
353
|
points needed: 12 (error bar 0.00999); with 11: 0.01035
|
|
354
|
+
12 points, threshold fitted: beta +- 0.0197, chi_c +- 1.4e-05 (relative)
|
|
355
|
+
points needed with the threshold fitted: 57 (error bar 0.00995)
|
|
331
356
|
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
357
|
```
|
|
333
358
|
|
|
334
359
|
`beta_relative_sigma` gives the error bar of a straight-line slope on a
|
|
335
360
|
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
|
-
|
|
361
|
+
`beta`, not a relative one. It assumes the threshold is known, and
|
|
362
|
+
`points_for_beta` finds the smallest number of points that meets the
|
|
363
|
+
target on that assumption.
|
|
364
|
+
|
|
365
|
+
`fit_branch` fits the threshold as well, and that costs precision:
|
|
366
|
+
the same 12 points give an error bar about twice as large. `plan_fit`
|
|
367
|
+
gives the error bars `fit_branch` will report (for `beta`, and
|
|
368
|
+
relative ones for `chi_c` and `A`), before any data are taken, from
|
|
369
|
+
the planned range of `eps` and the expected `beta`. `points_for_fit`
|
|
370
|
+
turns that into a number of points: 57 instead of 12 here. How much
|
|
371
|
+
fitting the threshold costs depends on how many decades the points
|
|
372
|
+
span: for 12 points the error bar grows about 2.0 times over two
|
|
373
|
+
decades, 1.6 over three, and 1.3 over five. These are the standard
|
|
374
|
+
asymptotic error bars, held in the tests against 300 seeded simulated
|
|
375
|
+
fits (within 12 %).
|
|
340
376
|
|
|
341
377
|
### 6. A spread of coupling strengths changes the exponent
|
|
342
378
|
|
|
@@ -369,7 +405,86 @@ oscillator's own kernel has none: `s = min(s0, (gamma-2)/eta)`, with
|
|
|
369
405
|
`s0` the tail of the single-oscillator kernel (infinite for Kuramoto).
|
|
370
406
|
Here `s = 1.8`, so `beta = 1/(s-1) = 1.25`, which is `1/(gamma-3)`.
|
|
371
407
|
|
|
372
|
-
### 7. The
|
|
408
|
+
### 7. The amplitude when beta = 1/2
|
|
409
|
+
|
|
410
|
+
```python
|
|
411
|
+
import mpmath as mp
|
|
412
|
+
from lockkernel.lineshapes import lorentzian, gaussian
|
|
413
|
+
from lockkernel.kernels import kuramoto, gaussian_kernel
|
|
414
|
+
from lockkernel.parametric import amplitude_curvature, branch_point
|
|
415
|
+
|
|
416
|
+
mp.mp.dps = 20
|
|
417
|
+
for line, kern in [(lorentzian(1.0), kuramoto()), (gaussian(1.0), kuramoto()),
|
|
418
|
+
(gaussian(1.0), gaussian_kernel())]:
|
|
419
|
+
A = amplitude_curvature(line, kern)
|
|
420
|
+
_, R, eps = branch_point(line, kern, mp.mpf("1e-6"))
|
|
421
|
+
print(f"{kern.name:9s} kernel, {line.name:10s} line: A = {mp.nstr(A, 12)}, "
|
|
422
|
+
f"R/sqrt(eps) at Omega = 1e-6: {mp.nstr(R / mp.sqrt(eps), 12)}")
|
|
423
|
+
print("sqrt(pi) =", mp.nstr(mp.sqrt(mp.pi), 12))
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
kuramoto kernel, lorentzian line: A = 1.0, R/sqrt(eps) at Omega = 1e-6: 0.999999999999
|
|
428
|
+
kuramoto kernel, gaussian line: A = 1.77245385091, R/sqrt(eps) at Omega = 1e-6: 1.7724538509
|
|
429
|
+
gaussian kernel, gaussian line: A = 1.41421356237, R/sqrt(eps) at Omega = 1e-6: 1.41421356237
|
|
430
|
+
sqrt(pi) = 1.77245385091
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
For a kernel with no tail, or a tail `s > 3`, the onset is
|
|
434
|
+
`R = A eps^(1/2)`. The first correction to the self-consistency then
|
|
435
|
+
comes from the curvature of the line at its centre, `p''(0)`, and
|
|
436
|
+
`amplitude_curvature` returns
|
|
437
|
+
`A = G0^(3/2) sqrt(2 / (-p''(0) M2))`, where `G0 = p(0) m` and
|
|
438
|
+
`M2 = integral u^2 W(u) du` is the kernel's second moment
|
|
439
|
+
(`Kernel.second_moment()`). For the Kuramoto kernel on a Lorentzian
|
|
440
|
+
line this is 1, the closed form `R = sqrt(1 - chiN_c/chiN)`. On a
|
|
441
|
+
Gaussian line the width cancels, which leaves `sqrt(pi)` (Kuramoto
|
|
442
|
+
kernel) and `sqrt(2)` (Gaussian kernel) at any width. The branch
|
|
443
|
+
approaches these values with corrections of relative size
|
|
444
|
+
`Omega^2`, or `Omega^(s-3)` for a tail `3 < s < 5`.
|
|
445
|
+
|
|
446
|
+
### 8. A first-order onset: the hysteresis loop
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
import mpmath as mp
|
|
450
|
+
from lockkernel.lineshapes import bimodal_gaussian
|
|
451
|
+
from lockkernel.kernels import conservative
|
|
452
|
+
from lockkernel.parametric import fold_interval, threshold, extract_beta
|
|
453
|
+
|
|
454
|
+
line = bimodal_gaussian(4.0) # two Gaussian peaks (width 1) at -2 and +2
|
|
455
|
+
f = fold_interval(line, n=30, lo=-2, hi=1)
|
|
456
|
+
print("threshold :", mp.nstr(threshold(line, conservative()), 10))
|
|
457
|
+
print("hysteresis from chiN =", mp.nstr(f["chiN_lo"], 10), "to", mp.nstr(f["chiN_hi"], 10))
|
|
458
|
+
print("R jumps from 0 to :", mp.nstr(f["R_jump"], 10))
|
|
459
|
+
print("R where the high branch ends:", mp.nstr(f["R_high_at_lo"], 10))
|
|
460
|
+
try:
|
|
461
|
+
extract_beta(line, conservative(), [-3, -4, -5])
|
|
462
|
+
except ValueError as err:
|
|
463
|
+
print("refused:", str(err)[:72], "...")
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
```
|
|
467
|
+
threshold : 5.89561378
|
|
468
|
+
hysteresis from chiN = 3.493404803 to 5.89561378
|
|
469
|
+
R jumps from 0 to : 0.8480505911
|
|
470
|
+
R where the high branch ends: 0.3342759883
|
|
471
|
+
refused: eps = -0.0016464 <= 0 at Omega = 10^-3: the branch is at or below the th ...
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
With the peaks 4 widths apart `c < 0` (example 3). The branch leaves
|
|
475
|
+
the threshold backwards, then turns round at `chiN = 3.4934`. In the
|
|
476
|
+
usual reading of such a fold (the package computes where the
|
|
477
|
+
solutions are, not whether they are stable), raising the coupling
|
|
478
|
+
keeps the unsynchronized state up to the threshold, where `R` jumps to
|
|
479
|
+
0.848; lowering it again keeps the synchronized state down to
|
|
480
|
+
`chiN = 3.4934` (where `R = 0.334`) before it collapses. `fold_interval` samples the branch on `n` points
|
|
481
|
+
between `Omega = 10^lo` and `10^hi` and then finds the turning points
|
|
482
|
+
and the jump exactly. The tests check every number it returns against
|
|
483
|
+
an independent closed form (the Voigt profile). There is no exponent
|
|
484
|
+
on such a branch, and `extract_beta` says so instead of returning
|
|
485
|
+
one.
|
|
486
|
+
|
|
487
|
+
### 9. The quantum spin model against exact diagonalisation
|
|
373
488
|
|
|
374
489
|
```python
|
|
375
490
|
import numpy as np
|
|
@@ -424,12 +539,14 @@ directly.
|
|
|
424
539
|
Makers: `lorentzian`, `gaussian`, `student_t`, `box`,
|
|
425
540
|
`bimodal_gaussian`; `LINESHAPES` maps names to them.
|
|
426
541
|
- `Kernel` -- an even kernel with `W(0) = 1`, its tail exponent, its
|
|
427
|
-
support,
|
|
542
|
+
support, `mass()` and `second_moment()` (the integral of
|
|
543
|
+
`u^2 W(u)`, finite only for compact support, fast decay or `s > 3`).
|
|
544
|
+
Makers: `conservative`, `kuramoto`,
|
|
428
545
|
`power_tail(s)`, `gaussian_kernel`; `KERNELS` maps names to them.
|
|
429
546
|
- `heterogeneous(base, degree_exponent, eta=1, k_min=1)` -- the kernel
|
|
430
547
|
averaged over a power-law spread of coupling strengths (example 6).
|
|
431
548
|
- `predicted_beta(s)` -- the exponent the rule gives for tail `s`
|
|
432
|
-
(`None` meaning no algebraic tail).
|
|
549
|
+
(`None` meaning no algebraic tail), for a line with a rounded top.
|
|
433
550
|
|
|
434
551
|
**The exact solution** (`lockkernel.parametric`, mpmath precision)
|
|
435
552
|
|
|
@@ -439,14 +556,18 @@ directly.
|
|
|
439
556
|
- `threshold`, `branch_point`, `sweep`, `extract_beta` -- the
|
|
440
557
|
threshold, one point `(chiN, R, eps)` of the branch, the branch at
|
|
441
558
|
`Omega = 10^e` for a list of `e`, and the local exponents along it.
|
|
559
|
+
`eps` is computed from `G(0) - G(Omega)` directly, so it keeps the
|
|
560
|
+
working precision however small it is.
|
|
442
561
|
- `c_coefficient`, `amplitude` -- for the conservative kernel, the
|
|
443
562
|
number `c` whose sign sets the order of the onset, and the amplitude
|
|
444
563
|
`A = pi p(0)^2 / c`.
|
|
445
564
|
- `tail_integral`, `amplitude_general` -- the amplitude for a kernel
|
|
446
565
|
with tail exponent `1 < s < 3`.
|
|
566
|
+
- `amplitude_curvature` -- the amplitude when `beta = 1/2` (no tail,
|
|
567
|
+
or a tail `s > 3`, on a line with a rounded top; example 7).
|
|
447
568
|
- `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
|
|
569
|
+
the coupling range of the hysteresis, the turning points and the
|
|
570
|
+
jump in `R` (`None` if the branch does not fold; example 8).
|
|
450
571
|
|
|
451
572
|
**Fitting measured data** (`lockkernel.measured`, also at the top level)
|
|
452
573
|
|
|
@@ -458,8 +579,14 @@ directly.
|
|
|
458
579
|
exponent `s` and its error.
|
|
459
580
|
- `beta_relative_sigma(n_points, decades, sigma_log)`,
|
|
460
581
|
`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.
|
|
582
|
+
error bar of `beta` for a planned measurement with the threshold
|
|
583
|
+
known, and the number of points for a target error bar.
|
|
584
|
+
- `plan_fit(n_points, eps_min, eps_max, beta, sigma_log,
|
|
585
|
+
fit_threshold=True)`, `points_for_fit(target_sigma_beta, eps_min,
|
|
586
|
+
eps_max, beta, sigma_log)` -- the same for the fit `fit_branch`
|
|
587
|
+
actually does, with the threshold fitted: `plan_fit` returns a
|
|
588
|
+
`FitPlan` with `sigma_beta`, `sigma_chi_c_rel` and
|
|
589
|
+
`sigma_amplitude_rel` (example 5).
|
|
463
590
|
|
|
464
591
|
**Quantum spin dynamics** (`lockkernel.cumulant`, `lockkernel.ensemble`,
|
|
465
592
|
`lockkernel.exact`)
|
|
@@ -506,8 +633,20 @@ gives its inputs and conventions.
|
|
|
506
633
|
- a fitted `beta` is consistent with 1/2 (only the class `s >= 3` is
|
|
507
634
|
identified), clearly below 1/2 (no kernel gives that; the fit has
|
|
508
635
|
probably left the near-threshold range), or not positive;
|
|
509
|
-
- a planned measurement has fewer than 3 points
|
|
510
|
-
|
|
636
|
+
- a planned measurement has fewer than 3 points (6 with the threshold
|
|
637
|
+
fitted, as `fit_branch` needs), a non-positive or reversed range, a
|
|
638
|
+
non-positive scatter or `beta`, a non-positive target, or would need
|
|
639
|
+
more than 10^7 points;
|
|
640
|
+
- `extract_beta` meets a point with `eps <= 0`: the branch is at or
|
|
641
|
+
below the threshold there, because it bends back (a first-order
|
|
642
|
+
onset, example 8) or is flat (`R` jumps at the threshold), and there
|
|
643
|
+
is no exponent to measure;
|
|
644
|
+
- `amplitude_curvature` is asked for a line with `p''(0) >= 0` (a flat
|
|
645
|
+
top or a dip at the centre) or a kernel with tail `s <= 3`, and
|
|
646
|
+
`second_moment` for a kernel with tail `s <= 3` (it diverges);
|
|
647
|
+
- `fold_interval` finds a fold on a line with compact support (`box`),
|
|
648
|
+
which it cannot refine, or cannot bracket a turning point on its
|
|
649
|
+
grid;
|
|
511
650
|
- `power_tail(s)` is asked for `s <= 1` (the kernel mass would be
|
|
512
651
|
infinite), or `heterogeneous` for `gamma <= 2` or a resulting tail
|
|
513
652
|
`s <= 1`;
|
|
@@ -520,7 +659,7 @@ gives its inputs and conventions.
|
|
|
520
659
|
|
|
521
660
|
## How the results are checked
|
|
522
661
|
|
|
523
|
-
|
|
662
|
+
97 automated tests run on every push and pull request, on Python 3.9
|
|
524
663
|
to 3.14, and once more on Python 3.10 with the oldest NumPy (1.22.0),
|
|
525
664
|
SciPy (1.8.0) and mpmath (1.2.1) the package allows. The numerical
|
|
526
665
|
checks compare the package with something independent of it: a closed
|
|
@@ -549,6 +688,38 @@ the package does not import matplotlib. The main checks:
|
|
|
549
688
|
`Omega` = 1e-6 and 2e-6 to a relative 1e-9, on four lines; `R/eps` at `Omega = 1e-6` is
|
|
550
689
|
within a relative 1e-5 of the amplitude.
|
|
551
690
|
|
|
691
|
+
**New in 1.2.0** (15 digits unless stated; the references are closed
|
|
692
|
+
forms evaluated separately from the package's integrals)
|
|
693
|
+
|
|
694
|
+
- `eps`, `chiN` and `R` on the Lorentzian line match `eps = Omega/a`,
|
|
695
|
+
`chiN = a + Omega`, `R = Omega/(a + Omega)` to a relative 1e-13 for
|
|
696
|
+
`Omega` from 1e-6 to 1e-14, and to 1e-28 at 30 digits down to
|
|
697
|
+
`Omega` = 1e-20.
|
|
698
|
+
- Box line, kernels `1/(1+|u|^s)` with `s` = 1.5, 2.5, 4 and 6: `G`
|
|
699
|
+
and `eps` match the closed form `G = X 2F1(1, 1/s; 1+1/s; -X^s)`,
|
|
700
|
+
`X = 1/Omega`, to a relative 1e-13 at `Omega` = 1e-1, 1e-3 and 1e-5
|
|
701
|
+
(down to `eps` of about 1e-26). On this flat-topped line
|
|
702
|
+
`extract_beta` gives `1/(s-1)` = 1/3 and 1/5 for `s` = 4 and 6, within
|
|
703
|
+
1e-6.
|
|
704
|
+
- Two-peaked line: `c_coefficient` matches `(1 - 2x D(x))/pi` to 1e-13
|
|
705
|
+
for separations 1, 2 and 4; the sign of `c` flips across the
|
|
706
|
+
separation 2.61386 (the maximum of Dawson's function, located by root
|
|
707
|
+
finding); `G` matches the Voigt closed form to a relative 1e-13.
|
|
708
|
+
- `fold_interval`: every number it returns (couplings, order
|
|
709
|
+
parameters, `Omega` of the turning points and of the jump) matches
|
|
710
|
+
the Voigt closed form, with turning points found by root finding on
|
|
711
|
+
its derivative, to a relative 1e-12, for a branch that leaves the
|
|
712
|
+
threshold backwards (two peaks 4 widths apart) and for an S-shaped
|
|
713
|
+
branch (a three-peak line); it returns `None` for a monotonic branch.
|
|
714
|
+
- `extract_beta` refuses the backward branch and the flat one (box
|
|
715
|
+
line, Kuramoto kernel, where `eps` is exactly 0).
|
|
716
|
+
- `second_moment` matches `pi/8` (Kuramoto), `sqrt(pi)/2` (Gaussian
|
|
717
|
+
kernel) and `2 (pi/s)/sin(3 pi/s)` (`s` = 4, 6) to 1e-12.
|
|
718
|
+
`amplitude_curvature` is 1 for the Kuramoto kernel on a Lorentzian
|
|
719
|
+
line and `sqrt(pi)`, `sqrt(2)` on Gaussian lines of two widths, to
|
|
720
|
+
1e-12; at 20 digits it matches `R/sqrt(eps)` at `Omega = 1e-6` to a
|
|
721
|
+
relative 1e-10 for three kernel-line pairs, and to 2e-6 for `s = 4`.
|
|
722
|
+
|
|
552
723
|
**The exponent rule** (25 digits, Gaussian line, `Omega` down to 1e-6)
|
|
553
724
|
|
|
554
725
|
- `beta = 1/(s-1)` or 1/2 for the kernel family with `s` = 1.5, 1.8,
|
|
@@ -577,6 +748,17 @@ the package does not import matplotlib. The main checks:
|
|
|
577
748
|
- The planning formula matches 6000 seeded simulated fits within 5 %,
|
|
578
749
|
and `points_for_beta` returns the smallest `n` that meets the target
|
|
579
750
|
(checked on both sides).
|
|
751
|
+
- (New in 1.2.0) On noiseless data reaching `eps = 1e-7`, the error
|
|
752
|
+
bars `fit_branch` reports, and those `plan_fit` predicts, equal the
|
|
753
|
+
ones built from mpmath's numerical derivatives at 30 digits to a
|
|
754
|
+
relative 1e-6. With the threshold known, `plan_fit` equals
|
|
755
|
+
`beta_relative_sigma` to 1e-12. Over 300 seeded noisy fits (1 %
|
|
756
|
+
scatter), the scatter of `chi_c`, `beta` and `A` matches `plan_fit`,
|
|
757
|
+
and the median reported error bar matches the scatter, within 12 %.
|
|
758
|
+
`points_for_fit` is checked on both sides, and the error bar is
|
|
759
|
+
checked to fall with every added point from 6 to 400. `sigma_beta`
|
|
760
|
+
and `sigma_A` from `plan_fit` do not depend on `beta` and
|
|
761
|
+
`sigma_chi_c` scales as `1/beta` (to 1e-12).
|
|
580
762
|
|
|
581
763
|
**Dynamics and discretisation**
|
|
582
764
|
|
|
@@ -596,11 +778,46 @@ the package does not import matplotlib. The main checks:
|
|
|
596
778
|
within 5e-3 for couplings `r` = 1.2, 2 and 3 times the threshold,
|
|
597
779
|
and stays below 0.05 at 0.6 times the threshold.
|
|
598
780
|
|
|
599
|
-
Not covered by tests: `
|
|
600
|
-
`
|
|
781
|
+
Not covered by tests: `class_exact`, `physicality` and
|
|
782
|
+
`valid_window`.
|
|
601
783
|
|
|
602
784
|
## Corrections in earlier versions
|
|
603
785
|
|
|
786
|
+
**1.2.0 fixed four silent inaccuracies.** Numbers below are at
|
|
787
|
+
mpmath's default 15 digits unless stated.
|
|
788
|
+
|
|
789
|
+
- `branch_point` formed `eps` as `chiN/chiN_c - 1` and lost about
|
|
790
|
+
`log10(1/eps)` digits: on the Lorentzian line `eps` was 2e-4
|
|
791
|
+
(relative) off at `Omega = 1e-9` and wrong by a factor of about 220 at
|
|
792
|
+
`1e-12`. `G_of_Omega` itself was 4e-10 off at `Omega = 1e-12`,
|
|
793
|
+
because a segment spanning many decades was integrated on a linear
|
|
794
|
+
scale. On the box line with `s = 4`, `extract_beta` quoted 0.3194
|
|
795
|
+
instead of 1/3, and with `s = 6` it raised `ZeroDivisionError`. Both
|
|
796
|
+
quantities now keep the working precision (see the new checks above).
|
|
797
|
+
The earlier tests and examples ran at 20 to 30 digits with `eps` no
|
|
798
|
+
smaller than about 1e-12, where the loss did not show: all of them
|
|
799
|
+
still pass unchanged, and README examples 1, 2, 3, 6 and 9 print the
|
|
800
|
+
same as before.
|
|
801
|
+
- `extract_beta` returned slopes close to 1 (1.0016, 1.00016, 1.000016
|
|
802
|
+
for `Omega` = 1e-3 down to 1e-6, at 20 digits) for the backward
|
|
803
|
+
branch of the two-peaked line (a first-order onset, `eps < 0`), and
|
|
804
|
+
raised `ZeroDivisionError` on a flat branch. It now refuses both.
|
|
805
|
+
- `fit_branch` took its derivatives by finite differences, which near
|
|
806
|
+
the threshold are not small steps: with points down to `eps = 1e-7`
|
|
807
|
+
it reported `sigma_chi_c` about 17 % and `sigma_beta` about 2 % too
|
|
808
|
+
small, and on example 4's data the fit stopped marginally short of
|
|
809
|
+
the least-squares minimum. It now uses exact derivatives. In example 4 the threshold
|
|
810
|
+
error bar goes from 3.5e-9 to 3.8e-9 (beta 0.67322 -> 0.67325, its
|
|
811
|
+
error bar 0.00275 -> 0.00279).
|
|
812
|
+
- `fold_interval`, for a branch that leaves the threshold backwards,
|
|
813
|
+
returned the first grid point instead of the threshold as the end of
|
|
814
|
+
the low branch (5.89464 instead of 5.89561 in example 8, and so a
|
|
815
|
+
jump to 0.84800 instead of 0.84805). A turning point it could not
|
|
816
|
+
refine was silently replaced by a grid point; it now raises. It is
|
|
817
|
+
also much faster: on the line of example 8 with `n = 60` and the
|
|
818
|
+
default range it took 79 s in 1.1.1 and takes 3.5 s now (17 s at the
|
|
819
|
+
default `n = 400`).
|
|
820
|
+
|
|
604
821
|
**1.1.1 fixed the amplitude at ordinary precision.**
|
|
605
822
|
`c_coefficient` (and so `amplitude`) lost about 40 digits to a
|
|
606
823
|
cancellation near the centre of the line. At mpmath's default 15
|
|
@@ -626,6 +843,16 @@ CI did not run Python 3.10. The full history is in
|
|
|
626
843
|
right when the power law holds and the noise estimate is right.
|
|
627
844
|
- `extract_beta` converges slowly near `s = 3`, where the exponent
|
|
628
845
|
carries logarithmic corrections.
|
|
846
|
+
- The rule `beta = 1/2` for `s >= 3` (and `predicted_beta`,
|
|
847
|
+
`kernel_tail_from_beta`, `amplitude_curvature`) assumes a line with a
|
|
848
|
+
rounded top, `p''(0) < 0`. On a line that is flat at the centre, as
|
|
849
|
+
`box` is, a tail gives `beta = 1/(s-1)` for every `s > 1`, and a
|
|
850
|
+
kernel without a tail gives a jump. A measured `beta` below 1/2 is
|
|
851
|
+
refused by `kernel_tail_from_beta`, although a flat-topped line can
|
|
852
|
+
produce one.
|
|
853
|
+
- `plan_fit` gives the asymptotic error bars, exact to first order in
|
|
854
|
+
the noise. They were checked against simulated fits at 1 % scatter;
|
|
855
|
+
at much larger scatter the real fit can do worse.
|
|
629
856
|
- In the undamped spin model the truncated cumulant equations become
|
|
630
857
|
unstable at long times; check `physicality` / `valid_window` before
|
|
631
858
|
trusting a long run. The time-averaged kernel is an assumption,
|
|
@@ -635,9 +862,16 @@ CI did not run Python 3.10. The full history is in
|
|
|
635
862
|
digits at most, however high `mp.dps` is set: the integral leaves
|
|
636
863
|
out the first 1e-20 line widths next to the centre, a piece of
|
|
637
864
|
relative size about 1e-20.
|
|
638
|
-
- `fold_interval`
|
|
639
|
-
|
|
640
|
-
|
|
865
|
+
- `fold_interval` finds a fold only if its grid shows it: structure
|
|
866
|
+
below `Omega = 10^lo` or narrower than the grid spacing is missed, and
|
|
867
|
+
with several folds only the first and last turning points are used.
|
|
868
|
+
It needs a differentiable line. It costs `n` plus about 40
|
|
869
|
+
high-precision integrals (about 17 s at the default `n = 400` on the
|
|
870
|
+
two-peaked line).
|
|
871
|
+
- `Kernel.mass()` integrates slow tails numerically: for
|
|
872
|
+
`power_tail(1.5)` it is about 2e-10 (relative) off at 15 digits and
|
|
873
|
+
5e-18 at 30. This enters `threshold` (not `eps`, whose integral
|
|
874
|
+
treats the tail separately).
|
|
641
875
|
- `build_system`'s docstring refers to a convergence check in
|
|
642
876
|
`scripts/vlasov_check.py`; that script belongs to the research
|
|
643
877
|
repository and is not part of this package.
|
|
@@ -651,8 +885,10 @@ concept DOI
|
|
|
651
885
|
[10.5281/zenodo.22696369](https://doi.org/10.5281/zenodo.22696369)),
|
|
652
886
|
whose scripts, archived run records and figures remain with the
|
|
653
887
|
study. The core modules were carried over unchanged in v1.1.0 (1.1.1
|
|
654
|
-
changes only `c_coefficient
|
|
655
|
-
|
|
888
|
+
changes only `c_coefficient`; 1.2.0 changes how `G`, `eps` and the
|
|
889
|
+
fold are computed, and adds `amplitude_curvature` and
|
|
890
|
+
`Kernel.second_moment`); the `measured` module and the packaging are
|
|
891
|
+
new here. Copyright as in [NOTICE](NOTICE).
|
|
656
892
|
|
|
657
893
|
## Citing, support and license
|
|
658
894
|
|