Skip to content

Write down the degeneracy, and name the last two bare slots - #155

Merged
sotashimozono merged 4 commits into
mainfrom
feat/boundary-entropy-degeneracy
Sep 14, 2026
Merged

sotashimozono merged 4 commits into
mainfrom
feat/boundary-entropy-degeneracy

Conversation

@sotashimozono

@sotashimozono sotashimozono commented Sep 14, 2026 •

Copy link
Copy Markdown
Member

Two things the review of #154 turned up, plus the naming that was waiting on it.

The degeneracy an open chain cannot see

CFTEntanglementOBC is

S - (c/6) ln[2 (L/π) sin(πℓ/L)] - ln g - c₁/2

and ln g and c₁/2 reach the residual only as that sum, neither depending on
ℓ. 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 else. Worse
than the scaling-function factor c₁′ absorbed in #154, where the normalisation
was at least independently checkable.

Verified: with c = 1/2, L = 256, true (ln g, c₁) = (0.3, 0.4785), the
readings (0.3, 0.4785) and (0.0, 1.0785) give residuals agreeing to machine
precision at every block size.

Nothing about the behaviour changes. I first removed the ln_g=0 default so the
caller had to state it, then took that back: trace the caller through and they get
an error, type ln_g=0.0, and pass at every size with the same wrong answer. It
was a signal shaped like a check, and it charged a breaking bump for it.

What is left costs nothing and is what helps. The relation's docstring states the
degeneracy. A test runs the same data under both readings and asserts the
residuals agree, so a report that passes on both cannot later be read as having
checked either. The three call sites that were taking the default silently now
pass ln_g explicitly, which says what they assume without requiring it of
anyone else.

Also recorded, in the report's docstring: a single region is not evidence about
c or c̃. Each closed form has one free constant, so with one row it can be
chosen after the fact to zero the residual for any charge, and what passes is the
triple's mutual consistency. The docstring's own worked example had one region.

The last two bare slots

F is written by three relations meaning three different things: the free energy
in FreeEnergyLegendre, the cloning fidelity in CloningFidelityBound, and the
mean force in EhrenfestMomentum. The first two were typed already, so the third
was the last F that could still meet them on a name. It is a Force now.

κ is the same shape: the thermal conductivity in WiedemannFranz and
ThermoelectricFigureOfMerit, both typed, and the large-spin moment exponent in
LargeSpinMoment, which was not. S_eff ∼ Ω^{-κ} makes it an exponent, so
EffectiveMomentExponent joins AbstractExponent beside LargeSpinExponent, the
other half of the same relation. Neither slot had a hand-written quantities, so
nothing had to be retired alongside.

A claim the code contradicted

All seven exponents moved to AbstractExponent in #153 still opened their own
docstrings with <: AbstractQuantity. The parent changed and the line
documenting it did not. No test could see that, since it is prose about a
supertype rather than a use of one.

Version

Patch. Nothing breaks.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

`CFTEntanglementOBC` is `S - (c/6)ln[2 chord] - ln g - c₁/2`, and `ln g` and
`c₁/2` reach the residual only as that sum, neither depending on `ℓ`. So open
chain data cannot separate them at any number of block sizes: a caller fitting
`c₁` against this relation learns `2 ln g + c₁` and nothing else. This is worse
than the scaling function `c₁′` absorbed, where more sizes at least helped.

`ln_g` defaulted to 0 in `finite_size_entropy_report`, which is not declining to
say, it is asserting a trivial boundary on the caller's behalf. A caller who
never measured `ln g` and fitted `c₁` from the open chain got a passing row at
every block size, with the boundary's physics sitting inside the constant they
call `c₁`. Three tests in this repo were taking that default without saying so.

The default is gone for an open chain; a ring never reads `ln_g` and is not
asked. The degeneracy itself cannot be fixed, so it is written into the
relation's docstring and pinned by a test that runs the same data under both
readings and asserts the residuals agree, since a report that passes on both must
not be read as having checked either.

Also records, in the report's docstring, that one region is not evidence about
`c` or `c̃`: with one row the free constant can be chosen after the fact to zero
the residual for any charge, so what passes is the triple's mutual consistency.
The docstring's own example had exactly one region.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📚 Docs preview: https://qatlashub.github.io/AbstractQAtlas.jl/previews/PR155/

(updates on each push to this PR)

… for

Removing it forced a keystroke and changed no outcome. A caller who never
measured `ln g` and fitted `c₁` from the open chain passed at every block size
before, and passes at every block size after typing `ln_g=0.0`. The refusal was
shaped like a check while being a signal, which is the thing this branch exists
to complain about, and it charged a breaking bump for it.

What is left costs nothing and is what actually helps: the degeneracy written
into `CFTEntanglementOBC`'s docstring, the test running the same data under both
readings and asserting the residuals agree, and the note that one region is not
evidence about a central charge. The three call sites now pass `ln_g` explicitly,
which says what they assume without requiring it of anyone else.

Back to a patch bump, since nothing breaks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

`F` is written by three relations meaning three different things: the free energy
in `FreeEnergyLegendre`, the cloning fidelity in `CloningFidelityBound`, and the
mean force in `EhrenfestMomentum`. The first two were typed already, so the third
was the last `F` that could still meet them on a name. It is a `Force` now.

`κ` is the same story: the thermal conductivity in `WiedemannFranz` and
`ThermoelectricFigureOfMerit`, both typed, and the large-spin moment exponent in
`LargeSpinMoment`, which was not. `S_eff ∼ Ω^{-κ}` makes it an exponent, so
`EffectiveMomentExponent` joins `AbstractExponent` beside `LargeSpinExponent`, the
other half of the same relation.

Neither had a hand-written `quantities`, so nothing had to be retired with them.

Separately: all seven exponents moved to `AbstractExponent` in #153 still had
`<: AbstractQuantity` in the first line of their own docstrings. The parent
changed and the line documenting it did not, which is a claim the code contradicts
and no test could see.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sotashimozono sotashimozono changed the title An open chain has to say what its boundary entropy is Write down the degeneracy, and name the last two bare slots Sep 14, 2026
The prose this branch added to write down two facts got three of its own facts
wrong, which is the same defect it exists to fix.

"Each closed form has one free constant" is false for
`OffCriticalEntanglementSaturation`: `S - ncuts (c/6) ln ξ` has no additive
constant at all, `ξ` and `c` both being read from the bag, so a single region
DOES constrain the charge there. Measured: residual 8.3e-5 at c = 0.4999 and
exactly zero at 0.5.

"Three relations write `F`" is four: `GrandPotentialLegendre` also types it
`FreeEnergy`. And "the other two were already typed" is wrong about
`CloningFidelityBound`, whose `F` is bare and stays bare under the `@bound`
convention that types the ceiling and not the measured side. That claim came
from reading `variable_types` as though it were keyed by the slot I cared about,
when it returns the set of types present.

"The last `F` that could still collide on the name-keyed side" has it backwards.
The name-keyed node stays shared and always will, `variables` being deliberately
unchanged by typing. What a type buys is that the bag-keyed graph separates them.

`φ` in `ActivatedMomentGrowth` was the third bare slot of this kind and the scope
missed it, because the criterion used was "the letter is typed elsewhere" rather
than the design doc's own "identity-bearing". It is the structural twin of `κ`:
the moment exponent of the other random fixed point, pinned by an identity among
otherwise-typed exponents, used in one relation. Typing it also makes the name
`EffectiveMomentExponent` wrong, since "effective moment" covers both fixed
points; `LargeSpinMomentExponent` and `ActivatedMomentExponent` name the fixed
point, as `LargeSpinExponent` beside them already does.

Pre-existing, same class: `LatentHeat`'s docstring claimed `AbstractQuantity`
against an `AbstractThermalPotential` struct. `check_all(::NamedTuple)`'s
docstring was severed from it by an intervening definition, so the symbol
resolved to the `Bag` method's docstring and the crossref gate, being
method-blind, passed. `CFTEntanglementInfinite` was missing from
`relations_constraining(VonNeumannEntropy)` while both its siblings were there.

The degeneracy test compared two equivalent readings and never an inequivalent
one, so it showed agreement without showing the check could disagree. A reading
off by 0.01 in `ln g` is now asserted to fail at every block size.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sotashimozono
sotashimozono merged commit 6555592 into main Sep 14, 2026
26 checks passed
@sotashimozono
sotashimozono deleted the feat/boundary-entropy-degeneracy branch September 14, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant