lockkernel 1.2.0__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.
Files changed (27) hide show
  1. {lockkernel-1.2.0/src/lockkernel.egg-info → lockkernel-1.3.0}/PKG-INFO +230 -29
  2. {lockkernel-1.2.0 → lockkernel-1.3.0}/README.md +229 -28
  3. {lockkernel-1.2.0 → lockkernel-1.3.0}/pyproject.toml +1 -1
  4. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/__init__.py +2 -2
  5. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/exact.py +27 -6
  6. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/kernels.py +57 -7
  7. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/lineshapes.py +14 -0
  8. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/measured.py +253 -48
  9. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/parametric.py +59 -7
  10. {lockkernel-1.2.0 → lockkernel-1.3.0/src/lockkernel.egg-info}/PKG-INFO +230 -29
  11. {lockkernel-1.2.0 → lockkernel-1.3.0}/tests/test_cumulant.py +47 -0
  12. {lockkernel-1.2.0 → lockkernel-1.3.0}/tests/test_measured.py +168 -2
  13. {lockkernel-1.2.0 → lockkernel-1.3.0}/tests/test_onset.py +182 -0
  14. {lockkernel-1.2.0 → lockkernel-1.3.0}/LICENSE +0 -0
  15. {lockkernel-1.2.0 → lockkernel-1.3.0}/MANIFEST.in +0 -0
  16. {lockkernel-1.2.0 → lockkernel-1.3.0}/NOTICE +0 -0
  17. {lockkernel-1.2.0 → lockkernel-1.3.0}/setup.cfg +0 -0
  18. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/cumulant.py +0 -0
  19. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel/ensemble.py +0 -0
  20. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel.egg-info/SOURCES.txt +0 -0
  21. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel.egg-info/dependency_links.txt +0 -0
  22. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel.egg-info/requires.txt +0 -0
  23. {lockkernel-1.2.0 → lockkernel-1.3.0}/src/lockkernel.egg-info/top_level.txt +0 -0
  24. {lockkernel-1.2.0 → lockkernel-1.3.0}/tests/conftest.py +0 -0
  25. {lockkernel-1.2.0 → lockkernel-1.3.0}/tests/test_meanfield.py +0 -0
  26. {lockkernel-1.2.0 → lockkernel-1.3.0}/tests/test_parametric.py +0 -0
  27. {lockkernel-1.2.0 → 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.2.0
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 centre (its **tail**). The **lab half** (`lockkernel.measured`)
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. When the data cannot
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
 
@@ -161,8 +164,9 @@ and mpmath 1.2 or newer, and nothing else.
161
164
  ## Examples
162
165
 
163
166
  Each example below runs as written, and the output shown is what it
164
- printed with lockkernel 1.2.0. Line widths, couplings and noise levels
165
- are illustrative values, not taken from any experiment.
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.
166
170
 
167
171
  ### 1. Threshold and exponent from the theory
168
172
 
@@ -525,6 +529,98 @@ should. `xi^2` is the **Wineland spin-squeezing parameter**:
525
529
  1 for uncorrelated spins, below 1 when the spins are entangled in a way
526
530
  that improves phase measurements.
527
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
+
528
624
  ## What is in the package
529
625
 
530
626
  The top level imports the `measured` names below and the submodules
@@ -571,10 +667,18 @@ directly.
571
667
 
572
668
  **Fitting measured data** (`lockkernel.measured`, also at the top level)
573
669
 
574
- - `fit_branch(chi, r, reference, sigma_r=None, min_decade=1.0)` --
575
- returns a `BranchFit` with `chi_c`, `beta`, `amplitude`, their
576
- errors, `n_points`, `eps_range`, `residual_rms_log` (the
577
- root-mean-square misfit in `log R`) and `reference`.
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).
578
682
  - `kernel_tail_from_beta(beta, sigma_beta=0, n_sigma=2)` -- the tail
579
683
  exponent `s` and its error.
580
684
  - `beta_relative_sigma(n_points, decades, sigma_log)`,
@@ -606,7 +710,7 @@ directly.
606
710
  - `symmetric_exact` (all detunings equal, any number of emitters),
607
711
  `full_exact` (full quantum problem, up to about twelve emitters) and
608
712
  `class_exact` (emitters grouped by detuning, a few tens) -- exact
609
- references. `class_exact` is not covered by the tests.
713
+ references. `class_exact` is tested against `full_exact` since 1.3.0.
610
714
 
611
715
  Each function's docstring (`help(lockkernel.fit_branch)`, for example)
612
716
  gives its inputs and conventions.
@@ -619,12 +723,19 @@ gives its inputs and conventions.
619
723
  saying where they come from;
620
724
  - fewer than 6 points are given, or `chi` and `r` differ in length
621
725
  (three parameters with error bars need more);
622
- - the couplings are not finite and strictly increasing, or an order
623
- parameter is not finite and positive (points below the threshold,
624
- `R = 0`, carry no exponent information; drop them);
625
- - `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`;
626
730
  - the fit does not converge, or its covariance is singular (the data
627
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;
628
739
  - the fitted threshold is indistinguishable from the smallest
629
740
  coupling: the data do not reach the onset;
630
741
  - the fitted points span less than `min_decade` decades of `eps`
@@ -634,9 +745,10 @@ gives its inputs and conventions.
634
745
  identified), clearly below 1/2 (no kernel gives that; the fit has
635
746
  probably left the near-threshold range), or not positive;
636
747
  - 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;
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;
640
752
  - `extract_beta` meets a point with `eps <= 0`: the branch is at or
641
753
  below the threshold there, because it bends back (a first-order
642
754
  onset, example 8) or is flat (`R` jumps at the threshold), and there
@@ -647,11 +759,17 @@ gives its inputs and conventions.
647
759
  - `fold_interval` finds a fold on a line with compact support (`box`),
648
760
  which it cannot refine, or cannot bracket a turning point on its
649
761
  grid;
650
- - `power_tail(s)` is asked for `s <= 1` (the kernel mass would be
651
- infinite), or `heterogeneous` for `gamma <= 2` or a resulting tail
652
- `s <= 1`;
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;
653
766
  - `tail_integral` is asked for `s` outside `1 < s < 3`, where it
654
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;
655
773
  - a line shape without a floating-point distribution
656
774
  (`bimodal_gaussian`) is asked for `cdf`, `ppf` or a class table;
657
775
  - the solver of the differential equations fails in `evolve` or
@@ -659,7 +777,7 @@ gives its inputs and conventions.
659
777
 
660
778
  ## How the results are checked
661
779
 
662
- 97 automated tests run on every push and pull request, on Python 3.9
780
+ 128 automated tests run on every push and pull request, on Python 3.9
663
781
  to 3.14, and once more on Python 3.10 with the oldest NumPy (1.22.0),
664
782
  SciPy (1.8.0) and mpmath (1.2.1) the package allows. The numerical
665
783
  checks compare the package with something independent of it: a closed
@@ -720,6 +838,47 @@ forms evaluated separately from the package's integrals)
720
838
  1e-12; at 20 digits it matches `R/sqrt(eps)` at `Omega = 1e-6` to a
721
839
  relative 1e-10 for three kernel-line pairs, and to 2e-6 for `s = 4`.
722
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
+
723
882
  **The exponent rule** (25 digits, Gaussian line, `Omega` down to 1e-6)
724
883
 
725
884
  - `beta = 1/(s-1)` or 1/2 for the kernel family with `s` = 1.5, 1.8,
@@ -778,11 +937,45 @@ forms evaluated separately from the package's integrals)
778
937
  within 5e-3 for couplings `r` = 1.2, 2 and 3 times the threshold,
779
938
  and stays below 0.05 at 0.6 times the threshold.
780
939
 
781
- Not covered by tests: `class_exact`, `physicality` and
782
- `valid_window`.
940
+ Not covered by tests: `physicality` and `valid_window`.
783
941
 
784
942
  ## Corrections in earlier versions
785
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
+
786
979
  **1.2.0 fixed four silent inaccuracies.** Numbers below are at
787
980
  mpmath's default 15 digits unless stated.
788
981
 
@@ -868,10 +1061,17 @@ CI did not run Python 3.10. The full history is in
868
1061
  It needs a differentiable line. It costs `n` plus about 40
869
1062
  high-precision integrals (about 17 s at the default `n = 400` on the
870
1063
  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).
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.
875
1075
  - `build_system`'s docstring refers to a convergence check in
876
1076
  `scripts/vlasov_check.py`; that script belongs to the research
877
1077
  repository and is not part of this package.
@@ -887,8 +1087,9 @@ whose scripts, archived run records and figures remain with the
887
1087
  study. The core modules were carried over unchanged in v1.1.0 (1.1.1
888
1088
  changes only `c_coefficient`; 1.2.0 changes how `G`, `eps` and the
889
1089
  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).
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).
892
1093
 
893
1094
  ## Citing, support and license
894
1095