rigorous-levels 0.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 (76) hide show
  1. rigorous_levels-0.3.0/CHANGELOG.md +445 -0
  2. rigorous_levels-0.3.0/CONTRIBUTING.md +40 -0
  3. rigorous_levels-0.3.0/FILE_LIST.tsv +85 -0
  4. rigorous_levels-0.3.0/LICENSE +202 -0
  5. rigorous_levels-0.3.0/MANIFEST.in +8 -0
  6. rigorous_levels-0.3.0/NOTICE +10 -0
  7. rigorous_levels-0.3.0/PKG-INFO +414 -0
  8. rigorous_levels-0.3.0/README.md +388 -0
  9. rigorous_levels-0.3.0/SECURITY.md +16 -0
  10. rigorous_levels-0.3.0/bench/check_200.py +90 -0
  11. rigorous_levels-0.3.0/bench/compare_tools.py +83 -0
  12. rigorous_levels-0.3.0/bench/duffing_adapter_sphere.py +103 -0
  13. rigorous_levels-0.3.0/bench/fluxonium_devices.py +62 -0
  14. rigorous_levels-0.3.0/bench/gregory2016.py +74 -0
  15. rigorous_levels-0.3.0/bench/mathieu_vs_scipy.py +83 -0
  16. rigorous_levels-0.3.0/bench/matrix_elements.py +73 -0
  17. rigorous_levels-0.3.0/bench/multiport_batch.py +60 -0
  18. rigorous_levels-0.3.0/bench/parameter_balls.py +79 -0
  19. rigorous_levels-0.3.0/bench/readme_numbers.py +145 -0
  20. rigorous_levels-0.3.0/bench/real_transmons.py +71 -0
  21. rigorous_levels-0.3.0/check_tree.py +358 -0
  22. rigorous_levels-0.3.0/docs/PORT_CONTRACT.md +186 -0
  23. rigorous_levels-0.3.0/docs/PROOF.md +230 -0
  24. rigorous_levels-0.3.0/docs/THRESHOLD_RULE.md +24 -0
  25. rigorous_levels-0.3.0/pyproject.toml +34 -0
  26. rigorous_levels-0.3.0/requirements/build.txt +17 -0
  27. rigorous_levels-0.3.0/requirements/ci.txt +16 -0
  28. rigorous_levels-0.3.0/setup.cfg +4 -0
  29. rigorous_levels-0.3.0/src/rigorous_levels/__init__.py +41 -0
  30. rigorous_levels-0.3.0/src/rigorous_levels/_compat.py +59 -0
  31. rigorous_levels-0.3.0/src/rigorous_levels/_deterministic.py +382 -0
  32. rigorous_levels-0.3.0/src/rigorous_levels/duffing_vs_transmon.py +134 -0
  33. rigorous_levels-0.3.0/src/rigorous_levels/fluxonium.py +273 -0
  34. rigorous_levels-0.3.0/src/rigorous_levels/guaranteed_intervals.py +455 -0
  35. rigorous_levels-0.3.0/src/rigorous_levels/hyperfine_molecule.py +646 -0
  36. rigorous_levels-0.3.0/src/rigorous_levels/intervallo_garantito.py +23 -0
  37. rigorous_levels-0.3.0/src/rigorous_levels/isolation.py +1020 -0
  38. rigorous_levels-0.3.0/src/rigorous_levels/mathieu.py +216 -0
  39. rigorous_levels-0.3.0/src/rigorous_levels/matrix_elements.py +201 -0
  40. rigorous_levels-0.3.0/src/rigorous_levels/molecola_iperfine_garantita.py +24 -0
  41. rigorous_levels-0.3.0/src/rigorous_levels/multiport.py +496 -0
  42. rigorous_levels-0.3.0/src/rigorous_levels/multiporta.py +27 -0
  43. rigorous_levels-0.3.0/src/rigorous_levels/parameter_balls.py +170 -0
  44. rigorous_levels-0.3.0/src/rigorous_levels/port_runner.py +501 -0
  45. rigorous_levels-0.3.0/src/rigorous_levels/reproduce.py +307 -0
  46. rigorous_levels-0.3.0/src/rigorous_levels/rotor_spectrum.py +218 -0
  47. rigorous_levels-0.3.0/src/rigorous_levels/rotore_sferico_garantito.py +22 -0
  48. rigorous_levels-0.3.0/src/rigorous_levels/rotore_spettro.py +19 -0
  49. rigorous_levels-0.3.0/src/rigorous_levels/scqubits_adapter.py +305 -0
  50. rigorous_levels-0.3.0/src/rigorous_levels/scqubits_adattatore.py +35 -0
  51. rigorous_levels-0.3.0/src/rigorous_levels/spherical_rotor.py +247 -0
  52. rigorous_levels-0.3.0/src/rigorous_levels.egg-info/PKG-INFO +414 -0
  53. rigorous_levels-0.3.0/src/rigorous_levels.egg-info/SOURCES.txt +74 -0
  54. rigorous_levels-0.3.0/src/rigorous_levels.egg-info/dependency_links.txt +1 -0
  55. rigorous_levels-0.3.0/src/rigorous_levels.egg-info/requires.txt +17 -0
  56. rigorous_levels-0.3.0/src/rigorous_levels.egg-info/top_level.txt +1 -0
  57. rigorous_levels-0.3.0/tests/test_cos4phi_origin.py +63 -0
  58. rigorous_levels-0.3.0/tests/test_deprecated_aliases.py +93 -0
  59. rigorous_levels-0.3.0/tests/test_determinism.py +349 -0
  60. rigorous_levels-0.3.0/tests/test_duffing_vs_transmon.py +150 -0
  61. rigorous_levels-0.3.0/tests/test_examples.py +78 -0
  62. rigorous_levels-0.3.0/tests/test_fluxonium.py +183 -0
  63. rigorous_levels-0.3.0/tests/test_generative_inputs.py +194 -0
  64. rigorous_levels-0.3.0/tests/test_guaranteed_intervals.py +409 -0
  65. rigorous_levels-0.3.0/tests/test_hyperfine_molecule.py +473 -0
  66. rigorous_levels-0.3.0/tests/test_inputs_review.py +50 -0
  67. rigorous_levels-0.3.0/tests/test_mathieu.py +184 -0
  68. rigorous_levels-0.3.0/tests/test_matrix_elements.py +117 -0
  69. rigorous_levels-0.3.0/tests/test_multiport.py +1314 -0
  70. rigorous_levels-0.3.0/tests/test_parameter_balls.py +175 -0
  71. rigorous_levels-0.3.0/tests/test_port_centrex_tlf.py +94 -0
  72. rigorous_levels-0.3.0/tests/test_readme_example.py +44 -0
  73. rigorous_levels-0.3.0/tests/test_rotor_spectrum.py +122 -0
  74. rigorous_levels-0.3.0/tests/test_scqubits_adapter.py +249 -0
  75. rigorous_levels-0.3.0/tests/test_spec_bounds.py +110 -0
  76. rigorous_levels-0.3.0/tests/test_spherical_rotor.py +312 -0
@@ -0,0 +1,445 @@
1
+ # Changelog
2
+
3
+ All dates are from the author's system clock (Europe/Rome). Releases: 0.3.0 on 2026-11-12 (GitHub release with attested wheel and sdist; not on PyPI). 0.1.0 and 0.2.0 were never
4
+ released.
5
+
6
+ ## 0.3.0 — 2026-11-12 (first release; developed on branch dev-0.3 from 7beff99 since 2026-11-11)
7
+
8
+ ### Name (2026-11-12)
9
+
10
+ The package was developed privately under the working name `qraft-ra` (never released). Before the first release it
11
+ is renamed `rigorous-levels`, a name that says what it computes: distribution `rigorous-levels`, module
12
+ `rigorous_levels`, environment variables `QRAFT_*` → `RIGOROUS_LEVELS_*` (`RIGOROUS_LEVELS_TRUSTED_ENGINES`,
13
+ `RIGOROUS_LEVELS_HOST_LIMITS`, …; `QRAFT_BERSAGLI_VENV` → `RIGOROUS_LEVELS_TARGETS_VENV`), port contract
14
+ `qraft-port/0.3` → `rigorous-levels-port/0.3`, AppContainer profile `rigorous-levels-multiport`. No alias for the old
15
+ names: nothing was published under them. The entries below keep the names they had when they were written.
16
+
17
+ Four limits of 0.2.0 were named by the author on 2026-11-11 and are closed or narrowed here (measurements in
18
+ README "Measurements" and in the author's report; every number re-measured on this tree, dates and versions stated).
19
+
20
+ ### A. Tests that did not see what the CI saw
21
+
22
+ 0.2.0 shipped two defects found only by GitHub's CI (user namespaces denied by AppArmor; a nesting limit left to
23
+ RecursionError, which depends on the stack size on Python 3.14). The class is closed, not the two cases:
24
+
25
+ - `bench/matrix.sh`: the whole suite against the installed package on every venv given, with the stack at its
26
+ default, unlimited and 1 MiB, with the isolation engines present, with `bwrap` absent from PATH and with a fake
27
+ `bwrap` that fails; every run in a fresh temporary HOME; one summary line per run plus the list of skips with
28
+ reasons.
29
+ - CI: Linux x86-64 (3.10–3.14, stack default/unlimited/1 MiB, bwrap + podman + docker engines, each one's S1 test
30
+ required to run), Linux ARM64 (3.12, 3.14; bwrap + docker), macOS (3.12, 3.14; everything that is not a sandbox
31
+ plus the `sandbox-exec` engine, state of that tool measured on the runner), Windows (3.12, 3.14; everything that is
32
+ not a sandbox plus the `appcontainer` engine), free-threaded 3.14t (pure-math modules, dependencies installed only
33
+ from wheels). The jobs run when the branch is pushed: not yet.
34
+ - `tests/test_generative_inputs.py`: seeded random attacks on every public entry point (unknown keyword, wrong type,
35
+ NaN/inf, bool, integers beyond float64, unicode, nested containers, numpy scalars) must give exactly the declared
36
+ refusal; a valid input of the same family must pass (positive control); no public function accepts `**kwargs`.
37
+ Defects it found in this tree before the fix (all fixed): `Rotor`, `spectrum`, `guaranteed_levels`,
38
+ `mathieu_value` crashed with OverflowError on an integer beyond float64 instead of refusing it; `spectrum`
39
+ accepted a non-integer `nmax_limit`; `SphericalRotor` accepted any |M| and `guaranteed_sphere_levels` any
40
+ `L_limit`; `guaranteed_levels` accepted any `N_limit`; `mathieu_a/b` took `**kw`. Caps are now declared constants
41
+ (`N_LIMIT`, `L_LIMIT`, `K_LIMIT`, `N_LIMIT` of the fluxonium, `NMAX_LIMIT`).
42
+ - Audit of the dependence on interpreter limits (recursion, float conversion of big ints, locale, dict order,
43
+ encoding): no recursion in the parsers (the JSON depth is counted linearly), every float conversion of an input
44
+ goes through a guard, `json.dumps` with `sort_keys` where a canonical form is needed, every file opened with an
45
+ explicit encoding; `resource`, `socket`, `os.fork`, `os.kill` are optional in the runner (Windows, WASI).
46
+
47
+ ### B. English API
48
+
49
+ Modules, classes, functions, keyword arguments, fields, states and error messages are English. Old → new:
50
+ `intervallo_garantito` → `guaranteed_intervals` (`livelli_garantiti` → `guaranteed_levels`, `Intervalli` →
51
+ `Intervals` with `low/high/strings/max_width/state/seconds`, `GARANTITO/LARGO` → `GUARANTEED/WIDE`,
52
+ `FuoriDominio/ArbAssente/TempoScaduto` → `OutOfDomain/ArbMissing/DeadlineExceeded`); `rotore_spettro` →
53
+ `rotor_spectrum` (`Rotore` → `Rotor` with `cosines/sines`, `spettro` → `spectrum`, `Spettro` → `Spectrum` with
54
+ `energies/error/floor/state`, `NON_CONVERGENTE` → `NOT_CONVERGED`, `IngressoRifiutato` → `InputRejected`);
55
+ `scqubits_adattatore` → `scqubits_adapter` (`spettro_scqubits` → `scqubits_spectrum` with `check=` in
56
+ `none/doubling/auto`, `unit=`, `engine=` in `process/sandbox`, states `UNCHECKED`, `OK_FLOAT_FLOOR`, origins
57
+ `absolute/relative/user/floor`, rule id `scq-threshold/1.0`); `rotore_sferico_garantito` → `spherical_rotor`
58
+ (`RotoreSferico` → `SphericalRotor`, `livelli_sfera_garantiti` → `guaranteed_sphere_levels`, `N_fis` → `N_phys`);
59
+ `molecola_iperfine_garantita` → `hyperfine_molecule` (`MolecolaSigma` → `SigmaMolecule` with `two_I1/two_I2`,
60
+ `livelli_molecola_garantiti` → `guaranteed_molecule_levels` with `two_MF`, `dimensione_settore` →
61
+ `sector_dimension`); `multiporta` → `multiport` (`Richiesta/Uscita/Verdetto` → `Request/Output/Verdict`,
62
+ `esegui_porta/confronta/valida_uscita/leggi_uscita` → `run_port/compare/validate_output/read_output`, verdicts
63
+ `INSIDE/INSIDE_TOLERANCE/OUTSIDE/NOT_COMPARABLE`, output states `OK/ERROR/INVALID`); `porte_runner` → `port_runner`;
64
+ `duffing_vs_transmon` (`errore_duffing` → `duffing_error`, variants `spectrum/koch`, `ErroreDuffing` → `DuffingError`).
65
+ The Italian modules remain as deprecated aliases (`qraft_ra._compat`): importing them or using any old name emits a
66
+ DeprecationWarning that names the replacement; keyword arguments are translated; the fields of the results have
67
+ Italian read-only aliases; `tests/test_deprecated_aliases.py` measures that the old names work and that the
68
+ English API is warning-free under `-W error::DeprecationWarning`.
69
+ Formats on disk changed with a new version declared first: the port contract is "qraft-port/0.2" (then
70
+ "qraft-port/0.3" with the batches of §8, the one accepted by 0.3.0)
71
+ (`docs/PORT_CONTRACT.md`, English keys, port names `numpy_rotor`, `test_*`, model types `cos_rotor`,
72
+ `sigma_molecule`; "qraft-porta/0.1" is refused); the threshold rule is "scq-threshold/1.0"
73
+ (`docs/THRESHOLD_RULE.md`, same content as "soglia-scq/1.0").
74
+ Not translated: test method names and test docstrings (prose, not API), kept from 0.2.0.
75
+
76
+ ### C. Isolation beyond Linux (`qraft_ra.isolation`, the same contract for every engine)
77
+
78
+ - Engines, closed list with a declared order of strength (`ENGINE_ORDER`, docs/PORT_CONTRACT.md §6.0): `podman`,
79
+ `docker` (OCI container: `--network none --read-only --cap-drop ALL --security-opt no-new-privileges`, non-root
80
+ user, tmpfs /tmp, site-packages and work folder read-only, image taken only from the local store, kill on timeout
81
+ from the host), `bwrap` (as in 0.2.0), `sandbox-exec` (macOS, deny-default Seatbelt profile: no network, reads
82
+ everything except the user's homes, writes only the output file), `appcontainer` (Windows: AppContainer process
83
+ through `CreateAppContainerProfile` / `PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES`, read grants to the
84
+ interpreter and site-packages for the container SID, Job Object limits on memory, process count and CPU time),
85
+ `wasm` (wasmtime + a CPython built for WASI: no sockets, only pre-opened folders, interpreter folder required
86
+ read-only, `-W max-memory-size`, `-W timeout`).
87
+ - `run_port(request, engine="auto")` picks the first engine of the order whose fail-closed PROBE passes (engine
88
+ executable present; containers: image present locally and, inside, only the `lo` interface, uid ≠ 0, read-only
89
+ root; sandbox-exec/appcontainer/wasm: network closed, root not writable, home not visible); the engine used is
90
+ named in `Output.sandbox`. Nothing degrades silently: no engine → `SandboxMissing` with every reason.
91
+ - Tests: the S1–S7 controls run once per engine available (`TestSandbox_<engine>` classes), skipped with the
92
+ probe's reason otherwise; CI requires the S1 test of the expected engines to have run.
93
+ - Measured locally (Linux x86-64, 2026-11-11): bwrap 0.8.0 and podman 4.3.1 rootless pass S1–S7 (podman on cgroups
94
+ v1 ignores `--memory`/`--pids-limit` with a warning: not relied upon, the runner's rlimits apply); wasmtime 50.0.1
95
+ + CPython 3.15.2 WASI passes the controls that do not need numpy and has NO numpy, so no solver port can run there
96
+ (declared; `test_s3_memoria`, `s6`, `s7`, scqubits tests skipped with that reason). NOT measured locally:
97
+ docker, sandbox-exec, appcontainer, ARM64 (CI jobs written; they run after the push).
98
+ - Runner: `resource`, `socket`, `os.fork`, `os.kill` optional; `QRAFT_OUTPUT_DIR` for engines that cannot bind a
99
+ single file into a read-only folder; `ok_then_crash` ends with `os._exit(137)` when SIGKILL is ignored (PID 1).
100
+
101
+ ### D. Wider field with guaranteed intervals
102
+
103
+ - `parameter_balls`: E_C and the cosine coefficients as intervals (`RotorBall`, `guaranteed_levels_over_box`): the
104
+ level over the whole box from two guaranteed computations (E_C ↦ λ_j non-decreasing; coefficients ↦ λ_j Lipschitz
105
+ with constant Σ|Δc_m|, Weyl), docs/PROOF.md. Measured 2026-11-11 (`bench/parameter_balls.py`): 200 transmons of
106
+ the device regime with E_C, E_J ± 1 %, 6 levels, 5 inner points each: 0 of 1000 inner point intervals outside the
107
+ box interval; a point with E_J 3 % above the box detected outside in 187 of 200; box width 2.1× the observed
108
+ spread (median; max 5.5×: the Lipschitz bound is rigorous, not tight); 0.11 s per box. n_g as an interval: see below.
109
+ - `scqubits_adapter.guaranteed_from_scqubits`: from a `scqubits.Transmon` or `Fluxonium` object, the guaranteed
110
+ intervals of its H and the verdict (`INSIDE` / `INSIDE_TOLERANCE` / `OUTSIDE` per level, tolerance 32·eps·‖H‖) on
111
+ its own `eigenvals()` at its own cutoff; closed list of classes, read by name and attributes.
112
+ - The public API is exposed at the package level (`from qraft_ra import Rotor, guaranteed_levels, ...`, `__all__`).
113
+ - `examples/` (five scripts run by `tests/test_examples.py` in a fresh interpreter), `CITATION.cff`,
114
+ `bench/compare_tools.py` (time per point against scqubits, scipy, numpy: the other tools are 37–340× faster on
115
+ the inputs of the README table).
116
+
117
+ - `mathieu`: Mathieu characteristic values a_r(q), b_r(q) (r ≤ 200, any finite q, negative and large included) as
118
+ guaranteed intervals on the four Fourier sectors (Jacobi matrices with (0,1) = √2·q, d_0 = 1 ± q), same proof as
119
+ the rotor (Rayleigh–Ritz above, per-pair Young plus Gershgorin on the outer block below, Sylvester counts in Arb).
120
+ Measured 2026-11-11: r = 0…12, q = −400…400 step 0.5 (40 025 values), median 0.38 ms per value, 40 019
121
+ GUARANTEED at 1e-12 relative and 6 WIDE (valid, wider); scipy 1.17.1 and 1.18.1 give 52 values outside the
122
+ intervals (identical lists; 30 of them equal another order ≤ 12; all 52 confirmed wrong by an independent
123
+ tridiagonal reference at K = 400, which agrees with the intervals in 52 of 52); scipy 2.0.0.dev0+git20261108
124
+ gives 0. scipy is about 100× faster (3.6 µs median) and gives no guarantee.
125
+ - `fluxonium`: H = 4E_C n² + (E_L/2)φ² − E_J cos(φ − 2π·flux) in the oscillator basis with the truncation closed by
126
+ a per-site lower bound (Young on every coupling pair, DLMF 18.14.8 tails on the Laguerre couplings) and dense LDLᵀ
127
+ counts in Arb. Measured 2026-11-11 against scqubits 4.3.1 `Fluxonium`: cutoff 110 inside the intervals in every
128
+ level of 4 published parameter sets at flux ½ (`bench/fluxonium_devices.py`) and of 2 of them at flux 0, ¼, ½
129
+ (`tests/test_fluxonium.py`), cutoff 6 outside in at least one (negative control); width
130
+ 5e-10 GHz, 0.4–6.5 s per device (N = 54…117). Published devices (parameters as printed, one-mode model): Zhang et
131
+ al. 2021 f01 = 13.8816 MHz guaranteed vs 14 MHz measured; Moskalenko et al. 2022 qubit A f01 = 685.731 MHz vs
132
+ 688.224, f12 = 1831.204 MHz vs 1832 (shifts reported, not judged: two-mode fit, printed rounding).
133
+ - `matrix_elements`: guaranteed enclosures of ⟨v_i|n|v_j⟩, ⟨v_i|cos mφ|v_j⟩, ⟨v_i|sin mφ|v_j⟩ for the charge-basis
134
+ rotor: the float eigenvectors of H_N are approximations whose distance from the true eigenvectors is bounded by
135
+ the exact residual against the FULL operator over the gap given by the guaranteed enclosures of the neighbouring
136
+ levels (spectral theorem), with ‖n v_j‖ bounded through ⟨(n − n_g)²⟩ ≤ (λ_j + ‖V‖)/4E_C (docs/PROOF.md). Measured
137
+ 2026-11-11 (`bench/matrix_elements.py`, `tests/test_matrix_elements.py`): see README "Measurements".
138
+ - `parameter_balls`: n_g as an interval too (band functions of Hill's equation monotone on [0, ½]: the extremes
139
+ are at the ends and at the band edges 0, ½ inside the interval; Reed–Simon XIII.89, Eastham 1973 Thm 2.3.1,
140
+ DLMF 28.12(i)); checked against a fine sampling of n_g at E_J/E_C = 5 and a point outside (tests).
141
+ - Multiport: `python_rotor` (the reference tridiagonal in pure Python, Sturm bisection) and pure-Python control
142
+ ports, so that every engine, WebAssembly included, runs the full S1–S7 (measured: 16 of 17 tests of
143
+ `TestSandbox_wasm` run, the only skip is the scqubits/diatomic-py solver test); `python_rotor` agrees with
144
+ numpy's `eigvalsh` within 1e-11 GHz.
145
+ - `python -m qraft_ra.reproduce`: re-measures the numerical claims of the README offline (one [PASS]/[FAIL] line per
146
+ claim with the measured and the published value; fast set 22 s, `--full` adds the 40 025-value Mathieu grid and the
147
+ 200 wide-range transmons, 108 s); `--sabotage` makes the Mathieu solver return the next order of the sector as a positive control (at least one
148
+ FAIL; the first version swapped a_r with b_r, which left r = 0 and every odd r unchanged: 0 FAIL in the fast set);
149
+ `--readme` checks that every published value appears verbatim in README.md and is run by `check_tree.py` (a number
150
+ edited by hand fails the tree check: positive control included); `--hash` prints the sha256 of a fixed set of
151
+ exact enclosures: identical on Python 3.11 (numpy 2.4.6, scipy 1.17.1), 3.12, 3.13 and 3.14 (numpy 2.5.4, scipy
152
+ 1.18.1) on Linux x86-64 (c3ae60dd…); the CI jobs print it on every platform.
153
+ - Benches inside the tree for every row of the README tables: `bench/readme_numbers.py`, `mathieu_vs_scipy.py` (also
154
+ the fine grid of qiskit-metal #1252), `gregory2016.py` (the 16 RbCs lines), `real_transmons.py` (the 6 transmons of
155
+ arXiv:2407.17407), `duffing_adapter_sphere.py`,
156
+ `fluxonium_devices.py`, `parameter_balls.py`, `matrix_elements.py`, `check_200.py`, `multiport_batch.py`,
157
+ `compare_tools.py`. Re-measured here with the absolute criterion (error > 1e-6 GHz): scqubits at ncut 30 wrong in 70
158
+ of the 200 wide-range transmons (smallest E_J/E_C 5542), all 70 flagged by the adapter, 0 false alarms, 0 missed
159
+ (the 65 at ≥ 7744 of the README of 0.2.0 use the relative criterion of `readme_numbers.py`: both stated).
160
+ - `check(scqubits_obj, tolerance=…)` (= `guaranteed_from_scqubits`) and `assert_inside(levels, intervals, tolerance)`
161
+ for other people's regression tests (`examples/regression_assert.py`: fails on scipy's sparse `eigsh` from 1.15.0
162
+ on, which omits the exact zero eigenvalue of 4E_C n², and passes on the dense solver).
163
+ - `tests/test_cos4phi_origin.py`: H = h2·n² + c2·cos 4φ of the author's earlier work with a fixed cut Nb = 40: inside
164
+ for three parameter sets, OFF by 1.4e-11 for (h2, c2) = (0.25, 60) (first cut inside between 41 and 60); Nb = 2
165
+ outside for all (positive control).
166
+ - Multiport batches (`run_ports`, contract "qraft-port/0.3" §8): 100 requests in one sandbox process: 0.012 s
167
+ (numpy_rotor) and 0.023 s (python_rotor) per case against 1.0–1.1 s per single run (podman), outputs equal to the
168
+ single runs; `site_packages_of("scqubits")` finds the folder to mount (no placeholder in the examples).
169
+ - Tests are discoverable (`python -m unittest discover -s tests`); CITATION.cff; examples/ run by the tests.
170
+ - Not done: nothing of A–D is deferred. The deprecated Italian aliases stay (cost: one module and one test).
171
+
172
+ ### Other
173
+
174
+ - `guaranteed_intervals.Intervals.contains(j, value)` (was `valore`); `rotor_spectrum.spectrum` checks that its
175
+ first argument is a `Rotor`.
176
+ - `check_tree.py`: unchanged rules; `FILE_LIST.tsv` lists the new files.
177
+
178
+ ### Fixed before release (adversarial review of 0.3.0, 2026-11-12)
179
+
180
+ Each code fix has a test that fails on the tree before it and passes after it, except the Windows branch of the
181
+ root refusal (its test runs in the Windows CI job only); the `--sabotage` change is run by the Linux CI job
182
+ (`python -m qraft_ra.reproduce --sabotage`); the CI and text items have no test.
183
+
184
+ - Isolation: a user-owned `podman` placed first on PATH was chosen by `engine="auto"` and ran the port on the host
185
+ (network reached) while the result named a container: the probe is measured from inside the engine, so a binary
186
+ the user can replace can forge it. Every engine binary on POSIX must now be owned by root and not writable by
187
+ group or others, as `bwrap` already had to be; a user-installed binary is used only if its exact real path is
188
+ listed in `QRAFT_TRUSTED_ENGINES`. `sandbox-exec` and `appcontainer` refuse a readable or granted folder that is a
189
+ user's home, contains one, or is a root (any drive or share). Not covered (declared in README and docs/PORT_CONTRACT.md): a container
190
+ runtime set in the user's own podman/docker configuration, the binary owner on Windows, the AppContainer grants
191
+ that stay on the folders after the run.
192
+ - Isolation, second pass: a `QRAFT_TRUSTED_ENGINES` entry vouches only if it is an absolute real path (a relative
193
+ entry was resolved from the current folder); the engine is executed by its real path (a symlink on PATH retargeted
194
+ after the cached probe ran an unlisted binary on the host, network reached, `Output.sandbox` naming wasm); a
195
+ root-owned binary in a folder that is not root-owned or is writable by group or others is refused (a hard link
196
+ with `fs.protected_hardlinks = 0`); a vouched binary is named in `Output.sandbox`.
197
+ - Fluxonium: E_C = 1e17 or 2^63 made the oscillator basis grow without a cap (host out of memory); refused when
198
+ √(2E_C/E_L) exceeds `N_LIMIT`.
199
+ - Parameter boxes: an n_g beyond 2^53 made the candidate loop endless; the candidates are now computed in exact
200
+ arithmetic. The Lipschitz radius used the exact half-width around a float midpoint that can be rounded (one end
201
+ uncovered by up to 8.9e-16 in 49 914 of 100 000 random boxes; no value was observed outside, the computation
202
+ margins were larger): the radius is now the larger exact distance from the float midpoint to either end.
203
+ - Extreme inputs (1e308, 5e-324, q ≥ 1e155) in `guaranteed_levels`, `mathieu_a/b`, `Fluxonium` raised
204
+ OverflowError; they are now refused with `OutOfDomain` and the reason. `assert_inside` with 10**400 now fails with
205
+ its AssertionError and the reason instead of OverflowError; with a unit that is not a string ([1], {}) it refuses
206
+ with `OutOfDomain` instead of TypeError.
207
+ - `reproduce --sabotage` gave 0 FAIL in the fast set (swapping a_r with b_r leaves r = 0 and every odd r unchanged);
208
+ it now makes the Mathieu solver return the next order of the sector: 2 FAIL.
209
+ - CI: the `unittest discover` step could not fail (`> /dev/null && echo OK`); it now exits with the suite's status.
210
+ - Text: the port contract is "qraft-port/0.3" (README and the `multiport` docstring said 0.2); the number of example
211
+ scripts (five) and the fluxonium sets and flux points were corrected.
212
+
213
+ ### Fixed after the first CI run on macOS, Windows and ARM (2026-11-12)
214
+
215
+ The first run of the branch on GitHub (run 43152525407): 11 of 16 jobs green; Linux ARM64 and free-threaded 3.14t
216
+ green; 5 red. Product defects, each with a test that fails on the tree before it:
217
+
218
+ - `read_output` followed a symlink `output.json` on Windows, where `O_NOFOLLOW` does not exist: the link (and a
219
+ reparse point) is now refused from `lstat`, and the opened file must be the one `lstat` saw.
220
+ - docker: the runner's RLIMIT_NPROC of 16 counted every process of the host user (rootful docker, host user
221
+ namespace): "can't start new thread" in the scqubits port on Linux 3.10. docker now caps the container with
222
+ `--pids-limit 16` (the same cap of 16, now counted only inside the container) and the runner leaves RLIMIT_NPROC
223
+ to podman and bwrap.
224
+ - Deadlines and elapsed times use `time.perf_counter()`: on Windows with Python 3.12 a deadline of 1e-9 s did not
225
+ raise DeadlineExceeded in `mathieu_a` and `guaranteed_sphere_levels` with the clocks used before (`time.time`,
226
+ `time.monotonic`), whose resolution there is coarse; Windows with Python 3.14 passed.
227
+
228
+ - macOS (python.org framework build, macOS 26): the interpreter in bin/ is a stub that starts
229
+ `Resources/Python.app/Contents/MacOS/Python` with posix_spawn, which the Seatbelt profile did not allow: the
230
+ sandbox-exec probe failed. That binary is now allowed too (only when it exists next to the interpreter).
231
+ - Windows: CreateProcessW in the AppContainer failed with error 203 (ERROR_ENVVAR_NOT_FOUND) on the GitHub runner;
232
+ the environment block is now sorted by name, the error names the variables passed (names only), and the block
233
+ carries the system variables a process needs (SYSTEMDRIVE, WINDIR, COMSPEC, PATHEXT) and LOCALAPPDATA/APPDATA
234
+ pointed at the scratch folder. Sorting alone did not fix it (CI run 43156458790); with the system variables the
235
+ error 203 is gone and the AppContainer probe passes (CI run 43157816967).
236
+ - macOS: the sandbox-exec probe asked `os.path.isdir(home)`, which needs only file metadata, allowed everywhere by
237
+ the profile: a false "does not isolate". It now tries to list the home (file-read-data, denied there).
238
+ - CI: the check that each engine's S1 test ran matched only the test name format of Python 3.11+; on 3.10 it failed
239
+ although the test had passed. It now matches both formats and still refuses a skipped test.
240
+
241
+ After run 43157816967 (12 of 16 jobs green; macOS and Windows red in the S1–S7 tests of their engine):
242
+
243
+ - macOS refuses RLIMIT_AS ("ValueError: current limit exceeds maximum limit"), and the runner set all its rlimits in
244
+ one block: every sandbox-exec run, and the runner outside the sandbox, ended in that error. Each rlimit is now set
245
+ on its own, never above a lower hard limit already in force; a refused limit stops the run with the limit named
246
+ unless the engine enforces it from the host (`QRAFT_HOST_LIMITS`, closed list). sandbox-exec enforces memory from
247
+ the host: a watchdog samples the resident memory of the process group every 20 ms (libproc) and checks the peak
248
+ after the exit (wait4); over `mem_mib` the run is killed and the output is ERROR "memory: … (host watchdog)".
249
+ - sandbox-exec and appcontainer have no tmpfs, and Windows has no file-size rlimit: the same watchdog caps one file at
250
+ 64 MiB and the writable places at 256 MiB in all, the limits of the other engines (sampled, declared in
251
+ docs/PORT_CONTRACT.md §6); the whole process group (macOS) or job (Windows) is killed at the end of a run.
252
+ - Windows: the output file was granted `M`, which includes DELETE: the port removed it and `run_port` raised
253
+ FileNotFoundError. The grant is now `(R,W)`, and a missing output file is INVALID "no output", never an exception.
254
+ sandbox-exec allowed `file-write*` on the output file (unlink and create too): now `file-write-data` only.
255
+ - Windows: the AppContainer cannot open the NUL device (scqubits → dill: "Permission denied: 'nul'"); the runner points
256
+ `os.devnull` at an empty file in the scratch folder, only when NUL cannot be opened.
257
+ - The `test_write` port reported the real home "visible" from `os.path.isdir`, which Seatbelt allows everywhere
258
+ (metadata); it now lists the folder, as the probe does.
259
+
260
+ Test fixes for these platforms: the runner outside the sandbox (positive controls) gets the Windows system variables
261
+ (WinError 10106 without SYSTEMROOT) and `QRAFT_HOST_LIMITS=memory`; the engine-missing controls make appcontainer
262
+ missing through its Windows API, as it has no executable; the peak-memory check of the stdout test reads
263
+ PeakWorkingSetSize on Windows and bytes (not KiB) on macOS; the disk controls use the runner's `$WORK` and `$TMP` on
264
+ the engines that run on host paths.
265
+
266
+ Measured by the CI (2026-11-12): macOS refuses RLIMIT_AS, RLIMIT_DATA and RLIMIT_RSS alike ("current limit exceeds
267
+ maximum limit"; RLIMIT_FSIZE, RLIMIT_CPU and RLIMIT_NPROC are accepted): the host watchdog is the only memory cap
268
+ there. With these fixes both macOS jobs are green and every sandbox-exec S1–S7 test runs (run 43161023860; the
269
+ scqubits test is skipped there, scqubits is not installed on macOS). Inside the AppContainer `open('nul')` fails in
270
+ `rb`, `wb` and `r+b` (PermissionError), the AppContainer folder is found, and TEMP/TMP/LOCALAPPDATA are rewritten
271
+ by Windows to folders that do not exist (run 43163743554). Then, from run 43163743554:
272
+
273
+ - Windows, Python 3.12: the 2 s CPU control ended at the 7 s wall-clock timeout: Windows checks the Job's CPU-time
274
+ limit only periodically. The host watchdog now reads the Job's user time and stops the run over `seconds`.
275
+ - Windows: `python -m qraft_ra.reproduce` stopped with UnicodeEncodeError printing "≤" to the cp1252 console; a
276
+ character the console cannot encode is now printed as a backslash escape.
277
+
278
+ Earlier, after run 43162320603 (Windows: `$TMP` gave FileNotFoundError inside the AppContainer), the test ports'
279
+ `$TMP` placeholder follows `tempfile.gettempdir()` (where a solver's temporary files go) and the disk error names the
280
+ folder; the Seatbelt-profile test built POSIX paths that `realpath` changes on Windows (a test defect).
281
+
282
+ Result: run 43165166338 on commit d93eda1, 16 of 16 jobs green; every S1–S7 test of sandbox-exec (macOS 3.12, 3.14)
283
+ and appcontainer (Windows 3.12, 3.14) ran and passed (one skip each for a solver not installed there). The CI also
284
+ showed that the determinism hash differs between machines (README, "Determinism"); not investigated in this entry.
285
+
286
+ Test and CI fixes: tests that need `pwd`, a `sh` fake interpreter or `SIGALRM` are POSIX-only or use a thread with a
287
+ time limit; the examples get a fresh `USERPROFILE` on Windows (qutip needs a home); scqubits 4.3.1 is not installed
288
+ on macOS, where it requires scipy ≤ 1.13.1 (its scqubits tests are skipped there with the reason).
289
+
290
+ ### Determinism of the published ends (2026-11-12)
291
+
292
+ Defect, measured on 2026-11-12 (commit f9867d8, Python 3.11.2–3.14.8, Linux x86-64 with AVX2): `reproduce --hash`
293
+ gave c3ae60dd… with `OPENBLAS_CORETYPE` Haswell or unset, adfa1368… with Prescott and b99bed48… with Nehalem; in CI
294
+ the Linux x86-64 runners alternated between two values and ARM64, macOS and Windows each gave another. Cause: the
295
+ float estimates that choose where the exact counts are taken came from numpy/scipy (`eigvalsh`, `eig_banded`,
296
+ `eigvalsh_tridiagonal`), whose last bits depend on the BLAS/LAPACK kernel, and the ends of the published intervals
297
+ are built from those points (fluxonium, sphere, molecule and Mathieu centre the trial interval on the estimate; the
298
+ matrix elements use the float eigenvectors). The intervals were correct on every machine, but not the same. A seeded
299
+ probe of 309 inputs over all the guaranteed modules (`gen.py` in the author's report) gave different strings under
300
+ Prescott in 29 of them on the code before (8 of 8 fluxonium, 6 of 6 molecule, 15 of 15 matrix elements); the rotor,
301
+ sphere and Mathieu cases of the probe happened to agree.
302
+
303
+ - New `qraft_ra._deterministic`: eigenvalue estimates by bisection on the Sturm count (tridiagonal), Householder then
304
+ the same bisection (dense), bisection on the LDLᵀ inertia (band), and eigenvectors by inverse iteration with banded
305
+ partial pivoting from a fixed start vector (sign fixed by the largest entry, the first one on a tie), using only Python float operations
306
+ whose result IEEE 754 fixes (+ − × ÷, `math.sqrt`, `math.fsum`, `ldexp`/`frexp`, `float()` of an int or a Fraction)
307
+ in a fixed order. No BLAS/LAPACK, no libm transcendental, no builtin `sum()` of floats (changed in Python 3.12).
308
+ - Every guaranteed module uses it: `guaranteed_intervals` (estimates and the first-order filter of the basis growth),
309
+ `spherical_rotor`, `hyperfine_molecule`, `mathieu`, `fluxonium`, `matrix_elements`. The other float decisions use
310
+ the same operations: the initial cut of the rotor and the fluxonium (`** 0.25` → two `math.sqrt`), `sum` of floats
311
+ → `math.fsum`; `Fluxonium.phi_zpf` is now √√(2E_C/E_L) by `math.sqrt` (the last bit may differ from before).
312
+ - The bisection runs to adjacent floats: as a side effect the Mathieu grid r ≤ 12, |q| ≤ 400 step 0.5 has 0 WIDE
313
+ values of 40 025 (6 before).
314
+ - `reproduce --hash` covers three more enclosures (a rotor with cos φ and cos 2φ, its ⟨0|cos φ|1⟩, an RbCs hyperfine
315
+ sector); `reproduce.determinism_parts()` returns them by name.
316
+ - `tests/test_determinism.py`: the strings of every hash entry (python-flint 0.9.0; another version is skipped with
317
+ the reason), the fluxonium ends spelled out, the same guaranteed intervals in fresh interpreters under
318
+ `OPENBLAS_CORETYPE` unset, Prescott and Nehalem (where numpy runs on run-time-dispatched OpenBLAS on x86-64;
319
+ skipped elsewhere with the reason), no numpy/scipy eigen-solver called by any guaranteed entry point, the
320
+ estimators against numpy on seeded matrices, the zero-pivot and eigenvector-sign conventions, the overflow refusal,
321
+ φ_zpf and the fluxonium cuts (9 tests). On the code before, the cross-kernel test and the fluxonium test fail by
322
+ assertion (6 more by ImportError of the new names, 1 passes); with numpy's `eigvalsh` put back into the fluxonium
323
+ module (positive control) 4 of 9 fail, under `OPENBLAS_CORETYPE` unset and Prescott alike.
324
+ - Mutation testing of the new and changed lines (36 hand mutants, test file of the mutated module first, on a green
325
+ tree): 31 killed. Alive: 3 equivalent, declared — `q <= 0` for the first Sturm pivot (a zero pivot is already
326
+ replaced by −pivmin), a stop tolerance 1e6× larger (still ≈ 1e-302·‖A‖), the scaling exponent + 1 (scaling by a
327
+ power of two commutes with IEEE rounding without under/overflow); and 2 on an overflow re-check that was dead code
328
+ (`math.ldexp` already raises OverflowError), now removed. The first round left 8 alive; the tests for the
329
+ zero-pivot convention, the eigenvector sign, φ_zpf and the fluxonium cut were added and killed 4 of them.
330
+ - The CI runs `test_determinism` in every job and the free-threaded job prints the hash too. Run 43177561844 on
331
+ 22a5b37: 16 of 16 jobs green, all 16 print `4d7d61cd…` (Linux x86-64, ARM64, macOS, Windows, 3.14t); the
332
+ cross-kernel test ran on the 13 x86-64 jobs and was skipped with its reason on ARM64 and macOS.
333
+ - Measured after the change (2026-11-12): the same hash on Python 3.11.2, 3.12.15, 3.13.15, 3.14.8 × `OPENBLAS_CORETYPE`
334
+ unset/Haswell/Prescott/Nehalem (16 of 16 runs); the probe of 309 inputs identical under the three kernels and on
335
+ Python 3.12 and 3.14. Times (README): a Mathieu value 0.38 → 0.50 ms median, the real-transmon bench 5.6 → 8.7 s
336
+ (the cos 8φ fits use the band bisection), the sphere 0.36 → 0.51 ms; the other benches changed by −9 % to +11 %.
337
+
338
+ Also: `bench/readme_numbers.py` stopped with AssertionError on the state OK_FLOAT_FLOOR (added in 0.3.0) and counted
339
+ it as a flag; it now accepts it as an OK state. The watchdog test of the multiport expected the exact size the host
340
+ watchdog samples during a 6 MiB write ("6 MiB"); under load it saw 5 MiB: the test now accepts 4–6 MiB.
341
+
342
+ ### Fixed before release (adversarial review of the determinism delta, 2026-11-12)
343
+
344
+ - Dense and sphere estimates far from 1: `householder_tridiagonal` returned the squared off-diagonal scaled back to the
345
+ input's scale before the bisection rescaled it, so an entry beyond ≈ 1.3e154 overflowed; the sphere squared dE in
346
+ floats. Measured on 4e44e10: molecule B = dE = 1e160 and 1e200, sphere B = dE = 1e200 stopped with a raw
347
+ OverflowError, fluxonium E_C = E_J = E_L = 1e160 with OutOfDomain, where the LAPACK estimates of 36c574d gave
348
+ GUARANTEED. Now `dense_lowest` bisects the scaled tridiagonal and the sphere prescales its exact entries by a power
349
+ of two: the same floats as before wherever nothing overflowed (determinism hash unchanged).
350
+ - Deadline: the pure-Python estimates ran without the cooperative deadline check (LAPACK took milliseconds there):
351
+ `guaranteed_levels` with cos φ…cos 24φ at N = 4000 and `deadline=2` stopped after 212 s (8.4 s on 36c574d), cos φ…cos
352
+ 8φ at n_g = 900.2 after 17.3 s (8.1 s). The estimators now take the caller's check before every count, every
353
+ Householder column and every 256 eliminated columns: 2.0–2.1 s in the three cases; no number changes.
354
+ - `guaranteed_sphere_levels` and `guaranteed_molecule_levels` turn an OverflowError into OutOfDomain like the other
355
+ guaranteed entry points (B = dE = 1.7e308 crashed, before this release too); a NaN entry is refused like an
356
+ infinite one.
357
+ - Tests for four live mutants: the tie rule of the eigenvector sign (antisymmetric levels of a symmetric rotor tie
358
+ exactly: the rule fixes the published sign of ⟨0|n|1⟩), φ_zpf by two square roots, `math.fsum` in the molecule
359
+ entries (killed on Python ≤ 3.11, where `sum` is not compensated), the finiteness check.
360
+
361
+ ## 0.2.0 — 2026-11-11 (local, unreleased)
362
+
363
+ Added (every module carries its proof or its measurement in the docstring; identifiers, states and the docstrings of the
364
+ new modules are in Italian, as in the source they were extracted from):
365
+
366
+ - `rotore_sferico_garantito`: guaranteed intervals for the spherical rotor in a field,
367
+ H = B N(N+1) − D_c [N(N+1)]² − dE cos θ at fixed M (polar molecule, rigid rotor with centrifugal distortion). Same
368
+ argument as the charge-basis rotor: Rayleigh–Ritz above, Young's inequality on the outer block below, Sylvester
369
+ counts in Arb balls.
370
+ - `molecola_iperfine_garantita`: guaranteed intervals for a ¹Σ molecule with rotation, centrifugal distortion,
371
+ nuclear spin–rotation (c₁, c₂), scalar (c₄) and tensor (c₃) spin–spin terms, nuclear quadrupoles, Zeeman and Stark
372
+ terms (fields along z, one M_F sector at a time), with the truncation in N closed by the proof. The lower bound of the
373
+ outer block is −(K_T + K_Q) − dE (norms of bounded operators, compression does not increase the norm). The matrix
374
+ elements are built in Arb from exact square roots of rationals (3j symbols by sympy) and agree with diatomic-py 2.1.0
375
+ element by element (relative 6.5e-16 on 23 830 elements) once the sign of the tensor term is mapped: diatomic-py's
376
+ code uses +√6 c₃ T²(C)·T²(I₁,I₂), its paper (eq. 5b) and Aldegunde et al. 2008 the opposite sign; see "Reported".
377
+ - `scqubits_adattatore`: the convergence warning now follows the declared threshold rule "soglia-scq/1.0": declared
378
+ unit (GHz/MHz/Hz, covariant), absolute 1e-6 GHz, relative 1e-6 of the span, a float floor
379
+ 32·eps·(‖H(ncut)‖₁ + ‖H(2ncut)‖₁) with the origin of the threshold reported (`assoluta`, `relativa`, `utente`,
380
+ `pavimento`) and the state `OK_LIMITE_FLOAT` when the floor decides. Measured on 200 transmons of the device regime:
381
+ 0 results marked OK that were outside the threshold (2000 + 1200 runs), and 0 on 1080 runs outside that regime by
382
+ an independent reviewer. The old relative rule (up to 18 kHz in the device regime) is removed. Optional engine
383
+ `motore="sandbox"` runs scqubits through the multiport.
384
+ - `multiporta` and `porte_runner` (contract "qraft-porta/0.1", specification v0.2): an external solver runs in a
385
+ bubblewrap sandbox (no network, separate PID/IPC namespaces, only /usr, /lib, /lib64, /bin, /etc, the declared
386
+ site-packages and, for an interpreter outside /usr, its executable, standard library and libpython (never its whole
387
+ prefix) mounted read-only, tmpfs HOME, 256 MiB tmpfs, rlimits, timeout, minimal environment also for bwrap
388
+ itself), returns a JSON validated against a closed schema (unknown key, NaN, duplicate key, wrong length = not
389
+ comparable, with the reason), and is compared level by level with the intervals in exact arithmetic, with a
390
+ declared float tolerance. Ports: numpy reference, scqubits, diatomic-py, qiskit-metal `Hcpb` and `mathieu_level`
391
+ (from their source files, mounted read-only), SQcircuit, pyEPR, CeNTREX-TlF, plus the test ports used as positive
392
+ controls (network, file writes, loops, memory, malformed output). Without bubblewrap, or with a bubblewrap that is
393
+ not root-owned, nothing runs (`SandboxAssente`): no silent fallback.
394
+ - `duffing_vs_transmon`: guaranteed per-level error of the Duffing model ω a†a + (α/2) a†a†aa against the full
395
+ transmon, with ω and α taken from the guaranteed intervals of the first three levels ("spettro") or from Koch et al.
396
+ 2007 first order ("koch"). On 200 transmons of the device regime |δ_3| ≥ 1 MHz in 200 of 200 (median 34 MHz).
397
+
398
+ Fixed before publication (adversarial review of 2026-11-11):
399
+
400
+ - Multiport: an interpreter outside /usr had its whole installation prefix mounted read-only; with a prefix equal to
401
+ `<home>/.local` (Python built with `--prefix=$HOME/.local`), or equal to the home itself when `$HOME` differs from
402
+ the account's home, a port could read the user's hidden folders (measured: a fake keyring file read in 2 of 2 cases).
403
+ Now only the executable, the standard library and libpython are mounted, and the home is taken from both `$HOME` and
404
+ the user database. Tests: `TestPrefissoInterprete` (6 tests, they fail on the previous code). Mutation testing of the
405
+ new lines on Python 3.11 and 3.12: 12 of 15 mutants killed, 3 equivalent (early return for /usr, a redundant
406
+ coherence check, `-I` on the probe), checked by reasoning and on 305 inputs (0 differences; a non-equivalent control
407
+ mutant differs on 8).
408
+ - `requirements/ci.txt`: numpy 2.5.4 and scipy 1.18.1 have no wheels for Python 3.10 and 3.11; those two jobs now pin
409
+ numpy 2.2.6 / scipy 1.15.3 (3.10) and numpy 2.4.6 / scipy 1.17.1 (3.11).
410
+ - README: numbers of the qiskit-metal example, the CeNTREX-TlF warning at 30 kV/cm, what `TestGregory2016` checks, the
411
+ scipy versions tested, the sandbox test count and the width default corrected; CONTRIBUTING test command; NOTICE;
412
+ provenance hashes in `FILE_LIST.tsv`; license headers of the test files; references to the author's unpublished notes.
413
+
414
+ Changed:
415
+
416
+ - `pyproject.toml`: dependency on sympy (3j symbols); optional extra `porte` with the external solvers.
417
+ - CI: Python 3.10–3.14 (python-flint 0.9.0 wheels exist for 3.10–3.14); bubblewrap installed on the runner and
418
+ unprivileged user namespaces allowed (Ubuntu 24.04 restricts them through AppArmor); the job fails if the sandbox
419
+ cannot start, so in CI no test is skipped for lack of the sandbox (the CeNTREX and diatomic-py port tests are
420
+ skipped because those packages are not installed there).
421
+ - Multiport: the nesting of `[` and `{` in a port's output is limited to 32 levels, counted in one linear pass
422
+ before the JSON parser (`USCITA_MAX_PROFONDITA`). Before, the limit was the parser's RecursionError, which on
423
+ Python 3.14 depends on the stack size: measured on 2026-11-11 with CPython 3.14.8, 10 000 levels accepted with an
424
+ 8 MiB stack, 100 000 levels accepted with 64 MiB or unlimited; the first CI run on 3.14.10 accepted 100 000.
425
+ - Tests: `test_scqubits_adattatore` and `test_porta_centrex_tlf` decide whether the sandbox is usable with the same probe
426
+ as `test_multiporta` (bwrap started with `--unshare-all`), not with the presence of `bwrap` on PATH: on a runner where
427
+ `bwrap` exists but cannot create namespaces the sandbox test raised an error instead of being skipped.
428
+ - `check_tree.py`: the closed list of allowed third-party imports includes sympy and the optional port packages.
429
+
430
+ Reported upstream (public, by the author, 2026-11-11; the package reproduces each one, see README):
431
+
432
+ - durham-qlm/diatomic-py #15: sign of the tensor spin–spin term in `tensor_nuclear` (2.1.0) differs from the paper.
433
+ - qiskit-community/qiskit-metal #1252: `mathieu_level` returns the value of another order for some (m, q)
434
+ (root cause in `scipy.special.mathieu_a/b`, 522 wrong values on m ≤ 12, |q| ≤ 400 with scipy ≤ 1.18.1; fixed on
435
+ scipy main by PR #25128, milestone 2.0.0).
436
+ - scqubits/scqubits #334 (comment): the first scipy release whose sparse `eigsh` omits the zero eigenvalue is 1.15.0
437
+ (bisection 1.10.1 … 1.18.1, independent of the start vector).
438
+
439
+ Not in this release: the certificate module (removed in 0.1.0), guaranteed Mathieu characteristic values (planned),
440
+ fluxonium, matrix elements.
441
+
442
+ ## 0.1.0 — 2026-11-10 (local, unreleased)
443
+
444
+ First extraction: `rotore_spettro`, `intervallo_garantito`, `scqubits_adattatore` (relative threshold), docs/PROOF.md,
445
+ check_tree.py, CI with gitleaks.
@@ -0,0 +1,40 @@
1
+ # Contributing
2
+
3
+ Issues and pull requests are welcome. Security problems: see `SECURITY.md` (private report).
4
+
5
+ ## Rules of this repository
6
+
7
+ 1. **No number without a proof or a reason.** An interval marked GARANTITO must follow from `docs/PROOF.md`; where the
8
+ proof does not apply, the code raises `FuoriDominio` with the reason instead of returning a number.
9
+ 2. **Unknown input is refused with a named reason, and a test shows it.** `Rotore` refuses NaN, ±inf, booleans,
10
+ strings and other non-numbers, non-integer or non-positive harmonics and E_C ≤ 0 (`tests/test_rotor_spectrum.py`,
11
+ including 400 random inputs); `livelli_garantiti` refuses anything outside the domain of the proof with
12
+ `FuoriDominio`; the adapter refuses any rotor that is not a transmon.
13
+ 3. **Tests first fail, then pass.** A fix comes with a test that fails on the old code. A new check comes with a
14
+ positive control: an input it must refuse.
15
+ 4. **Only measured sentences** in README, docstrings and commit messages. If a number is quoted, say how it was
16
+ measured and on what date.
17
+ 5. **The file list is closed.** `FILE_LIST.tsv` lists every file with its sha256; `python check_tree.py` fails on any
18
+ file not listed, any import outside the standard library, the listed modules and a closed set of third-party
19
+ packages (numpy, scipy, python-flint, sympy, mpmath, scqubits and the optional port packages listed in
20
+ `check_tree.py`), and any secret found by gitleaks. A pull request that
21
+ changes or adds a file updates the list with `python check_tree.py --update` and commits the new `FILE_LIST.tsv`.
22
+
23
+ ## Running the checks
24
+
25
+ ```sh
26
+ python -m venv .venv && . .venv/bin/activate
27
+ pip install -r requirements/ci.txt && pip install --no-deps .
28
+ cd tests && python -B -m unittest test_guaranteed_intervals test_rotor_spectrum test_scqubits_adapter test_readme_example \
29
+ test_spherical_rotor test_hyperfine_molecule test_inputs_review test_duffing_vs_transmon test_mathieu test_fluxonium \
30
+ test_multiport test_port_centrex_tlf test_parameter_balls test_matrix_elements test_cos4phi_origin test_examples test_generative_inputs test_deprecated_aliases && cd ..
31
+ git add -A # check_tree.py lists files with git (tracked + untracked, .gitignore respected)
32
+ python check_tree.py --update # needs gitleaks on PATH
33
+ python check_tree.py --positive-control
34
+ ```
35
+
36
+ Install with `pip install .` (not `-e`) before running the tests, so they exercise the installed package.
37
+
38
+ ## License of contributions
39
+
40
+ By contributing you agree that your contribution is licensed under the Apache License 2.0 (see `LICENSE`).
@@ -0,0 +1,85 @@
1
+ # FILE_LIST.tsv — closed list of the files of this repository. Any other file is an error of check_tree.py.
2
+ # columns: path<TAB>sha256 in this repository<TAB>kind (copy | derived | new)<TAB>sha256 of the source file or -
3
+ # derived = from the author's OMEGA repository (commit d29eeedc, 2026-11-10, for intervallo_garantito, rotore_spettro and
4
+ # their tests; commit 3589178b, 2026-11-11, for the modules added or changed in 0.2.0, whose docstrings stay in Italian), with declared changes: package name
5
+ # in imports, Apache-2.0 header, docstrings and comments translated to English, unused functions removed, tests
6
+ # of the certificate module removed (see the commit message). check_tree.py, LICENSE and SECURITY.md are adapted
7
+ # from the author's pq-scitt repository and listed as new.
8
+ .github/workflows/ci.yml cd376d0d8ab565579d83c98124654e8f2f9786ad1cf0c345d4c741d12d0ed5a5 new -
9
+ .gitignore 38aa872843f05008de09056f5acb78835d8a8c9b21bc68854ec2f78b8c86fb07 new -
10
+ CONTRIBUTING.md 4caa3a929fea743a4cd4c0a9add1fe4950fb8bd7a69e2cb96f6e44b7433d58bc new -
11
+ LICENSE cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 new -
12
+ MANIFEST.in 229498c55a9b68d917c824131f9f7e92d3c76ae9b5ad393da81203c8fdcffc52 new -
13
+ NOTICE 70c60f70a8f6a42159b146b67a288cfa669d9247d90fd52d5907258acaad28e7 new -
14
+ README.md d8d075f5b886ea3076a9c14c026185789593a8cba8592c7688107c05f8f6204c new -
15
+ SECURITY.md ac81e2b3b0e1b3eb6099e1cf8c59a0b66a920a43361907320a5db816446064e1 new -
16
+ bench/readme_numbers.py 12d4d4d20a333c160e3de0a38bad6b98e8b8f163d0e46664e8e42733d87c24d1 new -
17
+ check_tree.py a9203b8b28b5df3ba42edc3e76866befd188213b17e204eb70e2d77894199063 new -
18
+ docs/PROOF.md ebf2303c441d386856129f15dea03d5a0554bcc8c067041f7eaeabdf10d486e0 new -
19
+ pyproject.toml 501bcba29d16ed57496688d90c5ca969daed43e4bca2f18e70633b1ad705a7e0 new -
20
+ requirements/ci.txt 6332112ea9ed8a81d7b732d8b350ecb22c67a4c00946b8938991e4d7f898f81a new -
21
+ src/rigorous_levels/__init__.py 406a6c60e73549f20b7a2ad9d3b1ec5cacce5fe18059b63a59c98e8836c10099 derived 4d406ffbf052a29dbc8b9af81695c7f04ab484c0bd510dbb1fd14678ae6a02d4
22
+ src/rigorous_levels/intervallo_garantito.py 352ed9aec2bf552b0ef96f48e076885d8fb95779f3acd9cc3f207370fa13bf53 derived 4fb43f09aee317d3d26beb378c7f732ec0d254cbdab559eef36984d8532f4b1a
23
+ src/rigorous_levels/rotore_spettro.py 42fb0385da2db29250a5ebf382d6c8706ba10562e2467c51bce622d7b9ee1d39 derived c92a5192777fa2c2b8b4e92ab488d3b4b393b00df6ec6e6f39379431dedf929b
24
+ src/rigorous_levels/scqubits_adattatore.py 7d0239d411737a03f11ff4701a876412dd06686297941a8b09b7d87976187fc3 derived b371dc03dd4b4180c8466433426661f95dc0ec0051ce2eadf03c4815da5b1fd2
25
+ tests/test_readme_example.py a0d5be0a2efddedafbf078fb10b32cdd612529a62b1cc927a16fe69ea02ccffe new -
26
+ src/rigorous_levels/duffing_vs_transmon.py 12c5a14f43db7da867e2ad3a6a167151caf93f8b7191ff436ab16269b4420175 derived d92fd01a7f64b6ba43c762f197fbefc60434c3f0e3c128149eb8397072bdeb20
27
+ src/rigorous_levels/molecola_iperfine_garantita.py 44774d9f3725697c9891abf842a7167ee461ca3f452c9cb16f974262f6112991 derived 0d0c5e6745c657af2788aa865855622db5e0a6365892ff475761fd88191e497b
28
+ src/rigorous_levels/multiporta.py a8dca6d6bc339c0e45b50038c8d20fe4f236cade0eecb370c9fc49a8b773b229 derived edf48bd2a7667ba3cc0623222cfbc86b54e2829359249052d7b8d90754e829d3
29
+ src/rigorous_levels/rotore_sferico_garantito.py c6a24a6cd3cbe5ed0a0d1a6b18a265fc270a9dd782ebaf0fca0dce4cef63a20a derived c1e3e642157a172d07f9f66211012b545a258c333df48c66ba9c1fb3e5f32310
30
+ tests/test_duffing_vs_transmon.py c71d3d902a6b67d9bad01a86fc7347b25afc925f853c7cdfe7cdcf6eb81645c8 derived 888fd8a85a2707da48a243648b1818756ce778fe8e11b14ca7438e0d0d03be0b
31
+ CHANGELOG.md e2f5fcbcbb049833a0801d99e94dd854464ef2f647da47de7f5be52fdcf6fcd7 new -
32
+ bench/fluxonium_devices.py c2b28fae5297dc379b1ad805ea515ef8fc174282696a6b843501862cb7ccbf01 new -
33
+ bench/mathieu_vs_scipy.py 372ca437311ce4433651603fd61cc98960213669943c0dcf5c652828bb148497 new -
34
+ bench/matrix.sh 63484a83bbfc74d6c843424e6c66c3ed1a382bfab05bb763291fe89b010a7957 new -
35
+ docs/PORT_CONTRACT.md 8a901faa476648eaa563039052e0325fd6d4bfbbe54eebb81fbd0d3889745fde new -
36
+ docs/THRESHOLD_RULE.md b2b788498ec2eddcef165a00e8b67b3e6a0bd4e8e3cc746c817b47083b89ff79 new -
37
+ src/rigorous_levels/_compat.py b0bfefed0f17a40674c8486e23f3cac0d7eedc5cf80efc17ceb8504e81859887 new -
38
+ src/rigorous_levels/fluxonium.py 1206fed81377dd3a9bcc826051e2177c08f3b67d86cf3888bacb563f3e8c3fa7 new -
39
+ src/rigorous_levels/guaranteed_intervals.py 14a27a8f3d318366448ec5ac11193789dbc5aeff5c76a7708d45208b94a0e6cd new -
40
+ src/rigorous_levels/hyperfine_molecule.py 272a4e900776431d7e881e1d1d623919705221bd153cc39d2d6348fb5652a78f new -
41
+ src/rigorous_levels/isolation.py 0c849be07a067c4c359f8c40c62c4660611aa82680f60454e7de95ef49ef61ea new -
42
+ src/rigorous_levels/mathieu.py 641dd16b75944e8d0c975e4ab38e8d5bbf62dd664d033e5434375238789d8551 new -
43
+ src/rigorous_levels/multiport.py 2b12e63354bedd86b08bcd6882733a4ee022c57d80def006627fc6d67b59b4fa new -
44
+ src/rigorous_levels/port_runner.py c0ba7d5f779f7dd99fbe1f82f26a353a53f2756ed5d9bd50c8ac98accb020b44 new -
45
+ src/rigorous_levels/rotor_spectrum.py 03d18c377199c10da0773609d4984c5b865ac91be55b6c7a6a017ebe278dcce9 new -
46
+ src/rigorous_levels/scqubits_adapter.py 6fa5a043578ba8bbdb5db4700515d6f3e3fafeb20f5a21fe72cdd8c2f2418990 new -
47
+ src/rigorous_levels/spherical_rotor.py fb2adde0bfcc560a90dda3eacbdb0a63014fd052c381841d1c432e7fbe4daab4 new -
48
+ tests/test_deprecated_aliases.py cd15020872dcaf98145cec33ba95628fbdc70f11e7f592243b600cc470c7454d new -
49
+ tests/test_fluxonium.py 024c343e802c0bbc43f150bb93edc38bf4921ecfe17ce8cc702d93a5e87132fc new -
50
+ tests/test_generative_inputs.py 2e491bdc479640910e9cda8236875213857fbd05982d1c8be5bf9fa4eacd5611 new -
51
+ tests/test_guaranteed_intervals.py 5c4608b40012efe5e8a1bdcaa5bcffadedfbdde849d28bf43fa8b01af9feafd0 new -
52
+ tests/test_hyperfine_molecule.py 30325336ac147efed235847027b8a5bfb336f70e65d82e903cd8e8737676a4db new -
53
+ tests/test_inputs_review.py 452bd79367765cb3e1ebc784c3abd49be3e1715cd127c92c5a03f03f73b8d200 new -
54
+ tests/test_mathieu.py fe30237d39c5efd1a8d6cfb2ab1adad1f31f82c99397ee040229dc683e1b6bf4 new -
55
+ tests/test_multiport.py 5b6e1eaf14d8b54f3109a168eb982120dee4a42a00a2d4c8b06b4663c6ab1d0d new -
56
+ tests/test_port_centrex_tlf.py 6928c59ab97e22ec1380730bf32394f920089d88ec1751329dd8198b6d00e41b new -
57
+ tests/test_rotor_spectrum.py 7933e81c1ac370d52d8def7f4b947a054b17d882efef739a2c452f7f774dbf06 new -
58
+ tests/test_scqubits_adapter.py 9d1f14d07466a1243b9c29432bb760c2cf8c9abf8c5b1198cb36319a3716ad9f new -
59
+ tests/test_spherical_rotor.py bf0460fff6c6b6f15fe637f50883c6ef866d1e6ddff5b176f301caec6aafa788 new -
60
+ CITATION.cff fd72b2351052b585b391a87d644e77cdfb09aa6c021fe02356e3d188fdf574f7 new -
61
+ bench/compare_tools.py 80522dbd8506cc46b3ef9b7a7c9390c20255517972bea9bc6bcf197cc152772c new -
62
+ bench/parameter_balls.py 677263e038fc02f8b4aab7c78dd3bc7513970beb143739c9b5f1959fa224e4ee new -
63
+ examples/from_scqubits.py ad6910049a48fe1c1dfd4bad00b6ec39f2b04fb6ac9999d2b9a7e643f1a899eb new -
64
+ examples/mathieu_and_fluxonium.py c546d7435d5ea15d8333a1b295489e5989809a4f5b41fac5bbd05ffb9b6b7426 new -
65
+ examples/parameter_box.py 55decb1cde04a482453d1e4e000b9c535d1cac0e70b7435b83deb23e37b3ead6 new -
66
+ src/rigorous_levels/parameter_balls.py 63fa2e6eef5cbfe3fd3c6dc0e390031889b9e91b2d80ad42376e92865c52fb0a new -
67
+ tests/test_examples.py 4ce66a0087e141312bcd419bca0b013c0fe7f79b8fc189112a4dae19b82d1ce2 new -
68
+ tests/test_parameter_balls.py fed6f5670783808dda822b9bd1764b6f51d9a7e5d0ae61d77572e282a509fca6 new -
69
+ bench/matrix_elements.py 9b1bfe227f78cab91ce360397e75999440af76d0eaacb27117a54ae7d283fba2 new -
70
+ examples/matrix_elements.py 5ea3149b48287cb98832d49f80dbe9c6a82f8aaa729f4eeb0497daddec757a07 new -
71
+ src/rigorous_levels/matrix_elements.py 72b808a1d2242b80ae765305522ee94fd7a87f2add23bb527e6be2394d203e17 new -
72
+ tests/test_matrix_elements.py 0a26cd560826c1eacea395f56a26d6ad0bc46dea20cd1e5a166cd8312b722d1a new -
73
+ bench/check_200.py 3ae12b345293210f53a9756ee5d7f72dfaba965ddec6689d0531b2a6268aebe8 new -
74
+ examples/regression_assert.py 0d43ba60fc7045744e42e78005c2141dd5aa0817f7b656ac7d0912d235c17d9a new -
75
+ src/rigorous_levels/reproduce.py 45ed728a854a8b308b8d220ff02422a91202694814ffc240eb10966324722b92 new -
76
+ tests/test_cos4phi_origin.py f4d86d8dd7e287a65581b96021ebeb79e17aaf7933ea41d5dd4c6c92e20485e4 new -
77
+ bench/duffing_adapter_sphere.py d6c5e6705d0e1c4ab600ee2b3eed50f3b8cea4bc9d49dda2795fcb220b9fa5e7 new -
78
+ bench/gregory2016.py 456a85798b653d9bb1a0d6b3437615c1a980f81cbcc8188603718ee0dd7f6d3f new -
79
+ bench/multiport_batch.py 96e2a773a98b1ad7f8cf85822d86fbebced9966ac2510b015de51912d0668779 new -
80
+ bench/real_transmons.py e9a381f1f80f9493d7231b69eeb3f8cdeb0fed3279ad8c001511af24a7e91b2b new -
81
+ tests/test_spec_bounds.py d05b79fe63c34978ec8479b2f3ee79e45009cc4d5e5057d3b5b5336b98495281 new -
82
+ src/rigorous_levels/_deterministic.py 62b9de82f35b3f64ef0d5f87c76f6770d295b4866a5cdb8972defe8937eee9d0 new -
83
+ tests/test_determinism.py 46ce86b8e1b7d9f78e3cd1c124955fc4deff41528efc6f7c64f5bb21f0bb8794 new -
84
+ .github/workflows/release.yml 69739364a9782dd281e6eb03ba7a0ef96ecb34249b814b2e4553aa95f28c43aa new -
85
+ requirements/build.txt 57a14d76b46d96e8e1b0dcaf5fda2e4d23658e5128fcae98c484f9346e749f18 new -