From 2495177d3fcd2847e8e13847814535f42023e750 Mon Sep 17 00:00:00 2001 From: sotashimozono Date: Mon, 14 Sep 2026 15:30:28 +0000 Subject: [PATCH 1/4] Sample-to-sample width, which is the thing a disorder run already has MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An ensemble of samples gives `R_X = Var(X)/[X]²` per size, and its slope says which fixed point the calculation is at. Nothing read it. StrongSelfAveraging d ln R_X/d ln L = -d (L ≫ ξ) CriticalSelfAveraging d ln R_X/d ln L = α/ν (critical, disorder irrelevant) PseudocriticalWidthScaling d ln δT_c/d ln L = -1/ν (random fixed point) The third is the sharp one. `δT_c ∼ L^{-1/ν}` and not `L^{-d/2}`, which is the central-limit answer and holds only where Harris does. The two are 2% apart for the site-dilute Ising model in d = 3, where the measurement is 1.449(8) against d/2 = 1.5, six of its own errors away, so the test asserts that separation rather than the value. Relevant randomness gives no decay at all: `R_X` approaches a universal non-Gaussian of L-independent width, so both decay laws are refused rather than fitted. Aharony and Harris, and the simulations in Wiseman and Domany, both contradict the earlier conjecture that it goes as `L^{α/ν}` with the random fixed point's exponents, so `CriticalSelfAveraging`'s docstring says the exponents are the pure system's and that reading it otherwise is that refuted form. Both citations checked against Crossref rather than written from memory; the Wiseman-Domany title differs between the preprint and the published version and the published one is used. `1/ν` is not affine in `ν`, so `solve(:ν)` needs a specialized `_solve`. Writing one in `scaling.jl` defined a new local function instead of extending the parent's and was silently inert, since `Criticality` did not import `_solve` the way `StatisticalMechanics` does. It does now. Co-Authored-By: Claude Opus 5 (1M context) --- Project.toml | 2 +- docs/references.bib | 22 ++++++++++++ src/AbstractQAtlas.jl | 1 + src/relations/scaling.jl | 59 ++++++++++++++++++++++++++++++++ test/relations/test_interface.jl | 4 +-- test/relations/test_scaling.jl | 29 ++++++++++++++++ 6 files changed, 114 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index db5c6f4..f23a853 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "AbstractQAtlas" uuid = "dcea2817-62f8-4a75-b498-1b50a9ed1e4d" -version = "0.7.13" +version = "0.7.14" authors = ["sota shimozono "] [deps] diff --git a/docs/references.bib b/docs/references.bib index baf8b46..556fa09 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -858,3 +858,25 @@ @article{Peterson1967 publisher = {American Physical Society (APS)}, doi = {10.1103/RevModPhys.39.69} } + +@article{AharonyHarris1996, + title = {Absence of Self-Averaging and Universal Fluctuations in Random Systems near Critical Points}, + author = {Aharony, Amnon and Harris, A. B.}, + journal = {Physical Review Letters}, + volume = {77}, + number = {18}, + pages = {3700--3703}, + year = {1996}, + doi = {10.1103/PhysRevLett.77.3700} +} + +@article{WisemanDomany1998, + title = {Finite-Size Scaling and Lack of Self-Averaging in Critical Disordered Systems}, + author = {Wiseman, Shai and Domany, Eytan}, + journal = {Physical Review Letters}, + volume = {81}, + number = {1}, + pages = {22--25}, + year = {1998}, + doi = {10.1103/PhysRevLett.81.22} +} diff --git a/src/AbstractQAtlas.jl b/src/AbstractQAtlas.jl index f91ca36..ce4781d 100644 --- a/src/AbstractQAtlas.jl +++ b/src/AbstractQAtlas.jl @@ -107,6 +107,7 @@ end "Critical phenomena and conformal field theory: scaling laws, finite-size scaling, Cardy, the c-theorem." module Criticality using ..AbstractQAtlas + import ..AbstractQAtlas: _solve # extended for a non-affine variable (PseudocriticalWidthScaling:ν) include("relations/scaling.jl") include("relations/cft.jl") end diff --git a/src/relations/scaling.jl b/src/relations/scaling.jl index a06f42d..3a075fd 100644 --- a/src/relations/scaling.jl +++ b/src/relations/scaling.jl @@ -702,6 +702,65 @@ autocorrelation `G(t) ∼ exp(−A|ln t|^d)` of Eq. (A.34) (= Eq. (9.7), §9.1.3 @relation :scaling OrderedGriffithsEnergyScale(dloglogΩ_dloglogL, d::SpatialDimension) = dloglogΩ_dloglogL * d - 1 +""" + StrongSelfAveraging <: AbstractRelation + +`R_X ∼ (L/ξ)^{-d}` for `L ≫ ξ`, so `d ln R_X/d ln L = -d`, where +`R_X = Var(X)/[X]²` over an ensemble of samples (Aharony & Harris, +[AharonyHarris1996](@cite)). + +Off criticality only. At criticality the slope is `(α/ν)_pure` if the randomness +is irrelevant ([`CriticalSelfAveraging`](@ref)) and ZERO if it is relevant, the +distribution then approaching a universal non-Gaussian of `L`-independent width. +So the measured slope says which of the three a calculation is in, and a fixed +point where it does not decay is one where more samples at larger `L` do not +improve the statistics. + +Variables: `dlogR_dlogL` (caller-computed), `d`. +""" +@relation :scaling StrongSelfAveraging(dlogR_dlogL, d::SpatialDimension) = dlogR_dlogL + d + +""" + CriticalSelfAveraging <: AbstractRelation + +`R_X ∼ L^{α/ν}` at criticality when the randomness is IRRELEVANT, so +`d ln R_X/d ln L = α/ν` ([AharonyHarris1996](@cite)). + +The exponents are the PURE system's, which is what makes this the irrelevant- +randomness statement rather than a general one. Wiseman and Domany conjectured +the same form with the random fixed point's exponents; [AharonyHarris1996](@cite) +and the simulations in [WisemanDomany1998](@cite) both contradict it, the width +going to a constant there instead. Reading this relation with random-fixed-point +exponents is that refuted conjecture. + +Variables: `dlogR_dlogL` (caller-computed), `α`, `ν`. +""" +@relation :scaling CriticalSelfAveraging( + dlogR_dlogL, α::SpecificHeatExponent, ν::CorrelationLengthExponent +) = dlogR_dlogL - α / ν + +""" + PseudocriticalWidthScaling <: AbstractRelation + +`δT_c(L) ∼ L^{-1/ν}`, so `d ln δT_c/d ln L = -1/ν`, where `δT_c` is the width of +the sample-to-sample distribution of pseudocritical temperatures +([WisemanDomany1998](@cite), measured directly). + +Not `L^{-d/2}`, the central-limit answer, which holds only where the Harris +criterion does. The two are close and the measurement has to be good enough to +tell them apart: the site-dilute Ising model in `d = 3` gives `1.449(8)` against +`d/2 = 1.5`, six of its own errors away, and `1/ν = 1.47`. + +Variables: `dlogδTc_dlogL` (caller-computed), `ν`. +""" +@relation :scaling PseudocriticalWidthScaling(dlogδTc_dlogL, ν::CorrelationLengthExponent) = + dlogδTc_dlogL + 1 / ν + +# `1/ν` is not affine in `ν`, so the generic three-probe inverter refuses it. +function _solve(::PseudocriticalWidthScaling, ::Val{:ν}; dlogδTc_dlogL, _extra...) + return -1 / dlogδTc_dlogL +end + """ WeinribHalperinExponent <: AbstractRelation diff --git a/test/relations/test_interface.jl b/test/relations/test_interface.jl index edb2a90..9b3f041 100644 --- a/test/relations/test_interface.jl +++ b/test/relations/test_interface.jl @@ -18,9 +18,9 @@ AbstractQAtlas.domain(::_NonAffineDemo) = :test_only # domain that has no line here shows up as a mismatch in this number alone. # Universal-only: the model-specific relations (spin glass, Drude mobility, # single-band Hall) live in QAtlas. - @test length(rels) == 170 + @test length(rels) == 173 @test allunique(typeof.(rels)) - @test length(all_relations(; domain=:scaling)) == 30 # +ActivatedDynamicalScaling, ActivatedFiniteSizeScaling, +14 Appendix-A scaling types, +WeinribHalperinExponent, +QuantumHyperscaling; +TypicalCorrelationLength, GriffithsExponentDivergence, GriffithsSusceptibility, GriffithsSpecificHeat, ActivatedMomentGrowth; +CriticalCorrelationDecay, ActivatedCriticalCorrelation (#158) + @test length(all_relations(; domain=:scaling)) == 33 # +ActivatedDynamicalScaling, ActivatedFiniteSizeScaling, +14 Appendix-A scaling types, +WeinribHalperinExponent, +QuantumHyperscaling; +TypicalCorrelationLength, GriffithsExponentDivergence, GriffithsSusceptibility, GriffithsSpecificHeat, ActivatedMomentGrowth; +CriticalCorrelationDecay, ActivatedCriticalCorrelation (#158); +3 self-averaging @test length(all_relations(; domain=:thermodynamic)) == 15 @test length(all_relations(; domain=:fundamental)) == 9 # +GrandPotentialLegendre, ParticleNumberResponse (grand-canonical); +ElectricCurrentResponse (j = −∂H/∂A) @test length(all_relations(; domain=:topology)) == 3 diff --git a/test/relations/test_scaling.jl b/test/relations/test_scaling.jl index e9eb4ce..91bd9c7 100644 --- a/test/relations/test_scaling.jl +++ b/test/relations/test_scaling.jl @@ -684,3 +684,32 @@ end @test CriticalCorrelationDecay() in relations_constraining(DisorderAveraged{ConnectedSpinCorrelation}) end + +@testset "how the sample-to-sample width scales says which fixed point it is" begin + # `R_X = Var(X)/[X]²` over an ensemble of samples, which is what a disorder + # calculation already produces per size. + d, α, ν = 3, -0.1, 0.7 + @test solve(StrongSelfAveraging(), Val(:d); dlogR_dlogL=(-d)) ≈ d + @test solve(CriticalSelfAveraging(), Val(:α); dlogR_dlogL=α / ν, ν=ν) ≈ α + @test !check(StrongSelfAveraging(); dlogR_dlogL=(+d), d=d, atol=1e-9) # sign + @test !check(CriticalSelfAveraging(); dlogR_dlogL=(-α / ν), α=α, ν=ν, atol=1e-9) + + # Relevant randomness means no decay at all, so the off-critical law is refused + # rather than fitted: a fixed point where more samples at larger L do not help. + @test !check(StrongSelfAveraging(); dlogR_dlogL=0.0, d=d, atol=1e-6) + @test !check(CriticalSelfAveraging(); dlogR_dlogL=0.0, α=α, ν=ν, atol=1e-6) + + # `δT_c ∼ L^{-1/ν}`, not `L^{-d/2}`. The site-dilute Ising model in d = 3 measures + # 1.449(8), which is six of its own errors from d/2 = 1.5, so the discriminator + # discriminates; asserted as the ratio because the two are only 2% apart. + ρ, σρ = 1.449, 0.008 + @test abs(ρ - d / 2) / σρ > 5 + @test solve(PseudocriticalWidthScaling(), Val(:ν); dlogδTc_dlogL=(-ρ)) ≈ 1 / ρ + @test isapprox(1 / ρ, 0.69; atol=0.01) # and lands on that model's own ν + @test !check(PseudocriticalWidthScaling(); dlogδTc_dlogL=(-d / 2), ν=1 / ρ, atol=1e-3) + + @test variable_types(StrongSelfAveraging()) == (SpatialDimension,) + @test variable_types(CriticalSelfAveraging()) == + (SpecificHeatExponent, CorrelationLengthExponent) + @test variable_types(PseudocriticalWidthScaling()) == (CorrelationLengthExponent,) +end From 13daa7d538ab919555062e15581e49da94d5c448 Mon Sep 17 00:00:00 2001 From: sotashimozono Date: Mon, 14 Sep 2026 15:41:06 +0000 Subject: [PATCH 2/4] Teach the d-consumer table about the new relation `StrongSelfAveraging` types `d::SpatialDimension`, so `relations_constraining` is 14 and not 13. That table names every entry rather than counting a subset, precisely so one missing name cannot hide, and this was the missing name. Co-Authored-By: Claude Opus 5 (1M context) --- test/relations/test_scaling.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/relations/test_scaling.jl b/test/relations/test_scaling.jl index 91bd9c7..8a7f73c 100644 --- a/test/relations/test_scaling.jl +++ b/test/relations/test_scaling.jl @@ -604,7 +604,7 @@ end rs = relations_constraining(SpatialDimension()) # Every relation that takes a d, named. Listing a subset leaves the rest # resting on the soft coverage ratio, which cannot see one missing entry. - @test length(rs) == 13 + @test length(rs) == 14 for r in ( Josephson(), QuantumHyperscaling(), @@ -619,6 +619,7 @@ end FixedPointDisorderStrength(), ConventionalFieldSusceptibility(), ConventionalFieldSpecificHeat(), + StrongSelfAveraging(), ) @test r in rs end From 87309ee78476ae59c68f274d6554c38f0997059b Mon Sep 17 00:00:00 2001 From: sotashimozono Date: Mon, 14 Sep 2026 16:32:23 +0000 Subject: [PATCH 3/4] Review: the two regimes shared a symbol, and the graph read one off the other MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `StrongSelfAveraging` and `CriticalSelfAveraging` both called their slope `dlogR_dlogL`, which in the name-keyed graph is one node, so a critical measurement could be read by the off-critical law. Typing `d` then put an edge into a node ten other relations use, and the two together were worse than either: derive(:ν; dlogR_dlogL=-0.1/0.7, α=-0.1) -> 14.7 StrongSelfAveraging: {dlogR_dlogL} -> d = 0.1428 Josephson: {α, d} -> ν = 14.7 The intended answer is 0.7, and nothing errored. `consistency_report` also reported a DISAGREE on legitimate off-critical data, because both routes had to agree independently on a number only one of them was about. The source says the abscissa off criticality is `L/ξ`, not `L`: `R_X ∼ (L/ξ)^{-d}`. Writing it that way is both more faithful and a different variable, so the two regimes are separate nodes. Both symptoms are gone, verified, and the disjointness is pinned. `CriticalSelfAveraging`'s `α` and `ν` are the PURE system's, while those types mean the system under study everywhere else, so a bag built for other scaling relations could feed this one exponents that turn it into the published, refuted Wiseman-Domany reading. They are `α_pure`/`ν_pure` and untyped now, which is what `WeinribHalperinExponent` already does with `ν_dis` two dozen lines below. No reachability is lost: exponents are not `AbstractQuantity`, so nothing could see them anyway. The specialized `_solve` returned `-1/dlogδTc_dlogL` unguarded. At a flat slope, the case the docstring itself discusses, that is an infinity whose SIGN comes from whether the caller wrote `0.0` or `-0.0`, and feeding it back passes at `atol=0`. The generic inverter this override bypasses guards exactly that; this one now does too. The test asserting `solve(:ν) ≈ 1/ρ` compared the hand-written inverse to itself. It round-trips through `check` against the declared residual now, and the cross-check against the source's separately fitted `1/ν = 1.467(5)` is stated in units of the combined error, where it sits at 1.9. Adds the positive `check` cases the testset had none of, the pole refusal, and the `α = 0` vacuity, which is documented rather than refused because `α = 0` is the marginal case this literature is about and not a bad input. Prose: the sources tie "does not improve the statistics" to L, not to sample count, and averaging over more realisations does help; that clause is gone. Co-Authored-By: Claude Opus 5 (1M context) --- src/AbstractQAtlas.jl | 8 +++++ src/relations/quantity_links.jl | 5 +++ src/relations/scaling.jl | 51 +++++++++++++++------------ test/relations/test_scaling.jl | 61 +++++++++++++++++++++++++-------- 4 files changed, 88 insertions(+), 37 deletions(-) diff --git a/src/AbstractQAtlas.jl b/src/AbstractQAtlas.jl index ce4781d..0dfe6d5 100644 --- a/src/AbstractQAtlas.jl +++ b/src/AbstractQAtlas.jl @@ -95,6 +95,7 @@ include("relations/interface.jl") "Equilibrium statistical mechanics: occupation statistics → ensembles → thermodynamic potentials → response, FDT & stability." module StatisticalMechanics using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's using ..AbstractQAtlas: _beta # β-or-T normalization (occupation functions) import ..AbstractQAtlas: _solve # extended for a non-affine variable (FreeEnergyFromZ:Z) include("relations/thermodynamic.jl") @@ -107,6 +108,7 @@ end "Critical phenomena and conformal field theory: scaling laws, finite-size scaling, Cardy, the c-theorem." module Criticality using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's import ..AbstractQAtlas: _solve # extended for a non-affine variable (PseudocriticalWidthScaling:ν) include("relations/scaling.jl") include("relations/cft.jl") @@ -115,6 +117,7 @@ end "Correlations, Green's functions and response: the spectral graph (Dyson, A=−ImG/π), the Keldysh RAK structure + fluctuation–dissipation, Wick / Bloch–De Dominicis (Gaussian factorization), Kramers–Kronig, detailed balance." module Correlations using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's using LinearAlgebra: inv, det include("relations/spectral.jl") include("relations/keldysh.jl") @@ -124,12 +127,14 @@ end "Transport: DC/AC conductivity, thermal & thermoelectric coefficients, the Hall family, Onsager, Wiedemann–Franz, optical sum rule, Johnson–Nyquist." module Transport using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's include("relations/transport.jl") end "Quantum information & entanglement: the entropy zoo, its inequalities, multipartite entanglement, measurement and topological entanglement entropy." module QuantumInformation using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's using ExperimentalAPI: @experimental include("relations/entanglement.jl") end @@ -137,18 +142,21 @@ end "Quantum-mechanical foundations & bounds: virial, Hellmann–Feynman, Ehrenfest, zero-variance eigenstate, the uncertainty relation and the Lieb–Robinson bound." module QuantumFoundations using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's include("relations/quantum.jl") end "Universal bounds stated against a fetched bounding value: Bell (CHSH, Mermin), chaos (MSS), speed limits, fast scrambling, BB84 key rate, optimal cloning, Bekenstein." module UniversalBounds using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's include("relations/bounds.jl") end "Topological invariants: Chern number, TKNN, winding, bulk–boundary correspondence." module Topology using ..AbstractQAtlas + import ..AbstractQAtlas: fetch # unexported, so bare `fetch` here would be Base's using LinearAlgebra: det, eigen, Hermitian include("relations/topology.jl") end diff --git a/src/relations/quantity_links.jl b/src/relations/quantity_links.jl index 2108ead..3534cb8 100644 --- a/src/relations/quantity_links.jl +++ b/src/relations/quantity_links.jl @@ -147,6 +147,11 @@ quantities(::HolevoMixingBound) = (VonNeumannEntropy,) quantities(::RenyiMonotonicity) = (RenyiEntropy,) quantities(::RelativeEntropyNonNegativity) = (RelativeEntropy,) +# The three self-averaging relations name no quantity: `R_X = Var(X)/[X]²` leaves `X` +# generic, and a width is not a mean, so neither `Typical` nor `DisorderAveraged` +# names it and `CriticalTemperature` would name the thing `δT_c` is the spread of. +# Same abstention as the two below, for the same reason. + # ── Disorder statistics ── # Both are fully symbol-keyed: `X_typ`/`X_avg` and `F_quenched`/`F_annealed` are # REDUCTIONS over an ensemble, and no single quantity names a reduction, so there diff --git a/src/relations/scaling.jl b/src/relations/scaling.jl index 3a075fd..5cf7094 100644 --- a/src/relations/scaling.jl +++ b/src/relations/scaling.jl @@ -705,20 +705,18 @@ autocorrelation `G(t) ∼ exp(−A|ln t|^d)` of Eq. (A.34) (= Eq. (9.7), §9.1.3 """ StrongSelfAveraging <: AbstractRelation -`R_X ∼ (L/ξ)^{-d}` for `L ≫ ξ`, so `d ln R_X/d ln L = -d`, where +`R_X ∼ (L/ξ)^{-d}` for `L ≫ ξ`, so `d ln R_X/d ln(L/ξ) = -d`, where `R_X = Var(X)/[X]²` over an ensemble of samples (Aharony & Harris, [AharonyHarris1996](@cite)). -Off criticality only. At criticality the slope is `(α/ν)_pure` if the randomness -is irrelevant ([`CriticalSelfAveraging`](@ref)) and ZERO if it is relevant, the -distribution then approaching a universal non-Gaussian of `L`-independent width. -So the measured slope says which of the three a calculation is in, and a fixed -point where it does not decay is one where more samples at larger `L` do not -improve the statistics. +Off criticality only, and the abscissa is `L/ξ` rather than `L`, which is also +what keeps this slope a different variable from +[`CriticalSelfAveraging`](@ref)'s: one number cannot be both, and a graph that +shared the name would read an off-critical law off a critical measurement. -Variables: `dlogR_dlogL` (caller-computed), `d`. +Variables: `dlogR_dlogLξ` (caller-computed), `d`. """ -@relation :scaling StrongSelfAveraging(dlogR_dlogL, d::SpatialDimension) = dlogR_dlogL + d +@relation :scaling StrongSelfAveraging(dlogR_dlogLξ, d::SpatialDimension) = dlogR_dlogLξ + d """ CriticalSelfAveraging <: AbstractRelation @@ -726,18 +724,20 @@ Variables: `dlogR_dlogL` (caller-computed), `d`. `R_X ∼ L^{α/ν}` at criticality when the randomness is IRRELEVANT, so `d ln R_X/d ln L = α/ν` ([AharonyHarris1996](@cite)). -The exponents are the PURE system's, which is what makes this the irrelevant- -randomness statement rather than a general one. Wiseman and Domany conjectured -the same form with the random fixed point's exponents; [AharonyHarris1996](@cite) -and the simulations in [WisemanDomany1998](@cite) both contradict it, the width -going to a constant there instead. Reading this relation with random-fixed-point -exponents is that refuted conjecture. +The exponents are the PURE system's, so they are `α_pure` and `ν_pure` and stay +untyped: [`SpecificHeatExponent`](@ref) elsewhere in the registry means the system +under study, and at a random fixed point that is a different number. Wiseman and +Domany conjectured this form with the random fixed point's exponents; +[AharonyHarris1996](@cite) and the simulations in [WisemanDomany1998](@cite) both +contradict it, the width going to a constant there instead. -Variables: `dlogR_dlogL` (caller-computed), `α`, `ν`. +At `α = 0`, the marginal case this literature is largely about, the residual does +not depend on `ν` and a passing check says nothing about it. + +Variables: `dlogR_dlogL` (caller-computed), `α_pure`, `ν_pure`. """ -@relation :scaling CriticalSelfAveraging( - dlogR_dlogL, α::SpecificHeatExponent, ν::CorrelationLengthExponent -) = dlogR_dlogL - α / ν +@relation :scaling CriticalSelfAveraging(dlogR_dlogL, α_pure, ν_pure) = + dlogR_dlogL - α_pure / ν_pure """ PseudocriticalWidthScaling <: AbstractRelation @@ -747,17 +747,24 @@ the sample-to-sample distribution of pseudocritical temperatures ([WisemanDomany1998](@cite), measured directly). Not `L^{-d/2}`, the central-limit answer, which holds only where the Harris -criterion does. The two are close and the measurement has to be good enough to +criterion does. The two are close and the measurement has to be good enough to tell them apart: the site-dilute Ising model in `d = 3` gives `1.449(8)` against -`d/2 = 1.5`, six of its own errors away, and `1/ν = 1.47`. +`d/2 = 1.5`, six of its own errors away, agreeing with that model's separately +fitted `1/ν = 1.467(5)`. Variables: `dlogδTc_dlogL` (caller-computed), `ν`. """ @relation :scaling PseudocriticalWidthScaling(dlogδTc_dlogL, ν::CorrelationLengthExponent) = dlogδTc_dlogL + 1 / ν -# `1/ν` is not affine in `ν`, so the generic three-probe inverter refuses it. +# `1/ν` is not affine in `ν`, so the generic three-probe inverter refuses it. That +# inverter also refuses a non-finite probe; this one has to refuse its own pole, or +# a flat slope returns an infinity whose SIGN comes from the caller's zero. function _solve(::PseudocriticalWidthScaling, ::Val{:ν}; dlogδTc_dlogL, _extra...) + iszero(dlogδTc_dlogL) && error( + "solve: PseudocriticalWidthScaling has no ν at dlogδTc_dlogL = 0. A width " * + "that does not shift with L does not identify a correlation-length exponent.", + ) return -1 / dlogδTc_dlogL end diff --git a/test/relations/test_scaling.jl b/test/relations/test_scaling.jl index 8a7f73c..053fab1 100644 --- a/test/relations/test_scaling.jl +++ b/test/relations/test_scaling.jl @@ -6,7 +6,7 @@ # pins the no-float-promotion contract. using AbstractQAtlas -using AbstractQAtlas: residual, check, solve +using AbstractQAtlas: residual, check, solve, derive using LinearAlgebra const ISING2D = (α=0//1, β=1//8, γ=7//4, δ=15//1, ν=1//1, η=1//4) @@ -690,27 +690,58 @@ end # `R_X = Var(X)/[X]²` over an ensemble of samples, which is what a disorder # calculation already produces per size. d, α, ν = 3, -0.1, 0.7 - @test solve(StrongSelfAveraging(), Val(:d); dlogR_dlogL=(-d)) ≈ d - @test solve(CriticalSelfAveraging(), Val(:α); dlogR_dlogL=α / ν, ν=ν) ≈ α - @test !check(StrongSelfAveraging(); dlogR_dlogL=(+d), d=d, atol=1e-9) # sign - @test !check(CriticalSelfAveraging(); dlogR_dlogL=(-α / ν), α=α, ν=ν, atol=1e-9) + @test solve(StrongSelfAveraging(), Val(:d); dlogR_dlogLξ=(-d)) ≈ d + @test solve(CriticalSelfAveraging(), Val(:α_pure); dlogR_dlogL=α / ν, ν_pure=ν) ≈ α + @test check(StrongSelfAveraging(); dlogR_dlogLξ=(-d), d=d, atol=1e-12) + @test check(CriticalSelfAveraging(); dlogR_dlogL=α / ν, α_pure=α, ν_pure=ν, atol=1e-12) + @test !check(StrongSelfAveraging(); dlogR_dlogLξ=(+d), d=d, atol=1e-9) # sign + @test !check( + CriticalSelfAveraging(); dlogR_dlogL=(-α / ν), α_pure=α, ν_pure=ν, atol=1e-9 + ) + + # Relevant randomness means no decay at all, so both decay laws are refused. + @test !check(StrongSelfAveraging(); dlogR_dlogLξ=0.0, d=d, atol=1e-6) + @test !check(CriticalSelfAveraging(); dlogR_dlogL=0.0, α_pure=α, ν_pure=ν, atol=1e-6) - # Relevant randomness means no decay at all, so the off-critical law is refused - # rather than fitted: a fixed point where more samples at larger L do not help. - @test !check(StrongSelfAveraging(); dlogR_dlogL=0.0, d=d, atol=1e-6) - @test !check(CriticalSelfAveraging(); dlogR_dlogL=0.0, α=α, ν=ν, atol=1e-6) + # The two abscissas are different variables and must stay so. Sharing the name let + # an off-critical law be read off a critical slope and, through `d`, put a wrong ν + # on the far side of Josephson with no error. + @test isdisjoint(variables(StrongSelfAveraging()), variables(CriticalSelfAveraging())) + @test_throws "not reachable" derive(:ν; dlogR_dlogL=α / ν, α=α) + + # At α = 0 the residual does not depend on ν, so a pass says nothing about it. + # Documented rather than refused: α = 0 is the marginal case, not a bad input. + @test all( + x -> check(CriticalSelfAveraging(); dlogR_dlogL=0.0, α_pure=0.0, ν_pure=x, atol=0), + (0.001, 0.63, 999.0), + ) # `δT_c ∼ L^{-1/ν}`, not `L^{-d/2}`. The site-dilute Ising model in d = 3 measures # 1.449(8), which is six of its own errors from d/2 = 1.5, so the discriminator - # discriminates; asserted as the ratio because the two are only 2% apart. + # discriminates. Asserted in units of that error, the two being 3.4% apart. ρ, σρ = 1.449, 0.008 @test abs(ρ - d / 2) / σρ > 5 - @test solve(PseudocriticalWidthScaling(), Val(:ν); dlogδTc_dlogL=(-ρ)) ≈ 1 / ρ - @test isapprox(1 / ρ, 0.69; atol=0.01) # and lands on that model's own ν - @test !check(PseudocriticalWidthScaling(); dlogδTc_dlogL=(-d / 2), ν=1 / ρ, atol=1e-3) + # Round-tripped through `check` rather than compared to the hand-written inverse, + # which would be that formula against itself. + ν_solved = solve(PseudocriticalWidthScaling(), Val(:ν); dlogδTc_dlogL=(-ρ)) + @test check(PseudocriticalWidthScaling(); dlogδTc_dlogL=(-ρ), ν=ν_solved, atol=1e-12) + # Not a test of this package: a guard on the two numbers the docstring quotes, so + # editing one without the other is caught. They are separate measurement channels + # on the same model and agree at 1.9 of their combined error. + @test abs(ρ - 1.467) < 2.5 * sqrt(σρ^2 + 0.005^2) + @test !check( + PseudocriticalWidthScaling(); dlogδTc_dlogL=(-d / 2), ν=ν_solved, atol=1e-3 + ) + + # A flat width identifies no exponent, so the specialized inverse refuses its own + # pole rather than returning an infinity signed by the caller's zero. + @test_throws "no ν at" solve(PseudocriticalWidthScaling(), Val(:ν); dlogδTc_dlogL=0.0) + @test_throws "no ν at" solve(PseudocriticalWidthScaling(), Val(:ν); dlogδTc_dlogL=-0.0) @test variable_types(StrongSelfAveraging()) == (SpatialDimension,) - @test variable_types(CriticalSelfAveraging()) == - (SpecificHeatExponent, CorrelationLengthExponent) @test variable_types(PseudocriticalWidthScaling()) == (CorrelationLengthExponent,) + # Untyped on purpose: these are the PURE system's exponents, and the registry's + # exponent types mean the system under study. `WeinribHalperinExponent` leaves + # `ν_dis` bare for the same reason. + @test isempty(variable_types(CriticalSelfAveraging())) end From e0a113a3ba36294fa2d9bbd9d10ab3d88be8be5f Mon Sep 17 00:00:00 2001 From: sotashimozono Date: Mon, 14 Sep 2026 16:32:24 +0000 Subject: [PATCH 4/4] Import fetch into every physics submodule before it is needed `fetch` is deliberately unexported, so inside a submodule a bare `fetch` resolves to Base's. A future method defined there without this import would not be inert like the `_solve` one was; it would be added to `Base.fetch`, invisible to the seam every atlas dispatches on. No submodule defines or uses `fetch` today, so this is a no-op that stops being one silently. Co-Authored-By: Claude Opus 5 (1M context)