Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "AbstractQAtlas"
uuid = "dcea2817-62f8-4a75-b498-1b50a9ed1e4d"
version = "0.7.11"
version = "0.7.12"
authors = ["sota shimozono <shimozono-sota631@g.ecc.u-tokyo.ac.jp>"]

[deps]
Expand Down
16 changes: 15 additions & 1 deletion src/core/quantities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ struct ParticleNumber <: AbstractQuantity end
export ParticleNumber

"""
LatentHeat() <: AbstractQuantity
LatentHeat() <: AbstractThermalPotential

The latent heat `L = T ΔS` of a first-order transition — the entropy
jump across the phase boundary times the temperature. Enters the
Expand Down Expand Up @@ -277,6 +277,20 @@ The potential-energy expectation `⟨V⟩` — the `V` of the virial theorem
struct PotentialEnergy <: AbstractThermalPotential end
export PotentialEnergy

"""
Force() <: AbstractQuantity

The mean force `⟨F⟩ = −⟨∂V/∂x⟩`, which [`EhrenfestMomentum`](@ref) equates to
`d⟨p⟩/dt`.

Not the free energy that wears the same letter in [`FreeEnergyLegendre`](@ref),
nor the cloning fidelity in [`CloningFidelityBound`](@ref): three quantities
share `F` across the registry, and the type is what keeps them apart when a bag
is read rather than a symbol.
"""
struct Force <: AbstractQuantity end
export Force

"""
EnergyVariance() <: AbstractQuantity

Expand Down
49 changes: 42 additions & 7 deletions src/core/relation_variables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ export AbstractExponent
# touching the others, since only the thirteen `:scaling` relations are annotated.

"""
SpecificHeatExponent() <: AbstractQuantity
SpecificHeatExponent() <: AbstractExponent

`α`, the specific heat's divergence at a critical point, `c ∼ |t|^{-α}`.
"""
struct SpecificHeatExponent <: AbstractExponent end
export SpecificHeatExponent

"""
OrderParameterExponent() <: AbstractQuantity
OrderParameterExponent() <: AbstractExponent

`β`, the order parameter's vanishing, `m ∼ (-t)^β`. Not
[`InverseTemperature`](@ref), which wears the same letter across most of this
Expand All @@ -79,23 +79,23 @@ struct OrderParameterExponent <: AbstractExponent end
export OrderParameterExponent

"""
SusceptibilityExponent() <: AbstractQuantity
SusceptibilityExponent() <: AbstractExponent

`γ`, the susceptibility's divergence, `χ ∼ |t|^{-γ}`.
"""
struct SusceptibilityExponent <: AbstractExponent end
export SusceptibilityExponent

"""
CriticalIsothermExponent() <: AbstractQuantity
CriticalIsothermExponent() <: AbstractExponent

`δ`, the critical isotherm's shape, `m ∼ h^{1/δ}` at `t = 0`.
"""
struct CriticalIsothermExponent <: AbstractExponent end
export CriticalIsothermExponent

"""
CorrelationLengthExponent() <: AbstractQuantity
CorrelationLengthExponent() <: AbstractExponent

`ν`, the correlation length's divergence, `ξ ∼ |t|^{-ν}`. The exponent, where
[`CorrelationLength`](@ref) is the length itself.
Expand All @@ -104,15 +104,15 @@ struct CorrelationLengthExponent <: AbstractExponent end
export CorrelationLengthExponent

"""
AnomalousDimension() <: AbstractQuantity
AnomalousDimension() <: AbstractExponent

`η`, the correlation function's decay at criticality, `G(r) ∼ r^{-(d-2+η)}`.
"""
struct AnomalousDimension <: AbstractExponent end
export AnomalousDimension

"""
LargeSpinExponent() <: AbstractQuantity
LargeSpinExponent() <: AbstractExponent

`ζ` of the large-spin fixed point, where the effective moment GROWS under
renormalization ([IgloiMonthus2005](@cite), §A.5). A random-walk argument on the
Expand All @@ -125,6 +125,41 @@ occupation in `(0, 1)`.
struct LargeSpinExponent <: AbstractExponent end
export LargeSpinExponent

"""
LargeSpinMomentExponent() <: AbstractExponent

`κ` of the large-spin fixed point, by which the effective moment grows as the
energy scale falls, `S_eff ∼ Ω^{-κ}` ([IgloiMonthus2005](@cite), §A.5, with §8.2
the 1D instance). Tied to the other two by [`LargeSpinMoment`](@ref),
`κ = dζ/z`, which is how a measured `κ` gives the dynamical exponent when `ζ` is
known.

Named for its fixed point rather than for the physics, like
[`LargeSpinExponent`](@ref) beside it: the infinite-disorder fixed point has a
moment exponent too, [`ActivatedMomentExponent`](@ref), and "effective moment"
alone does not say which.

Not the thermal conductivity that wears the same letter in
[`WiedemannFranz`](@ref) and [`ThermoelectricFigureOfMerit`](@ref).
"""
struct LargeSpinMomentExponent <: AbstractExponent end
export LargeSpinMomentExponent

"""
ActivatedMomentExponent() <: AbstractExponent

`φ` of the infinite-disorder fixed point, where the moment grows in the logarithm
of the energy scale rather than a power of it, `μ ∼ |ln Ω|^φ`
([IgloiMonthus2005](@cite), §A.3). Pinned by [`ActivatedMomentGrowth`](@ref),
`φψ = d - x_m`.

The activated twin of [`LargeSpinMomentExponent`](@ref). It is bookkeeping
rather than a separately measured number, which is a statement about how it is
obtained and not about whether it is a quantity of its own.
"""
struct ActivatedMomentExponent <: AbstractExponent end
export ActivatedMomentExponent

"""
RelationVariable

Expand Down
6 changes: 6 additions & 0 deletions src/relations/entanglement.jl
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ than `L/π`, and an open chain has a boundary entropy `ln g` (Affleck & Ludwig)
that a ring does not. `c₁` is the same constant as in the ring, entering
halved, so reading one geometry's data with the other's formula misses all
three.

`ln g` and `c₁` reach the residual only as the sum `ln g + c₁/2`, and neither
depends on `ℓ`, so open-chain data cannot separate them at any number of block
sizes: a caller who fits `c₁` against this relation learns `2 ln g + c₁` and
nothing more. `c₁` has to come from the ring or the infinite chain, which is
what "the same constant as in the ring" is for.
"""
@relation :entanglement CFTEntanglementOBC(S, c::CentralCharge, L, ℓ, c₁, ln_g) = begin
_require_block(:CFTEntanglementOBC, L, ℓ)
Expand Down
10 changes: 5 additions & 5 deletions src/relations/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -952,16 +952,16 @@ function relation_report(data::NamedTuple; atol=0, domain::Union{Nothing,Symbol}
end
export relation_report

# The shared "all applicable relations passed, and at least one applied" rule in one
# place, so the NamedTuple and Bag `check_all` methods cannot drift on "empty ⇒ false".
_all_passed(report) = !isempty(report) && all(row -> row.pass, report)

"""
check_all(data::NamedTuple; atol=0, domain=nothing) -> Bool

`true` iff every applicable relation passes on `data` — and at least one
`true` iff every applicable relation passes on `data`, and at least one
relation applies: an empty match is `false`, never a silent green.
"""
# the shared "all applicable relations passed, and at least one applied" rule — one
# place, so the NamedTuple and Bag `check_all` methods can't drift on "empty ⇒ false".
_all_passed(report) = !isempty(report) && all(row -> row.pass, report)

function check_all(data::NamedTuple; atol=0, domain::Union{Nothing,Symbol}=nothing)
return _all_passed(relation_report(data; atol=atol, domain=domain))
end
Expand Down
7 changes: 4 additions & 3 deletions src/relations/quantity_links.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ also_constrains(::SusceptibilityResponse) = (Magnetization,) # fundamental: χ
also_constrains(::ParticleNumberResponse) = (GrandPotential,) # grand-canonical: N = −∂Ω/∂μ
also_constrains(::StaticFromDynamicalStructureFactor) = (DynamicalStructureFactor,) # Sq = ∫S(q,ω)dω/2π (supplied)
also_constrains(::ChernFromBerryCurvature) = (BerryCurvature,) # topology: C = ∫Ω d²k/2π (supplied integral)
also_constrains(::CFTEntanglementInfinite) = (VonNeumannEntropy,) # the L→∞ sibling
also_constrains(::CFTEntanglementSlope) = (VonNeumannEntropy,) # entanglement: dS/d(ln ℓ) (supplied derivative)
also_constrains(::CFTEntanglementPBC) = (VonNeumannEntropy,) # entanglement: S(ℓ) on a ring
also_constrains(::CFTEntanglementOBC) = (VonNeumannEntropy,) # entanglement: S(ℓ) at an open end
Expand Down Expand Up @@ -147,9 +148,9 @@ quantities(::TypicalBelowAverage) = (Typical, DisorderAveraged)
quantities(::AnnealedFreeEnergyBound) = (FreeEnergy,)

# ── Quantum-mechanical foundations ──
# VirialTheorem is type-keyed (quantum.jl), `quantities` auto-derived. The Ehrenfest /
# Hellmann–Feynman / uncertainty / Lieb–Robinson relations stay symbol-keyed (generic
# operators / derivatives). EnergyVarianceEigenstate stays symbol-keyed — its subject
# VirialTheorem and EhrenfestMomentum are type-keyed (quantum.jl), `quantities`
# auto-derived, as is LiebRobinsonBound. EhrenfestPosition, Hellmann-Feynman and the
# uncertainty relations stay symbol-keyed (generic operators and derivatives). EnergyVarianceEigenstate stays symbol-keyed — its subject
# EnergyVariance enters only through the moment combination ⟨H²⟩ − E², not a slot.
quantities(::EnergyVarianceEigenstate) = (EnergyVariance,)

Expand Down
4 changes: 2 additions & 2 deletions src/relations/quantum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ the mean momentum obeys the classical force law,
`d⟨p⟩/dt = −⟨∂V/∂x⟩ = ⟨F⟩`,

the quantum counterpart of Newton's second law. Variables: `dp_dt`,
`F` = `⟨F⟩`.
and `F` = `⟨F⟩` as a [`Force`](@ref).
"""
@relation :quantum EhrenfestMomentum(dp_dt, F) = dp_dt - F
@relation :quantum EhrenfestMomentum(dp_dt, F::Force) = dp_dt - F

"""
EnergyVarianceEigenstate <: AbstractRelation
Expand Down
12 changes: 11 additions & 1 deletion src/relations/region_entropy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,17 @@ The central charge is read from the bag, as `CentralCharge` and, when a random
critical chain is being checked, `EffectiveCentralCharge`; the latter also needs
`f`, the scaling function, and its own constant `c₁′`. The non-universal
constants are arguments because they are not quantities: `c₁`, and `ln_g` for
the boundary entropy an open chain carries.
the boundary entropy an open chain carries. `ln_g` and `c₁` enter
[`CFTEntanglementOBC`](@ref) only as a sum, so no number of block sizes separates
them; see that relation.

A single region is usually not evidence that `c` or `c̃` is right. The four
logarithmic forms each carry one free constant, so with one row that constant can
be chosen after the fact to zero the residual for ANY central charge, and what
passes is the mutual consistency of the triple rather than the charge. Two or
more block sizes are what make the charge falsifiable there. The exceptions are
the forms with no constant to spend, [`OffCriticalEntanglementSaturation`](@ref)
and the slope relations, where one row does constrain the charge.

A region whose sites are not integers is skipped, having no adjacency to count
cuts with. A region of integer sites lying off the chain `bc` declares is not
Expand Down
10 changes: 8 additions & 2 deletions src/relations/scaling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,10 @@ for the RTFIC is that review's Eq. (3.18), §3.5, reached by a different route.
Variables: `φ`, `d`, `x_m`, `ψ`.
"""
@relation :scaling ActivatedMomentGrowth(
φ, d::SpatialDimension, x_m::ScalingDimension, ψ::ActivatedExponent
φ::ActivatedMomentExponent,
d::SpatialDimension,
x_m::ScalingDimension,
ψ::ActivatedExponent,
) = φ * ψ - (d - x_m)

# ─── Appendix A: the four scaling types of a random system ───────────────
Expand Down Expand Up @@ -577,7 +580,10 @@ instance is Eqs. (8.6)-(8.8), §8.2, where `ζ = 1/2` and `κ = 0.22(1)` is
measured, giving `z = 1/(2κ)`.
"""
@relation :scaling LargeSpinMoment(
κ, d::SpatialDimension, ζ::LargeSpinExponent, z::DynamicalExponent
κ::LargeSpinMomentExponent,
d::SpatialDimension,
ζ::LargeSpinExponent,
z::DynamicalExponent,
) = κ * z - d * ζ

"""
Expand Down
17 changes: 17 additions & 0 deletions test/relations/test_derivation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@ end
@test CorrelationMatrixEigenvalue in variable_types(EntanglementSpectrumCorrelation())
@test LargeSpinExponent !== CorrelationMatrixEigenvalue

# `κ` in the same relation shares its letter with the thermal conductivity in two
# transport relations. As with `:F`, the name-keyed node stays shared; the type
# is what separates them once a bag is involved.
@test LargeSpinMomentExponent in variable_types(LargeSpinMoment())
@test VariableKey(LargeSpinMomentExponent) != VariableKey(ThermalConductivity)
@test ThermalConductivity in
Set(AbstractQAtlas._family(T) for T in variable_types(WiedemannFranz()))

# Each fixed point has its own moment exponent and they are not the same number:
# a power of Ω at the large-spin one, a power of |ln Ω| at the infinite-disorder
# one. Naming either of them for the physics alone would take the other's name.
@test ActivatedMomentExponent in variable_types(ActivatedMomentGrowth())
@test LargeSpinMomentExponent !== ActivatedMomentExponent
@test isempty(
filter(t -> t === nothing, last.(variable_slots(ActivatedMomentGrowth())))
)

# And the exemplar stays split.
@test OrderParameterExponent in variable_types(Rushbrooke())
@test !(InverseTemperature in variable_types(Rushbrooke()))
Expand Down
25 changes: 19 additions & 6 deletions test/relations/test_quantum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,19 @@ end
# The genuinely GENERIC quantum relations stay symbol-keyed, and should: their
# variables do not name quantities. `RobertsonUncertainty(ΔA, ΔB, comm)` is about
# two arbitrary observables; `MandelstamTammBound(τ, ΔE)` about a time and an
# energy spread; `EhrenfestMomentum`/`Position` about expectation values of
# whichever operator. Typing those would need quantities that do not exist and
# arguably should not.
# energy spread; `EhrenfestPosition` about `d⟨x⟩/dt = ⟨p⟩/m` for whichever
# operator. Typing those would need quantities that do not exist and arguably
# should not.
#
# `LiebRobinsonBound` used to be in this list and is not any more. Its `v_LR` is
# not a generic symbol — it IS a named quantity, `LiebRobinsonVelocity` — so
# typing it makes the inequality discoverable from the quantity
# not a generic symbol, it IS a named quantity, `LiebRobinsonVelocity`, so typing
# it makes the inequality discoverable from the quantity
# (`relations_constraining`), which is the whole point of the type-keyed front
# door. Its other slot, `v`, stays untyped: that is a measured information
# velocity, and nothing names it yet.
@test all(
r -> isempty(variable_types(r)),
(
EhrenfestMomentum(),
EhrenfestPosition(),
HellmannFeynman(),
RobertsonUncertainty(),
Expand All @@ -131,6 +130,20 @@ end
EnergyVarianceEigenstate(),
),
)

# `⟨F⟩` is not a generic expectation value, it is a force. Four relations write
# `F` and they mean three things: the free energy in `FreeEnergyLegendre` and
# `GrandPotentialLegendre`, the cloning fidelity in `CloningFidelityBound`, and
# this. The name-keyed graph still puts all four at one node and always will,
# `variables` being deliberately unchanged by typing; what a type buys is that
# the bag-keyed graph does not. `CloningFidelityBound`'s `F` stays bare under the
# `@bound` convention that types the ceiling and not the measured side.
@test variable_types(EhrenfestMomentum()) == (Force,)
@test quantities(EhrenfestMomentum()) == (Force,)
@test VariableKey(Force) != VariableKey(FreeEnergy)
@test check(EhrenfestMomentum(), bag(Force => -0.8); dp_dt=-0.8, atol=1e-12)
@test !check(EhrenfestMomentum(), bag(Force => 0.8); dp_dt=-0.8, atol=1e-9)
@test EhrenfestMomentum() in relations_constraining(Force)
end

@testset "LiebRobinsonBound is keyed on LiebRobinsonVelocity" begin
Expand Down
32 changes: 29 additions & 3 deletions test/relations/test_region_entropy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,36 @@ end
# the distinction the cut count exists for.
at_end = bag(entanglement_entropy(Region(1:16...)) => 0.0, CentralCharge => c)
in_bulk = bag(entanglement_entropy(Region(20:35...)) => 0.0, CentralCharge => c)
@test length(finite_size_entropy_report(at_end, OBC(N); c₁=c₁, atol=1e-12)) == 1
@test only(finite_size_entropy_report(at_end, OBC(N); c₁=c₁, atol=1e-12)).relation isa
@test length(finite_size_entropy_report(at_end, OBC(N); c₁=c₁, ln_g=0.0, atol=1e-12)) ==
1
@test only(finite_size_entropy_report(at_end, OBC(N); c₁=c₁, ln_g=0.0, atol=1e-12)).relation isa
CFTEntanglementOBC
@test isempty(finite_size_entropy_report(in_bulk, OBC(N); c₁=c₁, atol=1e-12))
@test isempty(finite_size_entropy_report(in_bulk, OBC(N); c₁=c₁, ln_g=0.0, atol=1e-12))

# Pinned because a report that passes on both readings of the same data must not
# be read as having checked either one.
lng, N_ = 0.3, 256
obc(ℓ) = (c / 6) * log((2 * N_ / π) * sin(π * ℓ / N_)) + lng + c₁ / 2
two_blocks = bag(
entanglement_entropy(Region(1:16...)) => obc(16),
entanglement_entropy(Region(1:64...)) => obc(64),
CentralCharge => c,
)
truth = finite_size_entropy_report(two_blocks, OBC(N_); c₁=c₁, ln_g=lng, atol=1e-12)
shifted = finite_size_entropy_report(
two_blocks, OBC(N_); c₁=2 * lng + c₁, ln_g=0.0, atol=1e-12
)
@test length(truth) == 2
@test all(r -> r.pass, truth)
@test all(r -> r.pass, shifted)
@test [r.residual for r in truth] ≈ [r.residual for r in shifted] atol = 1e-14

# And the negative control the pair needs: a reading that does NOT preserve the
# sum is a different physical claim and is refused. Without this the block shows
# that two equivalent readings agree and never that an inequivalent one does not.
off = finite_size_entropy_report(two_blocks, OBC(N_); c₁=c₁, ln_g=lng + 0.01, atol=1e-9)
@test !any(r -> r.pass, off)
@test all(r -> isapprox(r.residual, -0.01; atol=1e-12), off)

# An infinite chain has no L to supply and gets Eq. (4).
binf = bag(
Expand Down
Loading