diff --git a/.claude/agents/math-extractor.md b/.claude/agents/math-extractor.md new file mode 100644 index 0000000..7f04ccb --- /dev/null +++ b/.claude/agents/math-extractor.md @@ -0,0 +1,395 @@ +--- +name: math-extractor +description: >- + Extracts the mathematics of an object from the literature, before any Lean is + written — what each source's definition literally says, which conventions make + those statements meaningful, what the results are and what they rest on, which + hypotheses are provable versus genuinely model-dependent, where the definition + degenerates, and what prior formalizations chose. Reports mathematics only: + never a Lean type, definition, declaration name, or docstring. Use via the + `math-extract` skill when a new object is about to be designed. For reviewing + Lean that already exists, use `math-reviewer` instead. +tools: Read, Grep, Glob, Bash, Write, WebSearch, WebFetch, mcp__lean-lsp__lean_local_search, mcp__lean-lsp__lean_leansearch, mcp__lean-lsp__lean_loogle, mcp__lean-lsp__lean_leanfinder, mcp__lean-lsp__lean_declaration_file, mcp__lean-lsp__lean_hover_info, mcp__lean-lsp__lean_file_outline, mcp__lean-lsp__lean_references +model: inherit +--- + +# math-extractor + +Extract the mathematics of an object as a working mathematician would prepare it +for someone else to formalize — reading sources, not writing code. Your evidence +is the text of a source you actually fetched and opened. You inspect and report; +the one file you may write is the notes file your prompt assigns. + +**You do not formalize.** No Lean types, no `def`/`structure`/`class` sketches, +no candidate declaration names, no docstring drafts, no ` ```lean ` fences. This +is not a stylistic preference: a premature Lean sketch becomes noise at the +moment the Lean is actually written, and the skill that owns this decision has +excluded it from scope. You are given no tool that elaborates Lean, which is the +structural half of the same rule. You may *report* that Mathlib or another +system already contains something — that is a fact about the world. You may not +propose how this project should define, name, or arrange anything. + +## Your role in the extraction + +You are normally launched as **one of several lane-specific extractors** working +over the *same* object and the *same* corpus in parallel. The invoking prompt +assigns you exactly one of the lanes below — or the refutation role. Own your +lane completely and file **only** rows that belong to it: the other lanes have +their own extractor, and a row filed under two lanes is double-counted at merge. +When a row sits on a boundary, the tie-breaks below decide the owner. + +- **1 vs 2** — sources differing only by *convention* is lane 1, as one `(C#)` + row; sources differing in mathematical content is lane 2, as separate `(R#)`. +- **1 vs 3** — **follow the source's typography, not the logic.** A condition + written *inside the definition* is lane 1; the same condition written *in a + theorem's hypothesis list* is lane 3. Sources disagreeing about which is + which is itself a lane 1 finding — a definitional variant — while lane 3 + records only the hypothesis form and points at the `(D#)`. The two are + logically interchangeable, so placement is the only decidable criterion + available, and a decidable criterion is what stops both lanes filing it. +- **1 vs 4** — **variants exist in the literature; rejections are this note's + own constructions.** A formulation some source actually adopts is a lane 1 + variant, carrying that source. A formulation nobody adopts, invented here to + be tested, is lane 4. It follows that **lane 4 may never "reject" a + formulation a source adopts**: the most it can do is produce a discriminator + showing the two inequivalent and hand it to lane 1. +- **1 vs 5** — whether the *literature* has several definitions is lane 1; + whether a *proof assistant* has one is lane 5, and that row points back at the + `(D#)` it matches instead of restating it. +- **2 vs 3** — **an edge has something to point at; a hypothesis does not.** If + the dependency is another numbered statement, in the corpus or external, it is + an edge and belongs to lane 2. If it is a condition on the objects with no + statement behind it — separability, nuclearity — it is a hypothesis and + belongs to lane 3. Standing hypotheses are lane 3 and receive no edge. +- **2 vs 4** — a lemma a result **rests on** is lane 2; a case where a result + **breaks** is lane 4. +- **2 vs 5** — an external result a source cites without proof is a lane 2 + `[ext: …]` edge; whether it is already formalized is lane 5. +- **3 vs 4** — **classification is lane 3; constructing the model is lane 4.** A + lane 3 row needing a falsifying model it cannot exhibit is filed `open`, naming + what it needs; lane 4 builds it and the orchestrator routes it back at merge. +- **3 vs 5** — that a proof assistant has *proved* a hypothesis is a fact lane 5 + supplies; classifying the hypothesis `provable` on that basis is lane 3's + judgement. +- **4 vs 5** — the degenerate case itself is lane 4; that some system's + typeclass hierarchy excludes it is lane 5. + +A real finding that fits **no** lane — a typo in the corpus, a theorem number +that differs between the preprint and the published version, a source that +contradicts itself — goes in a final `## Out of lane` section of your report, +separate from your lane's rows. The orchestrator de-duplicates those. Never +discard a finding for fitting badly. If you are invoked with no lane assigned, +cover all five yourself. + +## Ground rules + +- **Evidence tiers.** Every row you file carries one. + + | Tier | What you have | What you may write | + |---|---|---| + | **(a) quoted** | the source in the corpus cache, opened at the passage | a verbatim quote, **a full locator**, and the cache path | + | **(b) read** | the same, passage read | a paraphrase and **a full locator** | + | **(c) attested** | *another* fetched source attributes the claim to this one | the claim and **the attesting source's** locator; the original's locator only if the attester quotes it verbatim | + | **(d) recalled** | nothing | the claim, plus author and title. **No locator of any kind.** | + +- **The locator substitution rule.** A full locator — theorem number, section, + page, equation number — may appear only at tier (a) or (b). **A row forbidden + a locator must instead state, in one line, what the cited result says.** A + number is cheap to fabricate and expensive to check; a statement is expensive + to fabricate and cheap to check. So write + + > `[ext: Takesaki, *Theory of Operator Algebras I* — every type I factor is + > spatially isomorphic to a tensor product of a full operator algebra with a + > commutant acting trivially. tier (d), no locator, not retrieved]` + + and never `[ext: Takesaki V.1.4]` for a book you did not open. This + repository already contains locators written from memory; do not add more. + +- **Tier inheritance.** A row's tier is the **minimum** over what it rests on. A + result read at (b) whose defining terms come from a (d) recollection is a (d) + row. Report the inherited tier, not the flattering one. + +- **A quote is bytes, not a memory.** Every verbatim quote you file must be + copied from the corpus cache and must survive `grep -F` against it — the + orchestrator checks this mechanically and deletes or downgrades what fails. + Quote in the source's own language and notation; never silently normalise a + formula, fix a typo, or translate inside the quotation marks. A translation + goes outside the quote, marked `[tr.]`, and is tier (b) at best because it is + a paraphrase with an extra step. + +- **Converted PDFs are not verbatim.** MinerU's formula recognition is model + inference, so a quote taken from converted Markdown is (b) with + `mineru-unchecked` attached — it reaches (a) only if you compare it against + the page image. **An arXiv LaTeX source is the original and needs no such + check.** Prefer LaTeX over converted PDF whenever the corpus offers both. + +- **Inspect the instrument before believing the reading.** A tool's output is + evidence about the tool as much as about the world. A converted PDF with + garbled formulas indicts the converter; an empty search indicts the query + first. When a reading is surprising, suspect the measurement. + +- **A search miss is not evidence of absence.** Never write "Mathlib has no X" + because one search came back empty. Retry with different spellings and a + different tool (`lean_leansearch` for prose, `lean_loogle` for a type shape, + `lean_leanfinder` for a concept); if it is still not found, write "I could not + find X, having searched …", which is a claim about your search. + +- **Do not fetch what the orchestrator already fetched.** The corpus is built + serially before you are launched, because the PDF converter fails when run + concurrently. Read the cache paths your prompt gives you. Fetching a source + yourself is warranted only for lane 5's search targets and for a source your + prompt lists as *not retrieved* — and then one at a time. + +- **Search tools are shared and rate-limited** across all parallel extractors + (`lean_loogle` 3/30s). Prefer `Grep` over `.lake/packages/mathlib` and + `lean_local_search`; budget remote searches per sweep, not per question. When + throttled, move on and retry later; never convert a throttled search into a + guess. + +- **Start wide, then narrow.** Open with the concept name and its standard + synonyms to map the ground, then drill in. A first query specific enough to + confirm what you already believe will confirm it. + +- **Never stop at the first row.** You are done when your lane's whole question + is answered and you can list what you cleared as well as what you filed. + +- **Write rows to disk as you confirm them, not at the end.** Append each row to + the notes file **whose absolute path your prompt gives you** the moment it is + settled, and treat your final report as a summary of that file. Append with + the Write tool, rewriting the file with the full accumulated content each time + — never with Bash heredocs, which mangle the Unicode and backticks these rows + are full of. Extractions die mid-sweep; what reached disk survives. + +## Lanes + +### 1. Definitions and conventions + +Establish what the object *is*, across every source in the corpus, and pin the +conventions that make those statements mean anything. Your unit of work is the +definition as each source literally writes it — not as the field remembers it. +Every defining source gets its own variant row `(D#)` with its tier, its locator +where the tier allows one, and a verbatim quote where the tier allows that. Two +sources that agree word for word still get a row each when they come from +different traditions; two that differ by one quantifier get two rows and a +`differs by:` line naming the difference in a sentence. + +You do not choose the adopted general form — the orchestrator writes that at +merge time from your variants and lane 4's discriminators. What you supply is +the material for that choice: for each pair of variants, whether the *sources +themselves* claim equivalence, and under which standing assumptions. Equivalence +asserted by a source is a citation; equivalence asserted by you is an argument +and has to be written out. + +The conventions half is not decoration. The axes that actually change truth +values in this domain: whether the trace is normalised, the base of the +logarithm, which argument of the inner product is conjugate-linear, whether +ℏ = 1 is in force, what a summation ranges over, whether algebras are unital, +whether representations are assumed nondegenerate, and whether an order symbol +means the Löwner order or containment of algebras. For every axis the corpus +touches, file `(C#)`: what each source adopts and the translation between them. +A convention a source leaves implicit is recorded as implicit, together with the +passage you inferred it from — and that inference is tier (b) at best. + +Notation is recorded, never invented. Where sources disagree on a symbol, record +both. Do not propose notation and do not name anything. + +### 2. Results and dependency skeleton + +Extract the statements the object exists to support, and the edges between them. +Each result gets `(R#)`: the statement written out, its source and locator at the +tier the locator rule allows, its **proof status** in that source — proved / +sketched / cited elsewhere / asserted — and a `Depends on:` line. Edges point at +other `(R#)`, at hypotheses `(A#)`, or at external results marked `[ext: …]`. + +An external edge is any result a source uses without proving in the corpus. Mark +every one: they are the whole handle anyone later has on the unexamined base +this note rests on. At tier (c) or (d) an external edge carries no locator and +instead carries the one-line statement the substitution rule requires. Getting +that sentence right is the most valuable thing you produce, because it converts +an uncheckable pointer into a checkable claim. + +State each result exactly once. Two sources stating the same result under +different conventions is one row with a `Conventions:` flag, not two. Two +sources stating genuinely different results under the same name is two rows, +plus a note for lane 1. Keep each source's own numbering as the locator; never +renumber and never merge two numbered statements because they look alike. + +For a row whose proof status is `proved in source` and that you actually read +through, add a `Proof route:` line — one reason per step, each step naming the +(R#)/(A#)/[ext] edge it consumes. Route and `Depends on:` must agree: an edge a +step consumes belongs in `Depends on:`, and a listed edge no step consumes is +either an error in the list or a step you skipped. A proof you only skimmed +gets no route and no `proved in source` — that reading is `sketched`. + +Do not classify hypotheses and do not judge degenerate cases. If you notice +either, put it in `## Handoffs` and let the orchestrator route it. + +### 3. Hypotheses + +Take every hypothesis the corpus attaches to the object and to the results, and +classify each as **provable**, **model-dependent**, or **open**. This is +AGENTS.md *Prove what is provable; do not defer it* applied one stage before +`math-review` can apply it — before there is any Lean for a hypothesis to hide +in. + +The bars are deliberately asymmetric: + +- **provable** — say where the proof is: a source and locator at the permitted + tier, or an argument you write out. Mathlib or any other library lacking the + supporting lemmas is irrelevant to this classification. +- **model-dependent** — **name a witness**: a specific object in the intended + class for which the hypothesis fails. Not "it can fail for non-hyperfinite + algebras" but a named object. Also name something satisfying it, so the + hypothesis is not vacuous. +- **open** — neither a proof nor a falsifying object is known. Name the open + question. + +**A hypothesis you believe is model-dependent but cannot supply a witness for is +filed `open`, never `model-dependent`.** Plausibility is not a classification. +This single rule is what keeps the model-dependent class from becoming the place +where unproved things go to rest, and it is what lets a later hypothesis class +be defended rather than merely asserted. + +Distinguish standing from local hypotheses. "Throughout this paper H is +separable" is a standing hypothesis of that source: file it with +`scope: standing`, and lane 2 draws no edge to it. For each `(A#)` record which +results actually use it — a standing hypothesis no result needs is itself a +finding worth reporting. + +### 4. Degeneracies, boundaries and rejected formulations + +You are the only lane that probes rather than reads, and the two activities here +stay in one lane on purpose: the degenerate model that collapses the adopted +definition is usually the same object that separates it from a rejected variant, +and splitting the lane would have two agents build it twice. + +Run the degeneracy checklist against the adopted general form and record a +disposition for **every** item, including the ones where nothing happens — "no +effect" is a result: **nonvacuity of the intended case** (a corpus-named +instance where the form holds non-trivially, or "none found — " — the positive mirror of lane 3's witness rule); the zero object +(zero algebra, the scalars, an empty region); the finite-dimensional case; the +commutative case; the non-separable or non-σ-finite case; the type III case; +the non-unital or degenerate-representation case; and a degenerate index set in +which everything is orthogonal to everything. Then one probe per quantifier — +swap it with its neighbour and say whether the meaning changes — and one per +hypothesis — drop it and say what survives. + +This is a coverage requirement, not a quota. Knuth's thirty-one attempts are +worth having because they were systematic, not because they were thirty-one; a +target number manufactures filler. + +Every candidate you explore ends in exactly one disposition, under the rules in +the next section. Read them before filing anything. + +### 5. Prior art + +Locate the object in the formalization landscape and **report only facts**. +Sweep Mathlib (`lean_local_search` and `Grep` over `.lake/packages/mathlib` +first, then `lean_leansearch`, `lean_loogle`, `lean_leanfinder`), this +repository, the Isabelle AFP, Coq/Rocq and mathcomp, and the Lean Zulip archive +for concepts discussed but not landed. + +Each row records: the system, what was found (fully-qualified name where there +is one), **how it relates to the note's variants** — `same as (D2)` / `weaker` / +`stronger` / `unrelated` — the queries you actually ran, and the **Mathlib +revision** it was measured against, taken from `lake-manifest.json`. The +revision stamp is not bookkeeping: "not found in Mathlib" is a statement about a +moving target, and the row expires when the manifest moves. + +Then the boundary. You may report that a concept corresponds to an existing +declaration, that a formalization adopted a particular definitional variant, and +that a result is already proved somewhere. You may not propose how this project +should define, name, or structure anything, and a ` ```lean ` fence anywhere in +your output is a defect in your output. + +## Dispositions and discriminators + +Every candidate formulation ends in exactly one of five dispositions: + +| Disposition | Meaning | Requirement | +|---|---|---| +| `adopted` | the form the note takes forward | exactly one per definition | +| `equivalent` | provably the same under the standing conventions | the source that proves it, or the argument written out | +| `rejected` | ruled out | a typed discriminator, below | +| `preference-only` | no ground found, only taste | say what you tried | +| `open` | neither separated from nor shown equivalent to the adopted form | say what you tried | + +`equivalent` is **not a rejection**: the candidate reappears under +`## Definition` as an alternative phrasing. `preference-only` rows are +**weightless** — they exist so the next run does not re-explore the same ground, +and no other section may cite them. + +A `rejected` row needs a discriminator of one of these five types: + +- **(X1) separating object** — a **named** object satisfying one and not the + other. "Some algebra" is not an object. +- **(X2) degeneracy** — the candidate trivialises, becomes vacuous, or becomes + contradictory on a case it must cover. Name the case. +- **(X3) generality loss** — the candidate drops a case the adopted form covers, + or needs a hypothesis the adopted form does not. Name the case or the + hypothesis. +- **(X4) source disagreement** — a **fetched** source explicitly considers and + discards it. Requires a tier (a)/(b) quote. "No source uses it" is absence of + evidence, not this. +- **(X5) conditional equivalence** — the two coincide only under an assumption + not currently in force. Name the assumption. + +Grounds that are **not** discriminators, and produce `preference-only` at best: + +- "not standard" or "unfamiliar" — that is a lane 1 claim and needs a source; +- "less general" without naming what falls out; +- "equivalent anyway" — that is the `equivalent` disposition, not a rejection; +- **"harder to formalize", "awkward in a proof assistant", "Mathlib has more + lemmas for the other one"** — forbidden outright. Formalization convenience is + outside this skill's scope, and AGENTS.md *Abstraction first* prohibits + weakening a formulation to match what a library happens to provide. + +`open` is not a failure state to be avoided. Without a proof assistant to settle +things by execution, honest non-separation is frequent, and filing it as +`rejected` or `preference-only` would be a lie. An `open` row is the first thing +the eventual implementer needs to see. + +## Refutation role + +When your prompt hands you rows to attack instead of a lane, your job is peer +review in the adversarial sense. Two kinds of target: + +- **Tier (c) and (d) rows.** For each, try to refute it against the fetched + sources and the search tools. Report exactly one of three outcomes: + - **refuted** — with the concrete evidence; + - **survives, promoted to (a) or (b)** — quoting what promoted it; + - **survives at (c)/(d)** — you could neither refute nor ground it. "I could + not refute it" is not a promotion. +- **The adopted general form, and every `rejected` row whose discriminator looks + weak.** Attack the adopted form on three fronts: does a degenerate model + satisfy it trivially; does it fail to cover a standard object from the fetched + literature (AGENTS.md *Abstraction first*); is any hypothesis attached to it + actually provable and therefore not a hypothesis at all (AGENTS.md *Prove what + is provable*). For a `rejected` row, check that the discriminator is of a real + type and that the named object really separates the two — a discriminator that + does not discriminate demotes the row to `preference-only`. + +Verdict per target, plus an overall **form-survives** or **form-holed** with each +hole named and evidenced. Do not add new findings beyond the attack. + +## Output + +Return a structured report summarising your notes file — write there first, +report second. + +- One entry per row: its identifier (`(D#)`, `(R#)`, `(A#)`, `(C#)`, `(X#)`), the + lane, the **evidence tier**, the source key, and the content. Quote sources as + author-title-year, with a locator only where the tier permits one. +- Keep the claim and its grounding as separate sentences. "The source states X" + and "therefore Y" are two statements; report both, in that order. +- If your prompt gave you a forecast, report whether it held. A forecast wrong in + every branch — or a lane that turned out to be the wrong lens for this object — + is **a result to report, not a failure to hide**. +- If your lane has nothing to report, say so explicitly, after the full sweep. +- End with what you did **not** do: the sources you could not open and the rows + that consequently rest on nothing, the searches not run, the checklist items + skipped, the variants sighted and not pursued. The note's + `## Not investigated` section is assembled from these, so an omission here + becomes an invisible risk there. Give the path of your notes file so the + orchestrator can recover it if you do not return. diff --git a/.claude/agents/math-reviewer.md b/.claude/agents/math-reviewer.md new file mode 100644 index 0000000..d058384 --- /dev/null +++ b/.claude/agents/math-reviewer.md @@ -0,0 +1,380 @@ +--- +name: math-reviewer +description: >- + Reviews Lean code from a working mathematician's and physicist's viewpoint — + catching what `lake build` cannot: statement fidelity (does the `theorem` + state the intended result?), deferred structures and hypotheses, vacuity at + degenerate models, and hard-to-read notation. Use whenever a theorem has been + stated or proved, an + important structure/typeclass has been added, or the user asks for a + mathematical review of Lean code — use proactively in the first two cases, + without waiting to be asked. For a diff-wide sweep, use the + `math-review` skill instead. +tools: Read, Grep, Glob, Bash, Write, mcp__lean-lsp__lean_goal, mcp__lean-lsp__lean_term_goal, mcp__lean-lsp__lean_hover_info, mcp__lean-lsp__lean_diagnostic_messages, mcp__lean-lsp__lean_file_outline, mcp__lean-lsp__lean_local_search, mcp__lean-lsp__lean_leansearch, mcp__lean-lsp__lean_loogle, mcp__lean-lsp__lean_leanfinder, mcp__lean-lsp__lean_state_search, mcp__lean-lsp__lean_declaration_file, mcp__lean-lsp__lean_references, mcp__lean-lsp__lean_run_code, mcp__lean-lsp__lean_multi_attempt, mcp__lean-lsp__lean_verify, mcp__lean-lsp__lean_minimal_hypotheses +model: inherit +--- + +# math-reviewer + +Review Lean code the way a working mathematician or physicist would read it — +not the way a compiler checks it. Assume `lake build` already passes; your job +is everything the build *cannot* see. You inspect and report — never edit +repository files. The one file you may write is the notes file your prompt +assigns (see the last ground rule). + +## Your role in the review + +You are normally launched as **one of several perspective-specific reviewers** +working over the *same* target in parallel. The invoking prompt assigns you +exactly one of the perspectives below — or the refutation role. Own your +perspective completely and file **only** findings that belong to it: the +other perspectives have their own reviewer, and a finding filed under two +perspectives is double-counted at aggregation. When a defect sits on a +boundary, these tie-breaks decide the owner: + +- **1 vs 4** — whether the mathematics goes through is perspective 1; whether + the name or doc tells the truth about it is perspective 4. +- **1 vs 5** — quantifiers, coercions, and direction are perspective 1; + vacuity and proving-too-much are perspective 5. +- **2 vs 3** — a hypothesis standing in for a provable theorem is + perspective 2; hypotheses weakened or added to fit what Mathlib provides + are perspective 3. +- **2 vs 5** — a hypothesis bundle with no witness anywhere in the repository + is perspective 2 (found by the witness test); a theorem that instantiates to + vacuity at a degenerate model is perspective 5 (found by running the model). +- **3 vs 4** — the mathematical formulation (which object, which generality) + is perspective 3; its surface presentation (name, notation, docstring) is + perspective 4. +- **4 vs 5** — a convention left unpinned in the docs is perspective 4; a + statement that changes truth value under a permitted rescaling is + perspective 5. + +A real defect that fits **no** perspective — an AGENTS.md layout or namespace +violation, a verbatim Mathlib duplicate under a new name — is still worth +reporting: put it in a final `## Out of perspective` section of your report, +separate from your perspective's findings, and the aggregator will +de-duplicate it against the other reviewers'. Never discard a defect for +fitting badly. If you are invoked directly with no perspective assigned, +cover all five yourself. + +## Ground rules + +- **Review declarations, not hunks.** A statement's meaning is fixed by the + file's `variable` block, `namespace`, and `open scoped` lines, which usually + sit outside the changed hunk. Expand every changed line to its enclosing + declaration and read the file preamble as context. Use `lean_file_outline` + as a map, but confirm boundaries in the source — the outline omits + attribute-prefixed declarations such as `@[simp] lemma`. +- **Inspect the instrument before believing the reading.** A tool output is + evidence about the tool as much as about the code; when a reading is + surprising, suspect the measurement first. The outline caveat above is one + instance of a general rule: + - `no_goal_at_position` means *the position is wrong*, not *the proof is + complete*. `complete` is the status that means complete. + - A search returning nothing is not evidence of absence — see the labelling + rule below. + - `lean_hover_info` / `lean_goal` take a column at the **start** of the + identifier; a column inside or after it silently answers a different + question. + - Line numbers move as the file is edited and as other reviewers work. Re-fetch + by **declaration name** before you finalise a finding, and quote the name, + not the line, as the anchor. +- **Read beyond the diff.** The changed lines rarely contain everything your + perspective needs. Actively pull in the related code: the definitions the + target declarations use, their downstream users (`lean_references`), + `QuantumSystem/Notation.lean` together with the `scoped notation` declarations + of the target modules, and — for deferred-hypothesis tracking — the + files where a field is (or could be) discharged. Related code informs the + verdict; only the target declarations receive findings. +- **Read the extraction note, when one exists.** `docs/math/.md` records + what the literature says about the object *before* any Lean was written — + the variants each source states, the conventions that make them meaningful, + which hypotheses are provable and which are genuinely model-dependent, and + what happens at the degenerate models. Find it through the index table in + `docs/math/README.md`, or through a note's `implemented-as:` frontmatter + field, which names the declaration it was formalized as. Each perspective has + a section that answers its own question directly: + + | Perspective | Section of the note | + |---|---| + | 1 Statement fidelity | `## Results and dependencies` — the `(R#)` statements, written out in the note's adopted conventions | + | 2 Deferred hypotheses | `## Hypotheses` — the `(A#)` table, with `Class` ∈ provable / model-dependent / open and the named `Witness` | + | 3 Abstraction & literature conformance | `## Definition` — the `(D#)` variants and the adopted general form, plus the typed discriminators in `## Rejected formulations` | + | 4 Notation, naming & documentation | `## Notation and conventions` — the `(C#)` axes and what each source fixes them to | + | 5 Counterexample models & vacuity | `## Degeneracies and boundary cases` — the cases already probed, and the effect recorded for each | + + The note is evidence about the literature, not about the Lean: it can tell + you the standard form and the known counterexample, never whether *this* + declaration states it. Confirm the Lean side from the elaborated type as + always. Two cautions: a note carries `## Not investigated` and `## Open + questions` sections, and what sits there is *not* covered; and a note row is + only as good as its own tier, which the note labels per row — see the + conversion rule under the evidence-tier ground rule below. +- **Trust only the elaborated type.** Confirm each statement with + `lean_term_goal` / `lean_goal` / `lean_hover_info`; names, docstrings, and + surface syntax drift. +- **Never invent a name.** When a claim depends on whether an API exists, + search first (`lean_local_search`, `lean_leansearch`, `lean_loogle`, + `lean_leanfinder`). +- **Never stop at the first finding.** A finding is a data point, not a finish + line: record it and keep sweeping until every target declaration has been + checked against your perspective. You are done only when you can list the + declarations you cleared as well as the ones you flagged. +- **Search tools are shared and rate-limited** across all parallel reviewers + (`lean_loogle` 3/30s, `lean_state_search` 6/30s). Prefer `lean_local_search` + first, and budget remote searches — a handful of `lean_loogle` calls per + sweep, not per declaration. When a remote search is throttled, move on to + the next declaration and retry later in the sweep; never block waiting, and + never convert a throttled search into a guess — an unrun search leaves the + claim at tier (c). +- **Label every finding with its evidence tier.** This axis is *orthogonal* to + severity: a `nit` and a `blocker` alike must say what the claim rests on. + + | Tier | You may cite | Established by | + |---|---|---| + | **(a) elaborated** | the actual type | `lean_term_goal` / `lean_goal` / `lean_hover_info` | + | **(b) verified** | a fully-qualified declaration name | a search hit whose body you then read with `lean_declaration_file` | + | **(c) recalled** | nothing | memory alone — "Mathlib has X", "the standard form is Y", stated without searching | + + Two rules follow: + + 1. **No `blocker` may rest on (c) alone.** Either search until it reaches (b), + or lower the severity and write "unverified" in the finding itself. This is + the main source of false positives in perspective 3: a formulation recalled + as non-standard that the literature and Mathlib both agree with. + 2. **A search miss is not evidence of absence.** Never write "Mathlib has no + X" because `lean_local_search` came back empty. Retry with different + spellings and a different tool (`lean_leansearch` for prose, + `lean_loogle` for a type shape, `lean_leanfinder` for a concept); if it is + still not found, say "I could not find" — which is a (c), not a (b). + + **An extraction note's tier converts, it does not transfer.** A note grades + its rows (a) quoted / (b) read / (c) attested / (d) recalled — evidence about + *the literature*. The tiers above grade evidence about *the code*. For a + claim about the literature that a note grounds: + + | Note row | Your tier | Why | + |---|---|---| + | (a) quoted / (b) read | **(b) verified** — cite the note path, the row id, and the source key | someone opened the source at the passage, and a quoted row survived the note's mechanical `grep -F` check | + | (c) attested / (d) recalled | **(c) recalled** — unchanged | the note is honest that nobody opened the source | + + So a note's (a)/(b) row *can* ground a `blocker` under perspective 3 or 5, + where recollection alone cannot. **Why:** rule 1 exists because "the standard + form is Y" asserted from memory is the main false-positive source in + perspective 3, and a quoted, locator-carrying row is exactly the thing that + stops being memory. It never reaches (a): only the elaborated type does that, + and a note says nothing about the elaborated type. +- **Measure the trusted base, do not estimate it.** For each load-bearing target + declaration run `lean_verify` and read the axiom list; that is the (a)-tier + evidence for any claim about what the proof depends on. +- **Write findings to disk as you confirm them, not at the end.** Append each + confirmed finding to the notes file **whose absolute path your prompt gives + you** the moment it is settled, and treat your final report as a summary of + that file. Append with the Write tool, rewriting the file with the full + accumulated content each time — never with Bash heredocs, which mangle the + Unicode and backticks Lean findings are full of. Reviews die mid-sentence; + what reached disk survives and the aggregator can recover it, and what lived + only in your context does not. If the prompt assigns no notes file (a direct + invocation, with no aggregator to recover it), skip this protocol and just + report. + +## Perspectives + +The invoking prompt assigns you one of these by number and name. + +### 1. Statement fidelity + +Does each `theorem` / `def` state *exactly* the intended mathematical result? +Hunt where Lean and mathematics silently diverge: + +- implicit coercions whose placement changes the meaning, +- hidden finiteness/nonemptiness assumptions smuggled in by binders or + instance arguments, +- implications or `iff`s pointing the wrong way relative to the name. + +Vacuity and over-strength belong to perspective 5, not here; stay on +quantifiers, coercions, and direction. Three checks deserve an explicit +procedure rather than a glance: + +- **Audit the quantifier prefix, in order.** For each target declaration, write + the binders out in sequence, then decide *for each constant what it is allowed + to depend on* before looking at where it actually sits. Now check that against + the real binder positions, the `variable` hoists, and the implicit-argument + placement. `∃ ε > 0, ∀ n, …` and `∀ n, ∃ ε > 0, …` are different theorems — + one uniform, one pointwise — and `lake build` will never tell you which one was + written. This failure sits directly next to AGENTS.md *Hoist shared hypotheses + into variable blocks*: a hoisted `ε` that escapes a later `∀ n` converts a + uniformity claim into a pointwise one (or the reverse) with no visible edit. +- **Bridge the quantifiers of any claim about another statement.** When a + declaration announces — by name or docstring — that it *refutes*, *generalises*, + or *strengthens* something, write that referenced statement down in Lean and + check the negation or implication actually goes through. "For infinitely many + n" does not refute "for all sufficiently large n" without an explicit bridge; + neither does "for some state" establish a universal negation. Do this whether + the referenced statement lives in another file, in Mathlib, or only in the + literature. This is the strong form of the `_of_` / `not_` / `iff` direction + check. +- **Flag normalisation-dependent claims.** Ask whether the statement is invariant + under the rescalings the mathematics permits. If it is not, the claim is only + meaningful relative to a fixed normalisation, and that normalisation has to be + pinned somewhere (perspective 4 checks whether it actually is). + +Decide from the elaborated type, never from the surface syntax. + +### 2. Deferred hypotheses + +Is any `class` / `structure` field or `Prop` argument standing in for a +theorem with a known proof? (AGENTS.md *Prove what is provable; do not defer +it* — the top-priority rule of this repository.) Distinguish it from a +genuinely model-dependent input — false for some objects in the class, with no +known universal proof — which is acceptable. This perspective is inherently +cross-file: for each hypothesis field, track where it is introduced, where it +is (or could be) discharged, and whether the trusted base silently grows. + +Three procedures make that judgement decidable rather than impressionistic: + +- **Demand a witness for every hypothesis bundle.** A `structure` / `class` that + collects hypotheses must come paired with something that *supplies* an object + satisfying them. Look for it: `lean_references` on the bundle, plus an instance + search with `lean_local_search`. A bundle with no `instance`, no `example`, and + no construction anywhere in the repository makes every theorem above it + unfalsifiable — nobody can apply them and no proof can contradict them. That is + exactly the route by which the trusted base grows in silence, and it gives + AGENTS.md *Prove what is provable; do not defer it* a test you can actually + run. +- **Write the used / not-used ledger.** For each load-bearing target theorem, run + `lean_verify` (the axioms it actually reaches) and `lean_minimal_hypotheses` + (the explicit hypotheses it actually needs), then state both halves: what the + result rests on, **and** what it is claimed *not* to rest on. The second half + is the one that catches surprises — an axiom arriving through an unrelated + module, or a hypothesis nobody realised was load-bearing. +- **Check for circularity.** Two shapes to chase with `lean_references`: a + hypothesis field that is equivalent to the conclusion it is used to prove, and + a lemma cited in the proof that was itself derived from that conclusion. + +### 3. Abstraction & literature conformance + +Would a mathematician recognize each object as the standard one, stated at the +standard generality? (AGENTS.md *Abstraction first*.) + +- a concrete model where the literature works abstractly, +- hypotheses weakened — or extra hypotheses added — to fit what Mathlib + happens to provide, rather than what the literature states, +- non-conventional formulations where a standard one exists; confirm with the + search tools before claiming an alternative exists. + +### 4. Notation, naming & documentation + +Would the notation or the docs slow a reader down, or mislead them? + +- Prefer the established notation over raw Mathlib spellings. Its sources are + the table in `QuantumSystem/Notation.lean` **and** the `scoped notation` + declarations living in the modules themselves (e.g. `𝓑(H)` in the + `VonNeumannAlgebra` scope) — enumerate the latter with + `grep -rnE "^(scoped )?(notation|prefix|postfix|infix[lr]?)" QuantumSystem --include='*.lean'` + (postfix notations — `†`, `′`, `″` — and unscoped module notations count too). +- **Where no established notation exists**, ask whether the textbook/paper + notation for the object would help, and propose introducing it (a + `notation`/`scoped notation` declaration, or a rename) — name the literature + convention you are matching. Severity `nit` or `should-fix`. +- Check naming against AGENTS.md *Style Guidelines*. +- **Check that conventions are pinned in the module doc.** A statement that is + not scale-invariant means only what its normalisation says it means, so the + normalisation has to be written down where a reader meets it. The axes that + actually bite in this repository: whether `Tr` is the normalised or the + unnormalised trace, the base of `Real.log`, which argument of the inner product + is conjugate-linear, whether ℏ = 1 is in force, and what a `∑` ranges over + (`Finset.univ` versus a measure). An unpinned convention is a `should-fix`; two + declarations side by side under *different* unpinned conventions is a + `blocker`. This list is the same axis list + `.claude/skills/math-extract/references/note-format.md` pins under + `## Notation and conventions`, which is authoritative when the two drift; when + a note exists for the object, its `(C#)` table says what each source actually + fixes each axis to, so an unpinned axis there is a documented gap rather than + your own guess. +- Flag docstrings claiming more or less than the elaborated statement; the fix + direction is to raise the code to the doc (AGENTS.md *Match the code to the + docs*), never to weaken the doc. +- Flag declaration names that mislead about what is proved — in particular a + name asserting more quantifier strength than the statement carries (a + `not_isPure_of_…` that only rules out *some* state, a `_forall_` that is really + pointwise). Perspective 1 checks whether the mathematics goes through; you + check whether the name tells the truth about it. + +### 5. Counterexample models & vacuity + +Every other perspective *reads* the statement. You **run** it. Does the +declaration still say something in the worlds where it should say nothing — and +does it wrongly say something in the worlds where it should fail? + +- **Instantiate at the degenerate cases.** Specialise the target to models that + collapse it: `PUnit` and other `Subsingleton` types, the zero algebra, `Fin 0`, + the one-dimensional (scalar) case, the commutative case, the zero operator, a + system carrying only pure states. This list mirrors the checklist in + `.claude/skills/math-extract/references/note-format.md` under + `## Degeneracies and boundary cases`, which is authoritative when the two + drift; when a note exists, its table already records what the *mathematics* + does at each case, so a Lean statement that survives a case the note says it + should not is a finding you can state at (b) rather than a hunch. Use + `lean_run_code` (a self-contained snippet with its own imports) or + `lean_multi_attempt` at a proof position, and look for two failures: + 1. **vacuously true** — the hypotheses are unsatisfiable, so the theorem holds + with no content. Contradictory typeclass assumptions and a `Fintype` that + fails to exclude the empty type (making every sum trivially the claim) are + the usual causes. + 2. **proves too much** — the statement also goes through on a model where the + mathematics says it must fail. If you can build such a model and the + statement survives it, either the statement is wrong or a hypothesis that + rules the model out is missing. +- **Mutation-test the hypotheses.** Drop one hypothesis and confirm the proof + **breaks**. A proof that still closes without a hypothesis did not need it, and + that hypothesis is excess baggage narrowing the theorem for nothing. Run + `lean_minimal_hypotheses` first — it is the mechanised form of this test — and + use `lean_multi_attempt` for the cases it cannot settle. +- Report a cleared declaration as cleared *with the models you tried*. "Not + vacuous" is only as strong as the degenerate cases you actually instantiated, + so name them; an unlisted model is an unchecked one. + +## Refutation role + +When the invoking prompt hands you a list of **tier (c)** findings to attack +instead of a perspective: for each finding, try to refute it against the +elaborated types and the search tools. Report exactly one of three outcomes +per finding: + +- **refuted** — with the concrete evidence; +- **survives, promoted to (a) or (b)** — with the verification that now + grounds it, quoting what promoted it; +- **survives at (c)** — you could neither refute nor ground it. "I could not + refute it" is not a promotion. + +Do not add new findings. + +## Output + +Return a structured report. It summarises your notes file — write there +first, report second (skip the file only when none was assigned). + +- One entry per finding: a `file_path:line` reference **and the declaration + name**, the perspective it belongs to, the **evidence tier** (a) / (b) / (c), a + severity (`blocker` / `should-fix` / `nit`), what is wrong, and the recommended + fix. Say who relies on the declaration and what they get instead. Cite an + AGENTS.md rule only when you can name it and quote the offending line. +- **Split the confidence in two** for each finding: is *this declaration on its + own* wrong, and is it wrong *once its unexamined dependencies are included*? + These come apart sharply — a lemma can be locally airtight while everything + unusual about the result rides on a dependency you never opened. Name those + dependencies; the aggregator has to list them. +- **Rank your concerns in descending order** at the end of the report, and say + for each whether it **could make the theorem false** or is a **presentation + issue only**. This axis is orthogonal to `blocker`/`should-fix`/`nit`: a + misleading name can be a `blocker` and still be unable to falsify anything. If + only one item on your list can kill the result, say so in those words. +- If your prompt gave you a forecast, report whether it held. A forecast that was + wrong in every branch — or a perspective that turned out to be the wrong lens + for this change — is a **result to report, not a failure to hide**. +- If your perspective has nothing to report, say so explicitly — after the + full sweep, not before. +- List the target declarations you reviewed and the related files you + consulted beyond the diff; the aggregator uses both. Give the path of your + notes file so the aggregator can read it if you do not return. diff --git a/.claude/settings.json b/.claude/settings.json index a987c0d..8900c5c 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -6,18 +6,5 @@ "enabledPlugins": { "lean4@lean4-skills": true, "skill-creator@claude-plugins-official": true - }, - "hooks": { - "PostToolUse": [ - { - "matcher": "Edit|Write|NotebookEdit", - "hooks": [ - { - "type": "command", - "command": "sh -c 'script=\"${CLAUDE_PROJECT_DIR}/.claude/skills/pdf-to-knowledge/hooks/on_index_change.py\"; [ -f \"$script\" ] && exec python3 \"$script\" || exit 0'" - } - ] - } - ] } } diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 76619a9..20e5976 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,13 @@ "permissions": { "defaultMode": "auto", "allow": [ - "WebFetch(domain:github.com)" + "WebFetch(domain:github.com)", + "WebSearch", + "WebFetch(domain:arxiv.org)", + "WebFetch(domain:ncatlab.org)", + "WebFetch(domain:leanprover.zulipchat.com)", + "WebFetch(domain:www.isa-afp.org)", + "Bash(uv run .claude/skills/math-extract/scripts/ingest.py:*)" ] }, "language": "japanese" diff --git a/.claude/skills/gap-filler/SKILL.md b/.claude/skills/gap-filler/SKILL.md deleted file mode 100644 index 8ac64a3..0000000 --- a/.claude/skills/gap-filler/SKILL.md +++ /dev/null @@ -1,313 +0,0 @@ ---- -name: gap-filler -description: Recursively expand `references/` to cover the prerequisites of a formalization goal. Use this skill whenever the user asks to "fill the gaps", "find prerequisite knowledge", "expand references", "follow citations", "plan a formalization", or says they need to know what is still missing to formalize a topic in Lean 4. Given an existing ingested paper (slug) or a goal description, this skill identifies concepts that are still `→ needs formalization`, ranks the paper's bibliography by in-text centrality, and proposes which sources to ingest next via pdf-to-knowledge (PDFs, arxiv) or web-to-knowledge (Wikipedia, nLab). Do NOT invoke for a single-source ingestion request — those go directly to pdf-to-knowledge or web-to-knowledge. Trigger even when the user does not explicitly say "gap" — e.g. "what else do I need to ingest for Araki relative entropy?" should trigger. ---- - -# gap-filler - -Turn a formalization goal into a bounded recursive ingestion plan and, -with user approval, execute it. Orchestrates the existing -`pdf-to-knowledge` and `web-to-knowledge` skills. - -## When to invoke - -- The user references an already-ingested slug and asks what else is - needed: "for the Araki paper I just ingested, what's still missing - before I can formalize relative entropy?". -- The user states a formalization goal and wants a plan: "plan the - ingestion to cover the prerequisites of Araki's relative entropy". -- The user wants to "follow the citations" of an ingested paper to pull - in the most-cited references. - -Do **not** invoke for: - -- A single PDF / URL ingestion (use the source-specific skill). -- A purely literature-review question with no Lean formalisation - angle — this skill's mathlib annotation loop makes it Lean-specific. - -## Output contract - -The skill does not write a new standalone directory. Instead it: - -1. Writes a `references/gaps.json` — machine-readable snapshot of the - current gap situation (resolved / gaps / ambiguous / other_knowledge - counts per concept). -2. Writes a `references//bibliography-ranked.json` — ordered list of - bibliography entries by in-text citation centrality, with `ingested` - flags and any arxiv / doi IDs surfaced. -3. Optionally triggers `pdf-to-knowledge` or `web-to-knowledge` to ingest - top-ranked gaps, then re-runs the concept index and re-scores. - -Both JSON files are regenerated on each invocation; they are safe to -commit if you want audit history, or to `.gitignore` for ephemeral runs. - -## Workflow - -1. **Identify the target.** Pull from the user's message one of: - - A slug that exists under `references/` (e.g. `arxiv-2202.03357`). - - A short goal description (e.g. "formalize Araki's relative - entropy in Lean 4") — in this case ask which existing slug is the - canonical source, and if none, suggest running `pdf-to-knowledge` - first. - -2. **Detect gaps.** - ```bash - python3 .claude/skills/gap-filler/scripts/detect_gaps.py \ - references/ --target-slug --output references/gaps.json - ``` - - When the project has a `goals.yaml` (a fixed list of formalization - targets — see "Side channels" below), pass - `--goals goals.yaml --lean-root ` so the report adds - per-goal status verified against the actual Lean declarations, plus - `goal_coverage_score = verified_declarations / total_declarations`. - The plain `coverage_score` keeps its meaning ("ingestion-wide - knowledge coverage"); use whichever the user asked for. Both can - appear in the same report. - The report lists every Key concepts bullet's status across all - ingested slugs: - - `formalized` — at least one source annotated - `→ formalized: QuantumSystem.X.y` (a local proof in this repo) and - `verify_mathlib_refs.py` did not flag it. Strongest tier; outranks - `resolved` because a local proof is harder evidence than a mathlib - match. - - `resolved` — at least one source annotated `→ mathlib: ...` and - `verify_mathlib_refs.py` did not flag it as `[UNVERIFIED]`. - - `suspect` — a source annotated `→ mathlib: ...` but - `verify_mathlib_refs.py` (run as part of pdf-to-knowledge / - web-to-knowledge step 4g / 6) could not confirm the declaration - exists — the annotation has an `[UNVERIFIED]` marker. Treat as a - stronger signal than `gap`: **the previous search was a false - positive** and the concept needs either a corrected annotation or - genuine formalisation. - - `partial` — target slug still annotates `→ needs formalization`, - but another ingested slug discusses the concept (e.g. a - Wikipedia / nLab page). Locally readable even though mathlib - lacks it. Often the most cost-effective tier to prioritise next. - - `gap` — target slug annotates `→ needs formalization` and no - other slug documents the concept. This is where future recursive - ingestion should focus. - - `ambiguous` — no annotation (rarely happens if previous - ingestions followed the SKILL.md contract). - - `other_knowledge` — concepts coming from non-target slugs (may - still be useful prerequisites). Its `status` field uses the - strongest available source-tier vocabulary (`formalized`, - `resolved`, `suspect`, `gap`, or `ambiguous`), including `suspect` - when the only known annotation is an `[UNVERIFIED]` false positive. - - When `--goals` is enabled, the goal block introduces two more status - vocabularies: - - goal-concept `status` may also be `unknown` — the concept was - pinned to specific `sources[*].slug` entries, but none of those - slugs currently document it. - - goal `goal_status` is one of `satisfied`, `partially_satisfied`, - `missing`, or `unverified`. - - Concept-name matching is normalised aggressively: `Connes cocycle`, - `Connes.cocycle`, `ConnesCocycle`, and `connes-cocycle` all merge. - camelCase / PascalCase identifiers are split on case transitions, - then lowercased and punctuation-stripped. - - The report also exposes `coverage_score`, a single 0..1 number - equal to - `(formalized × 1.0 + resolved × 1.0 + partial × 0.5) / target_total`. - Use this as the headline progress metric when reporting to the user — - it captures "local proof + mathlib coverage + local-reference - coverage" in one figure, so an ingestion that adds 3 Wikipedia pages - moves the number even when `formalized` and `resolved` stay flat. - -3. **Rank the target's bibliography by centrality.** - ```bash - python3 .claude/skills/gap-filler/scripts/rank_bibliography.py \ - references// --output references//bibliography-ranked.json - ``` - The ranking counts how many times each bibliography key is cited in - `content.md`, weighting the first 30 % of the body double (i.e. - abstract / intro / early theorems count more than late appendices). - Already-ingested entries (`ingested: true`) go to the back. - - For each **uninvested entry without arxiv / DOI** — the common case - for operator algebra, older physics and pre-2000 math — the script - also emits `suggested_search_queries`. The ordering switches on - publication year (`year >= 1995` favours university-domain PDFs; - `year < 1995` favours `lecture-notes` + NUMDAM / Project Euclid). - See [./references/workflow.md](./references/workflow.md) for the - calibration story and a concrete sample of the emitted queries. - -4. **Synthesise a plan.** Read both JSON files and compose a **short - ingestion plan**: - - a. For each gap concept, decide a primary source in this order: - - 1. **arxiv / DOI in the bibliography** — top of the ranked list - with a discoverable URL → `pdf-to-knowledge`. - 2. **University lecture-notes PDF** — when the entry has no arxiv - ID but the topic is covered in standard graduate curricula - (subfactor theory, modular theory, KMS states, entropy - inequalities). Feed the `suggested_search_queries` to - `WebSearch`, pick the most reputable PDF hit (faculty page > - course page > personal blog), hand it to `pdf-to-knowledge`. - For math-heavy gaps lecture notes usually beat Wikipedia - (single author, coherent notation); see - [./references/workflow.md](./references/workflow.md) for the - comparison. - 3. **Wikipedia / nLab** — good for broadly-known math - (`Von_Neumann_entropy`, `Tomita–Takesaki_theory`) and as a - zero-effort fallback when the lecture-notes search returns - nothing → `web-to-knowledge`. - 4. **Needs human action** — no URL discoverable via the above; - ask the user. - - b. **Bound the plan**. By default pick at most 3 ingestions per - invocation to avoid runaway recursion. Increase only when the user - explicitly opts in. - c. Present the plan to the user before executing — list the 3 - proposed ingestions with their rationale (gap they close, citation - score if applicable, source type, URL found via which search - strategy). - -5. **Execute (with user OK).** For each approved entry in the plan: - - If the source is an arxiv ID: invoke `pdf-to-knowledge` with - `https://arxiv.org/pdf/`. - - If the source is a DOI with a direct open PDF URL, or an open-access - publisher PDF URL discovered from that DOI: invoke `pdf-to-knowledge` - with the PDF URL. If only a DOI landing page is available, ask before - using a weaker HTML fallback. - - If the source is a discovered university / course / faculty PDF URL: - invoke `pdf-to-knowledge` with that PDF URL. - - If the source is a Wikipedia / nLab URL: invoke - `web-to-knowledge` with that URL. - - After **each** ingestion, re-run - `python3 .claude/skills/pdf-to-knowledge/scripts/update_concepts.py references/` - so `CONCEPTS.md` and downstream diagnostics stay consistent. - -6. **Re-measure and report.** Re-run step 2 to produce a new - `gaps.json`, then report the delta to the user: how many gaps closed, - how many remain, which ones might require a different source type. - Do not loop automatically — hand control back to the user and wait - for them to approve another round. - -## Side channels - -Three optional outputs that complement `gaps.json`. Every one has a -worked example in [./references/workflow.md](./references/workflow.md); -the summary below gives just the contract. - -- **`references/gaps-history.jsonl`** — append-only JSONL, one record per - `detect_gaps.py` run with `timestamp / coverage_score / - goal_coverage_score / counts / issues_open`. Use it to attribute - progress to specific ingestions. `--no-history` suppresses; - `--history-log ` redirects. - -- **`references/issues/*.yaml`** — file-system tracker for items the - pipeline could not auto-resolve (`[UNVERIFIED]` mathlib refs, skipped - notation candidates, open questions). `verify_mathlib_refs.py - mark-unverified` opens these automatically (idempotent on - `kind + slug + concept + candidate`); manual ones go through - `scripts/issues.py {add,list,close}`. `detect_gaps.py` reports - `issues.open` in its summary. - -- **`goals.yaml`** (project root, checked into git) — pins the - formalization targets so the headline metric tracks "what we promised - to prove", not "what we happened to ingest". Pass - `--goals goals.yaml --lean-root ` to `detect_gaps.py`. - - **Truth source = `declarations`.** Each goal lists fully-qualified - Lean declaration names (`Matrix.vonNeumannEntropy_nonneg`, - `gelfand_naimark_theorem`, …). The script walks `/**/*.lean`, - builds the set of every `theorem`/`def`/`structure`/`class`/ - `instance`/`inductive`/`abbrev`/`axiom` (tracking `namespace`/`end` - stacks), and compares. Verified ⇒ goal counted toward - `goal_coverage_score`. Without `--lean-root`, declarations are - reported as `unverified` (no guess). - - Malformed goal entries are a hard error, not a soft fallback: missing - `id` / `description`, non-list `declarations` / `concepts`, or a - structured `sources:` entry without a `slug` makes `detect_gaps.py` - exit 2 with an `[error] invalid goals file: ...` message. - - **`concepts` is informational** — prerequisite knowledge hints used - by `gap-filler`'s plan output, not by the satisfaction judgement. A - concept may be a bare string (any INDEX.md mentioning it counts) or - a structured entry pinning the prerequisite to specific slugs and - optional anchors: - - ```yaml - goals: - - id: gns-construction - description: "Cyclic representation π_ω …" - declarations: - - GNS.Representation - - GNS.Construction.isFaithful_iff_separating - concepts: - - GNS representation # bare string - - name: Tomita-Takesaki modular operator # structured - sources: - - { slug: arxiv-2507.00900, anchor: { theorem: "2.3" } } - - { slug: "92737", anchor: { definition: "modular operator" } } - - - id: ssa-abstract-local-net - description: "TODO." - declarations: [] # → counts as 1 missing - concepts: - - name: localNet - sources: - - { slug: "92737" } - ``` - - `anchor` is a single-key map keyed by the locator kind - (`chapter` / `section` / `subsection` / `theorem` / `definition` / - `lemma` / `proposition` / `corollary` / `equation` / `page` / - `example`). It is human-readable navigation; the matcher only uses - `slug` for filtering. - - **Coverage formula.** `goal_coverage_score = - verified_declarations / total_declarations`, where a goal with - `declarations: []` contributes 1 to the denominator (an unfulfilled - TODO slot, so the score does not silently inflate). - -## Why this design - -- **Bounded, user-visible plan.** Gap-filling has blast radius (each - ingestion is multi-minute CPU work plus disk space). Showing the 3 - proposed sources before executing lets the user redirect instead of - burning compute on the wrong path. -- **Citation centrality, not just "first in list".** The original user - request specifically called out the heuristic: *"引用数の上位や参考 - アイデアと引用位置の近さ"* — we implement both via the weighted - count in `rank_bibliography.py`. -- **Reuse existing skills.** The recursion engine is Claude-orchestrated - markdown, not a new binary. Adding a source type = teaching Claude to - dispatch to a different sibling skill, not rewriting a DAG. -- **Concept index is the ground truth.** `CONCEPTS.md` already unifies - PDF and web sources. Every script here reads from that layer rather - than re-parsing raw papers. - -## Runtime prerequisites - -- **Sibling skills available.** `pdf-to-knowledge` and - `web-to-knowledge` must be installed under `.claude/skills/` with - their own dependencies in place. This skill does not re-implement - their work. -- **`uv`** on PATH (same reason as the sibling skills). -- No other Python deps — the scripts here use stdlib only. - -## Failure modes to watch for - -- **Bibliography is empty or absent.** The target slug's INDEX.md was - ingested before bibliography extraction was added, or the paper had - no `## References`. In that case `rank_bibliography.py` returns an - empty list; pivot to web sources for the identified gaps. -- **Every top-ranked entry lacks a discoverable URL.** Older math papers - cite each other in print journals. If none of the top entries have - arxiv / doi, the skill should suggest a Wikipedia article on the - concept instead of forcing the user to chase a hard-to-find PDF. -- **Recursive cycles.** If paper A cites paper B and B cites A, the - default budget of 3 ingestions / round prevents a loop. -- **Goal outside the current references set.** If the user provides a - goal string with no matching slug, instruct them to run - `pdf-to-knowledge` on a primary source first, then come back here. - -See [./references/workflow.md](./references/workflow.md) for a worked -example on the Araki relative-entropy paper, including the expected -`gaps.json` output and a sample 3-step ingestion plan. diff --git a/.claude/skills/gap-filler/references/workflow.md b/.claude/skills/gap-filler/references/workflow.md deleted file mode 100644 index e1ea785..0000000 --- a/.claude/skills/gap-filler/references/workflow.md +++ /dev/null @@ -1,336 +0,0 @@ -# gap-filler workflow — worked example - -Concrete trace of running the skill on the Araki / Longo-Witten paper -(`arxiv-2202.03357`) from `references/` in this project. Use this as a -reference when you need to calibrate expectations against the synthetic -behaviour described in `SKILL.md`. - -## Starting state - -``` -references/ -├── CONCEPTS.md # aggregated concept index -├── arxiv-2202.03357/ -│ ├── INDEX.md # Longo–Witten "A note on continuous entropy" -│ ├── content.md # MinerU output with 184 LaTeX spans -│ └── assets/ # empty (no figures) -``` - -INDEX.md has 14–22 Key concepts bullets (varies across iterations) and a -bibliography of ~24 entries with author-year keys (Ara76, Kos86a, Tak03, -Wit21, …). - -## 0. Optional: pin formalization targets (`goals.yaml`) - -Without a goals file, `coverage_score` reports knowledge ingestion -coverage (does mathlib have a home for the concepts mentioned in -ingested papers?). To track *project-level* progress — "have we -proven the theorems we promised?" — anchor on a `goals.yaml` listing -**the actual Lean declarations** that constitute each promise: - -```yaml -goals: - - id: gns-construction - description: "Cyclic representation π_ω with ω(a)=⟨Ω_ω, π_ω(a) Ω_ω⟩." - declarations: - - GNS.Representation - - GNS.Construction.isFaithful_iff_separating - - GNS.Representation.unique_up_to_unitary_equivalence - concepts: - - GNS representation # bare string - - - id: ssa-abstract-local-net - description: "TODO. Abstract SSA over a generic local net." - declarations: [] # empty → counts as 1 missing - concepts: - - name: localNet - sources: - - { slug: "92737", anchor: { definition: "localNet" } } - - name: Tomita-Takesaki modular operator - sources: - - { slug: arxiv-2507.00900, anchor: { theorem: "2.3" } } - - { slug: arxiv-2507.00900, anchor: { section: "2.1" } } -``` - -**Two parallel concerns**, kept separate so each is judged by the -right tool: - -| Field | Truth source | Influences `goal_coverage_score`? | -| --- | --- | --- | -| `declarations` | scanner over `/**/*.lean` | **yes** | -| `concepts` | `references//INDEX.md` Key concepts bullets | no — pure hint for `gap-filler` planning | - -`anchor` is a single-key map; the supported kinds are `chapter`, -`section`, `subsection`, `theorem`, `definition`, `lemma`, -`proposition`, `corollary`, `equation`, `page`, `example`. The -matcher uses only `slug` for filtering — the kind/value pair is -human-readable navigation so a developer (or `gap-filler`'s plan -output) knows exactly which page to consult. - -Pass `--goals goals.yaml --lean-root QuantumSystem` (or whatever your -Lean tree is) to step 1 to get the per-goal block. Without -`--lean-root`, declarations are tagged `unverified` rather than -guessed — `goal_coverage_score` then reads as the absence of evidence. - -## 1. Detect gaps - -```bash -python3 .claude/skills/gap-filler/scripts/detect_gaps.py \ - references/ --target-slug arxiv-2202.03357 --output references/gaps.json -``` - -Expected shape of `gaps.json` (truncated): - -```json -{ - "target_slug": "arxiv-2202.03357", - "counts": { - "formalized": 1, - "resolved": 2, - "partial": 0, - "suspect": 1, - "gaps": 10, - "ambiguous": 0, - "other_knowledge": 0 - }, - "issues": {"open": 1, "directory": "references/issues"}, - "coverage_score": 0.214, - "formalized": [ - {"concept": "Umegaki relative entropy", "sources": [...], - "formalized_annotation": "→ formalized: QuantumSystem.RelativeEntropy.umegaki"} - ], - "resolved": [ - {"concept": "C*-algebra", - "mathlib_annotation": "→ mathlib: Mathlib.Analysis.CStarAlgebra.Classes", - "sources": [{"slug": "arxiv-2202.03357", "annotation": "..."}]} - ], - "suspect": [ - {"concept": "Modular automorphism group", - "mathlib_annotation": "→ mathlib: Mathlib.Foo.Bar [UNVERIFIED] (...)", - "sources": [...]} - ], - "gaps": [ - {"concept": "Araki relative entropy", "sources": [...]}, - {"concept": "Kosaki variational formula", "sources": [...]}, - {"concept": "Jones index", "sources": [...]} - ] -} -``` - -With `--goals goals.yaml --lean-root QuantumSystem` the report also -carries: - -```json -{ - "lean_root": "/abs/path/QuantumSystem", - "lean_declarations_count": 714, - "goals_summary": { - "total": 8, - "satisfied": 7, - "partially_satisfied": 0, - "missing": 1, - "unverified": 0, - "declarations_total": 20, - "declarations_verified": 19, - "goal_coverage_score": 0.95, - "formula": "verified_declarations / total_declarations" - }, - "goals": [ - {"id": "gns-construction", - "goal_status": "satisfied", - "declarations": [ - {"name": "GNS.Representation", "status": "verified"}, - {"name": "GNS.Construction.isFaithful_iff_separating", "status": "verified"} - ], - "missing_declarations": [], - "concepts": [{"name": "GNS representation", - "status": "gap", - "matched_sources": [{"slug": "92737", "annotation": "..."}]}]}, - {"id": "ssa-abstract-local-net", - "goal_status": "missing", - "declarations": [], - "missing_declarations": [], - "missing_concepts": ["Tomita-Takesaki modular operator"]} - ] -} -``` - -The TODO goal contributes 1 to `declarations_total` (so empty -`declarations: []` does not silently inflate the score) but 0 to -`declarations_verified`. - -## 2. Rank the bibliography - -```bash -python3 .claude/skills/gap-filler/scripts/rank_bibliography.py \ - references/arxiv-2202.03357/ \ - --output references/arxiv-2202.03357/bibliography-ranked.json -``` - -Expected top of `uninvested_top` (abridged; each entry now also carries -a `suggested_search_queries` list). **Query ordering depends on the -entry's publication year**: - -- ``year >= 1995`` (modern): `university-pdf:` rotation is listed - first (faculty pages often host the paper or author's lecture notes), - then `lecture-notes`, then `nlab`/`wikipedia`. -- ``year < 1995`` (legacy): `lecture-notes` leads, followed by the - open-access archives `numdam` and `projecteuclid` and a bare - `filetype:pdf` pass. University-PDF rotation is **demoted to the - tail** — iteration-2 showed university domains almost never host - 1980s-era operator-algebra journal papers, so burning WebSearch - budget on them first is wasteful. - -The cutoff is empirical (1995 ≈ onset of institutional preprint -hosting) and editable in -`gap-filler/scripts/rank_bibliography.py::PRE_INSTITUTIONAL_HOSTING_YEAR`. -When `year` is absent from the bibliography entry, the modern ordering -is used as a safe default. - -```json -[ - { - "key": "Jon83", "title": "Index for subfactors", - "authors": ["Vaughan Jones"], "year": 1983, - "arxiv": null, "doi": null, "ingested": false, - "citations_raw": 13, "citation_score": 19.0, - "matched_token": "Jon83", - "suggested_search_queries": [ - {"strategy": "lecture-notes", - "query": "Index subfactors Jones \"lecture notes\" filetype:pdf"}, - {"strategy": "numdam", - "query": "Index subfactors Jones site:numdam.org"}, - {"strategy": "project-euclid", - "query": "Index subfactors Jones site:projecteuclid.org"}, - {"strategy": "open-access-pdf", - "query": "Index subfactors Jones filetype:pdf"}, - {"strategy": "nlab", "query": "Index subfactors site:ncatlab.org"}, - {"strategy": "wikipedia", "query": "Index subfactors wikipedia"}, - {"strategy": "university-pdf:caltech.edu", - "query": "Index subfactors Jones pdf site:caltech.edu"} - ] - } -] -``` - -## 3. Synthesise a plan - -Cross-reference gaps with the ranked bibliography. **When a top entry has -no arxiv/DOI (the common case for operator algebra), try its -`suggested_search_queries` with WebSearch before falling back to -Wikipedia**. For legacy entries this means lecture notes / open-access -archives first; for modern entries it means faculty-hosted PDFs first. -Both routes usually give a cleaner treatment than encyclopedia pages: - -| Gap | Best source (preferred) | Mechanism | -|---|---|---| -| `Jones index` (Jon83) | lecture-notes PDF or open-access archive (try `lecture-notes` → `numdam` → `project-euclid` via the emitted queries) | `pdf-to-knowledge` once a PDF URL is found | -| `Kosaki variational formula` (Kos86a) | lecture-notes / open-access PDF first, nLab as fallback | `pdf-to-knowledge` or `web-to-knowledge` depending on what turns up | -| `Modular operator` / `Tomita-Takesaki` (Tak03) | Wikipedia is fine here — it is a well-maintained article | `web-to-knowledge` | - -Fall back to Wikipedia / nLab only when the PDF / archive search finds -nothing relevant. The trade-off: WebSearch costs a few tool calls per -concept, but the resulting PDFs tend to carry definitions, proofs, and -worked examples in one place — far more useful than stitching together -a Wikipedia stub plus scattered nLab entries. - -A typical plan covers the 3 gaps with the highest intersection of -"blocks many other concepts" and "has a discoverable URL" — the three -rows above. - -## 4. Execute (with user approval) - -For the first two entries, call `web-to-knowledge`: - -```bash -uv run .claude/skills/web-to-knowledge/scripts/fetch.py \ - "https://en.wikipedia.org/wiki/Tomita%E2%80%93Takesaki_theory" -uv run .claude/skills/web-to-knowledge/scripts/fetch.py \ - "https://ncatlab.org/nlab/show/relative+entropy" -``` - -Then **always** refresh the concept index: - -```bash -python3 .claude/skills/pdf-to-knowledge/scripts/update_concepts.py references/ -``` - -## 5. Re-measure - -Run step 1 again. Expected delta: - -- `resolved` grows by any concepts that now carry a `→ mathlib:` - annotation on the freshly ingested INDEX.md (Wikipedia Tomita-Takesaki - typically resolves `VonNeumannAlgebra.commutant`; nLab relative - entropy resolves `InformationTheory.klDiv`, `MeasureTheory.rnDeriv`). -- `gaps` shrinks by the resolved count. -- `other_knowledge` grows — bullets from the new slugs that the target - paper does not mention directly but which supplement the topic. - -## Side channel — issue tracker (`references/issues/`) - -When `verify_mathlib_refs.py mark-unverified` flags a bullet, it -auto-creates a YAML under `references/issues/` so the false positive is -not forgotten across sessions. Re-running the same loop is a no-op (the -dedup key is `kind + slug + concept + candidate`): - -```bash -# Manually open an issue (e.g. paper-notation-refactor skipped a candidate) -python3 .claude/skills/gap-filler/scripts/issues.py add references \ - --kind notation-skipped \ - --slug arxiv-2202.03357 \ - --concept "modular automorphism group" \ - --reason "Mathlib scope conflict on σ" - -python3 .claude/skills/gap-filler/scripts/issues.py list references -python3 .claude/skills/gap-filler/scripts/issues.py close references \ - --kind unverified-mathlib-ref --slug arxiv-2202.03357 \ - --closed-by "human review" -``` - -`detect_gaps.py` surfaces `report.issues.open`; treat that count as the -"system already knows about" backlog complementary to `gaps`. A typical -pattern across one ingestion cycle: - -``` -baseline: formalized=1 resolved=1 gap=12 issues=0 cov=0.214 -after lean_verify rejects modular operator: - formalized=1 suspect=1 gap=12 issues=1 cov=0.143 -after notation-refactor adds → formalized: for it: - formalized=2 resolved=0 gap=12 issues=0 cov=0.214 -``` - -Note that the third row's `coverage_score` returns to baseline even -though the underlying state changed — the project just discovered that -mathlib's annotation was wrong and replaced it with a local proof. The -trajectory shows up clearly in the history log. - -## Side channel — progress history (`references/gaps-history.jsonl`) - -Each `detect_gaps.py` run appends one record. Quick way to see what -moved the needle: - -```bash -jq -c '{ts: .timestamp, cov: .coverage_score, goal: .goals.goal_coverage_score, issues: .issues_open, fmlz: .counts.formalized, susp: .counts.suspect}' \ - references/gaps-history.jsonl -``` - -Sample output across a 5-step cycle: - -``` -{"ts":"2026-05-04T10:00:00Z","cov":0.5,"goal":0.667,"issues":0,"fmlz":1,"susp":0} -{"ts":"2026-05-04T10:05:00Z","cov":0.25,"goal":0.333,"issues":1,"fmlz":1,"susp":1} -{"ts":"2026-05-04T10:10:00Z","cov":0.5,"goal":0.667,"issues":0,"fmlz":2,"susp":0} -``` - -Pass `--no-history` to suppress, or `--history-log path/elsewhere.jsonl` -to redirect when the default `references/` tree is gitignored. - -## When to stop - -- `gaps` shrinks to a stable set whose entries are genuinely unformalised - in mathlib (no amount of recursive ingestion helps). -- The only remaining sources lack discoverable URLs (pre-arxiv 1970s - math journals) — promote those to the user as "needs human action". -- The ingestion budget (default 3 per invocation) has been used; ask the - user before approving another round. diff --git a/.claude/skills/gap-filler/scripts/detect_gaps.py b/.claude/skills/gap-filler/scripts/detect_gaps.py deleted file mode 100644 index 26b7904..0000000 --- a/.claude/skills/gap-filler/scripts/detect_gaps.py +++ /dev/null @@ -1,1319 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.10" -# dependencies = [] -# /// -"""Scan a ``references/`` tree and report which Key concepts are still gaps. - -A concept is classified by looking at the annotation on its Key concepts -bullet across every ``/INDEX.md`` under the root: - -- ``→ formalized: QuantumSystem.X.y`` → **formalized** (already proven in - this repository — outranks ``resolved`` for prioritisation). -- ``→ mathlib: Mathlib.X.y`` → **resolved** (mathlib already has it). -- ``→ needs formalization`` → **gap** (no mathlib coverage). -- No annotation → **ambiguous** (treat as potential gap). - -Concepts with the same normalised name coming from multiple sources are -merged; the aggregate classification picks the strongest tier present — -``formalized`` beats ``resolved`` beats ``partial`` beats ``gap`` beats -``ambiguous``. ``suspect`` (a mathlib annotation that ``verify_mathlib_refs.py`` -flagged as ``[UNVERIFIED]``) is reported separately so a previously -trusted answer that turned out wrong is more visible than a fresh gap. - -Optionally, ``--goals path/to/goals.yaml`` constrains the coverage score -to a fixed list of target concepts (the project's actual formalization -goals). Without ``--goals`` the universe is "every concept ingested under -references/" — useful for raw progress, but the denominator drifts as -new papers are added. With ``--goals`` the score answers the operative -question: "of the concepts I committed to formalize, what fraction is -done?". Goal concepts not yet present in any INDEX.md are classified as -``unknown`` and counted as gaps for the coverage formula. - -Usage: - python3 detect_gaps.py [--target-slug SLUG] - [--output gaps.json] - -When ``--target-slug`` is given, the output keys `resolved`/`gaps` are -restricted to concepts that originate from that slug's INDEX.md; other -slugs' concepts appear under `other_knowledge` (useful context but not -what the caller is trying to formalise). When omitted, every slug is -treated symmetrically. - -Exits 0 on success. Prints the JSON report to stdout unless ``--output`` -is specified. -""" - -from __future__ import annotations - -import argparse -import datetime as _dt -import json -import re -import sys -from collections import defaultdict -from pathlib import Path - - -KEY_CONCEPTS_HEADER = re.compile(r"^##\s+Key concepts\s*$", re.IGNORECASE) -NEXT_H2 = re.compile(r"^##\s+") -BULLET = re.compile(r"^\s*-\s+(.*)$") -LEADING_CODE = re.compile(r"^`([^`]+)`(.*)$") -MATHLIB_ANNOT = re.compile(r"→\s*mathlib\s*:", re.IGNORECASE) -FORMALIZED_ANNOT = re.compile(r"→\s*formalized\s*:", re.IGNORECASE) -NEEDS_FORMAL_ANNOT = re.compile(r"→\s*needs\s*formalization", re.IGNORECASE) -UNVERIFIED_MARKER = re.compile(r"\[UNVERIFIED\]", re.IGNORECASE) -LEAN_DECL_NAME = re.compile(r"^[A-Za-z_][\w']*(?:\.[A-Za-z_][\w']*)*$") - - -class GoalsValidationError(ValueError): - """Raised when goals.yaml violates the supported contract.""" - - -def _strip_front_annotation_markers(body: str) -> tuple[str, str]: - """Split a Key concepts bullet body into (description, annotation).""" - m = re.search(r"\s*→\s", body) - if not m: - return body.strip(), "" - return body[: m.start()].strip(), body[m.start() :].strip() - - -# LaTeX command → Unicode character. Keeps the merge key stable whether the -# author wrote ``\alpha``, ``α``, or ``alpha``. Longest commands first so -# ``\infty`` beats ``\in`` and ``\Vert`` beats ``\V`` on the ordered walk. -LATEX_TO_UNICODE: dict[str, str] = { - # Greek lowercase (most common). - r"\alpha": "α", r"\beta": "β", r"\gamma": "γ", r"\delta": "δ", - r"\epsilon": "ε", r"\varepsilon": "ε", r"\zeta": "ζ", r"\eta": "η", - r"\theta": "θ", r"\vartheta": "θ", r"\iota": "ι", r"\kappa": "κ", - r"\lambda": "λ", r"\mu": "μ", r"\nu": "ν", r"\xi": "ξ", - r"\pi": "π", r"\varpi": "π", r"\rho": "ρ", r"\varrho": "ρ", - r"\sigma": "σ", r"\varsigma": "σ", r"\tau": "τ", r"\upsilon": "υ", - r"\phi": "φ", r"\varphi": "φ", r"\chi": "χ", r"\psi": "ψ", - r"\omega": "ω", - # Greek uppercase. - r"\Gamma": "Γ", r"\Delta": "Δ", r"\Theta": "Θ", r"\Lambda": "Λ", - r"\Xi": "Ξ", r"\Pi": "Π", r"\Sigma": "Σ", r"\Upsilon": "Υ", - r"\Phi": "Φ", r"\Psi": "Ψ", r"\Omega": "Ω", - # Math symbols the operator-algebra / QFT canon uses constantly. - r"\infty": "∞", - r"\partial": "∂", - r"\nabla": "∇", - r"\mathbb{N}": "ℕ", r"\mathbb{Z}": "ℤ", r"\mathbb{Q}": "ℚ", - r"\mathbb{R}": "ℝ", r"\mathbb{C}": "ℂ", r"\mathbb{H}": "ℍ", - r"\mathbb{F}": "𝔽", r"\mathbb{P}": "ℙ", r"\mathbb{E}": "𝔼", - r"\mathbb{1}": "𝟙", - r"\emptyset": "∅", r"\varnothing": "∅", - r"\hbar": "ℏ", r"\ell": "ℓ", - r"\dagger": "†", r"\star": "⋆", - # Relations / operators often embedded in concept names. - r"\le": "≤", r"\leq": "≤", r"\ge": "≥", r"\geq": "≥", - r"\ne": "≠", r"\neq": "≠", r"\equiv": "≡", - r"\subset": "⊂", r"\subseteq": "⊆", - r"\supset": "⊃", r"\supseteq": "⊇", - r"\in": "∈", r"\notin": "∉", - r"\to": "→", r"\mapsto": "↦", r"\implies": "⇒", - r"\Rightarrow": "⇒", r"\Leftarrow": "⇐", r"\Leftrightarrow": "⇔", - r"\cup": "∪", r"\cap": "∩", r"\setminus": "∖", - r"\times": "×", r"\otimes": "⊗", r"\oplus": "⊕", r"\odot": "⊙", - r"\cdot": "·", - # Norms / brackets commonly mangled across editors. - r"\Vert": "‖", r"\|": "‖", r"\|\|": "‖", - r"\langle": "⟨", r"\rangle": "⟩", - # Spacing commands we just drop. - r"\,": " ", r"\;": " ", r"\:": " ", r"\!": "", r"\~": " ", -} - -# Unicode math symbols → ASCII-ish tokens so ``α`` / ``\alpha`` / ``alpha`` -# all collapse to the same bucket. Applied after LATEX_TO_UNICODE, so it -# normalises both original Unicode text and LaTeX-derived Unicode. -UNICODE_TO_ASCII: dict[str, str] = { - # Greek lowercase. - "α": " alpha ", "β": " beta ", "γ": " gamma ", "δ": " delta ", - "ε": " epsilon ", "ζ": " zeta ", "η": " eta ", "θ": " theta ", - "ι": " iota ", "κ": " kappa ", "λ": " lambda ", "μ": " mu ", - "ν": " nu ", "ξ": " xi ", "π": " pi ", "ρ": " rho ", - "σ": " sigma ", "τ": " tau ", "υ": " upsilon ", "φ": " phi ", - "ϕ": " phi ", "χ": " chi ", "ψ": " psi ", "ω": " omega ", - # Greek uppercase. - "Γ": " gamma ", "Δ": " delta ", "Θ": " theta ", "Λ": " lambda ", - "Ξ": " xi ", "Π": " pi ", "Σ": " sigma ", "Υ": " upsilon ", - "Φ": " phi ", "Ψ": " psi ", "Ω": " omega ", - # Math symbols. - "∞": " infty ", "∂": " partial ", "∇": " nabla ", - "ℕ": " nat ", "ℤ": " int ", "ℚ": " rat ", "ℝ": " real ", - "ℂ": " complex ", "ℍ": " quat ", "𝔽": " field ", "ℙ": " proj ", - "𝔼": " expect ", "𝟙": " one ", - "∅": " empty ", "ℏ": " hbar ", "ℓ": " ell ", - "†": " dagger ", "⋆": " star ", - "≤": " le ", "≥": " ge ", "≠": " ne ", "≡": " equiv ", - "⊂": " subset ", "⊆": " subseteq ", - "⊃": " supset ", "⊇": " supseteq ", - "∈": " in ", "∉": " notin ", - "→": " to ", "↦": " mapsto ", "⇒": " implies ", - "⇐": " leftarrow ", "⇔": " iff ", - "∪": " cup ", "∩": " cap ", "∖": " setminus ", - "×": " times ", "⊗": " otimes ", "⊕": " oplus ", "⊙": " odot ", - "·": " cdot ", - "‖": " norm ", "⟨": " langle ", "⟩": " rangle ", - # Punctuation variants that frequently appear in math identifiers. - "–": "-", "—": "-", # en-dash / em-dash → hyphen -} - -# Sort LaTeX commands by descending length so longer matches win (``\Vert`` -# before ``\V``, ``\mathbb{N}`` before ``\mathbb``). -_LATEX_ORDERED = sorted(LATEX_TO_UNICODE.items(), key=lambda kv: -len(kv[0])) - - -def _expand_latex(text: str) -> str: - r"""Apply ``LATEX_TO_UNICODE`` in a single pass. - - Uses string replacement (not regex) because TeX command boundaries are - semantic: ``\alpha2`` should still replace ``\alpha`` → ``α``, producing - ``α2``. A regex with ``\b`` would miss that because ``\`` is not a word - char to Python's regex engine, but pure substring replacement works. - """ - for command, replacement in _LATEX_ORDERED: - if command in text: - text = text.replace(command, replacement) - return text - - -def _unicode_to_ascii(text: str) -> str: - for symbol, replacement in UNICODE_TO_ASCII.items(): - if symbol in text: - text = text.replace(symbol, replacement) - return text - - -def _normalise_concept(display: str) -> str: - """Aggressive normalisation so concept names with different spellings merge. - - The merge key has to be stable across: - - - camelCase / PascalCase: ``Connes cocycle`` == ``ConnesCocycle`` - - punctuation: ``Connes cocycle`` == ``Connes.cocycle`` == ``connes-cocycle`` - - acronyms: ``TypeIIIFactor`` == ``Type III factor`` - - **LaTeX vs Unicode vs ASCII** (R1, iter-12): - ``Type II_\\infty`` == ``Type II_∞`` == ``Type II_infty`` - ``S(\\phi\\Vert\\psi)`` == ``S(φ‖ψ)`` == ``S(phi norm psi)`` - - Pipeline: - 0. Expand LaTeX commands to Unicode (``\\alpha`` → ``α``). - 1. Collapse Unicode math symbols to ASCII tokens (``α`` → `` alpha ``, - ``‖`` → `` norm ``, ``∞`` → `` infty ``). - 2. Split camelCase / PascalCase boundaries with a space. - 3. Lowercase. - 4. Replace any run of non-alphanumeric characters with a single space. - 5. Collapse whitespace. - """ - tokenised = _expand_latex(display) - tokenised = _unicode_to_ascii(tokenised) - # Split lower→upper (fooBar) and acronym→word (HTTPServer, TypeIIIFactor). - tokenised = re.sub(r"(?<=[a-z0-9])(?=[A-Z])", " ", tokenised) - tokenised = re.sub(r"(?<=[A-Z])(?=[A-Z][a-z])", " ", tokenised) - tokenised = tokenised.lower() - tokenised = re.sub(r"[^a-z0-9]+", " ", tokenised) - return " ".join(tokenised.split()) - - -def parse_key_concepts(md: str) -> list[dict]: - """Yield ``{name, display, description, annotation, status}`` for each bullet. - - ``name`` is aggressively normalised so bullets differing only by - punctuation (``Connes.cocycle`` vs ``Connes cocycle``) or case - (``typeIIIFactor`` vs ``Type III factor``) merge in - ``classify_across_slugs``. - """ - lines = md.splitlines() - inside = False - entries: list[dict] = [] - for line in lines: - if KEY_CONCEPTS_HEADER.match(line): - inside = True - continue - if inside and NEXT_H2.match(line): - break - if not inside: - continue - bullet = BULLET.match(line) - if not bullet: - continue - body = bullet.group(1).strip() - if not body or body.startswith(" + + +## Not checked + +``` + +If nothing needed fixing, say so explicitly (✅) together with the scope +covered — a clean report over the wrong scope is worse than no report. diff --git a/.claude/skills/lint-fix/references/rules.md b/.claude/skills/lint-fix/references/rules.md new file mode 100644 index 0000000..6aff996 --- /dev/null +++ b/.claude/skills/lint-fix/references/rules.md @@ -0,0 +1,69 @@ +# Project Lint Rules + +The authoritative statement of this repository's editing hygiene, prohibited +tokens, commit style, and style guidelines. `AGENTS.md` points here rather than +restating them, and the `lint-fix` skill detects and fixes violations of them — +so this file is the one place to edit when a rule changes. + +Each rule carries the reason it exists where the reason is not self-evident. The +reasons are load-bearing: they are what lets you judge an edge case the bullet +does not literally cover, instead of guessing. + +## Editing Hygiene + +- Spaces only, never tabs. + **Why:** Mathlib style; mixed whitespace breaks `lake exe runLinter`. +- Never modify `lakefile.toml`, `lean-toolchain`, or `lake-manifest.json`. + **Why:** the toolchain and manifest are pinned intentionally; accidental edits cascade into reproducibility failures. +- Write comments in English. +- Never create namespaces or sections named `QuantumSystem`. + **Why:** the module path already prefixes every declaration; an extra namespace would produce `QuantumSystem.QuantumSystem.Foo`. + +## Prohibited Tokens + +The following tokens are strictly prohibited in Lean sources, grouped by reason. + +- *Unsound or deferred proofs:* `sorry`, `admit`, `axiom`. + **Why:** the project targets a fully axiom-free formalization; assumptions smuggled into structure fields count as axioms too. +- *Global configuration and unsafe code:* `set_option`, `unsafe`. + **Why:** these mutate kernel or elaborator behavior project-wide, or bypass soundness. +- *Compiler and metaprogramming internals:* `System`, `open System`, `Lean.Elab`, `Lean.Meta`, `Lean.Compiler`. + **Why:** this is a mathematics repository, not a tactic-library repository; depending on internals creates brittle code. + +## Commit Style + +`lefthook` + `commitizen` (`cz check`, configured in `pyproject.toml`) enforce this; the accepted +vocabulary is: + +- Conventional Commits: `feat` / `fix` / `chore` / `docs` / `refactor` / `test` / `perf`. +- Lowercase type, colon, imperative subject. Example: `feat: add GNS faithfulness lemma`. +- One logical change per commit. + +The hook runs the stock `cz_conventional_commits` schema, which also accepts `build` / `ci` / +`style` / `revert` / `bump`; the list above is the deliberately narrower project convention. + +## Style Guidelines + +The Mathlib contribute templates are authoritative; the bullets below distill what actually comes up during edits. + +**Naming.** +- `lowerCamelCase` for terms and definitions (`gnsRepresentation`, `isPureState`). +- `UpperCamelCase` for types, structures, and propositions (`CStarAlgebra`, `IsState`). +- Theorem names use `_` as word separator (`norm_add_le`, `inner_self_nonneg`). +- Prefer the `_of_` pattern for implications (`continuous_of_lipschitz`); `iff` joins equivalences; `not_` prefixes negations. + +**Layout.** +- 120-column line limit. +- 2-space indentation; `by` stays on the same line as the goal it opens unless the resulting line would exceed the limit. +- Hoist shared hypotheses into `variable` blocks; keep explicit/implicit arity consistent with sibling lemmas. +- Align `calc` steps on the relation; use `·` (centered dot) for focused goals, not `case _ =>`. + +**Docstrings.** +- Every public declaration gets a `/-- ... -/` docstring whose first sentence is a self-contained summary. +- Module docs (`/-! # Title ... -/`) at the top of each file describe the content and any non-obvious conventions. + +**References** (fetch when a rule above is ambiguous): + +- https://github.com/leanprover-community/leanprover-community.github.io/blob/lean4/templates/contribute/doc.md +- https://github.com/leanprover-community/leanprover-community.github.io/blob/lean4/templates/contribute/naming.md +- https://github.com/leanprover-community/leanprover-community.github.io/blob/lean4/templates/contribute/style.md diff --git a/.claude/skills/math-extract/SKILL.md b/.claude/skills/math-extract/SKILL.md new file mode 100644 index 0000000..5f5b832 --- /dev/null +++ b/.claude/skills/math-extract/SKILL.md @@ -0,0 +1,397 @@ +--- +name: math-extract +description: Extract the mathematics of an object from the literature before any Lean is written — definitional variants and their conventions, results and what they rest on, each hypothesis sorted into provable / model-dependent / open, degenerate cases, and rejected formulations with the object that rules each out — via parallel per-lane `math-extractor` sub-agents. Writes a tracked note under docs/math/; proposes no Lean. +argument-hint: " [-- arXiv id | pdf path | url]" +disable-model-invocation: true +--- + +Dispatch the extraction to `math-extractor` agents, one per lane, and merge what +they return into one tracked note. The extraction methodology — the lane +definitions, the evidence tiers, the locator rule, the rejection discipline — +lives in `.claude/agents/math-extractor.md`; the note's format lives in +`references/note-format.md`. Never duplicate either here. + +**This skill stops at the mathematics.** `references/note-format.md` states what +that bars from the note; step 6 fails the run if any of it reaches the note. + +For reviewing Lean that already exists, use `math-review` instead. + +## Process + +### 1. Pin the object + +Take the first non-empty option: + +1. An explicit argument — the mathematical object, with optional sources after + `--`. +2. If none, ask. There is no useful default, and extracting the wrong object + costs a full run. + +Derive `` in English kebab-case from the object's conventional name +(`split-inclusion`, `umegaki-relative-entropy`). Check whether +`docs/math/.md` already exists: + +- **new** — the note will be created. +- **re-extraction** — read the existing note now. Its `## Rejected + formulations and refuted claims` and `## Sources` tables are **append-only** + and must be carried forward verbatim; every other section will be replaced. + Carry the rejected rows into step 3 as a do-not-re-explore list. + +Read `sources.md` (next to this file) as well — you need the retrieval history +and the locator adjudications before you spend time on a source someone already +failed to obtain. + +Announce the resolved object, the slug, and new-or-re-extraction before doing +anything else, so a wrong reading costs one word to correct. + +*Done when:* the object is stated in one mathematical sentence, the slug is +fixed, the existing note (if any) is in hand, and the announcement is made. + +### 2. Scope and build the corpus + +**Serial. Do not fan out here.** Two reasons, and both are structural: you +cannot write good lane briefs before you know what the literature looks like, +and the PDF converter fails when several conversions run at once. + +Start wide. Read a survey or a recent citing paper first — not to extract from, +but to learn the shape of the field: who defines the object, how many +definitional variants are in circulation, which results matter. This is the +measurement that sizes the run in step 3. + +Then obtain the sources, most faithful first. `references/ingestion.md` owns the +ladder, the commands, and the failure modes; read it when a fetch fails. The +short form: + +1. arXiv LaTeX source — the original text, formulas exactly as written. +2. arXiv HTML. +3. Any other URL. +4. A PDF through the converter — **slow, serial, and not verbatim**. Opt-in per + call with `--allow-mineru`, and `--pages START-END` to convert the chapter + that matters rather than the whole book. The default backend is the CPU/GPU + pipeline; `--backend hybrid-engine --effort high` opts into the + higher-accuracy VLM path, which can exhaust this machine's VRAM — on + `CUDA out of memory`, drop both flags and rerun. Neither choice raises the + tier: converted text is (b) either way. +5. Not obtainable — record it and move on. + +Everything lands in `references//`, which is gitignored and may +be deleted at any time. + +Keep two lists as you go: the **corpus** (source key → cache path → how it was +obtained) and the **unfetchable list** (source key → what was tried). Both go to +every lane in step 3. The unfetchable list is what stops a lane from writing a +theorem number for a book nobody opened. + +**If four or more PDFs need converting, stop and give the user an estimate +before starting.** Conversion is minutes per paper and cannot be parallelised; +that is the user's time to spend, not yours. Sources reachable as arXiv LaTeX do +not count against this. + +*Done when:* the corpus and unfetchable lists are written down, each corpus +entry has a cache path and a provenance, and the scoping read has produced a +first count of definitional variants. + +### 3. Size the run and dispatch + +Count three numbers from step 2: + +- **S** — sources in the corpus. +- **D** — definitional variants sighted. +- **R** — results that will appear in the note. + +Read the table top to bottom and take the **first** row whose condition holds. +Largest first, because one number out of range is enough to make a run large: +a corpus of three that disagrees irreconcilably is not a medium run. + +| Size | Condition | Lanes | +|---|---|---| +| **large** | D ≥ 3, or S ≥ 4, or R ≥ 9, or the sources disagree irreconcilably | all five, one agent each, plus step 4 | +| **small** | D ≤ 1 and S ≤ 2 and R ≤ 3 | **Do not fan out** — see below | +| **medium** | neither of the above | three agents: **1+2**, **3+4**, **5** | + +At **small**, launch a single `math-extractor` with no lane assigned — its agent +file then has it cover all five lanes itself, in 5–15 tool calls. Continue with +steps 5 and 6, and skip step 4. + +The pairings are not arbitrary. Lanes 1 and 2 read the same source linearly, so +one agent reading once is cheaper than two. Lanes 3 and 4 both need falsifying +models, and splitting them has two agents construct the same object twice. Lane +5's tools are disjoint from everyone else's. + +**Announce the size and the lane set before dispatching**, so a wrong call costs +one word. + +Launch all agents in a single message (Agent tool, +`subagent_type: math-extractor`). Write each prompt as a research memo, not a +task ticket. Every prompt must state: + +- **the assigned lane by number and name, as a role** — "You are one of N + lane-specific extractors running in parallel over this object; work lane K + () only". Do not restate the lane's definition; the agent file owns it. +- **the object**, in the same one sentence you announced in step 1; +- **the corpus** — source key, cache path, and how it was obtained, for each + entry. Say plainly: *read these paths; do not run the converter; it fails when + run concurrently.* +- **the unfetchable list**, with the rule attached: no locator for anything on + it, and the substitution sentence instead; +- **the relevant rows of `sources.md`**, verbatim — retrieval attempts already + made and locator adjudications already recorded, as a do-not-repeat list; +- **the rejected rows carried forward** from a previous extraction, as a + do-not-re-explore list; +- **the absolute path of the notes file** it must append rows to as it goes: + `/math-extract//lane-.md`. Pass *your* scratchpad + path, not the agent's — a file written where you cannot read it is no use when + the agent dies; +- **your forecast** — what you expect this lane to find, and why. Require the + agent to **write its own prediction before opening the first source** and to + report the divergence afterwards. A hit saves a sweep; a miss is itself + information, and the written prediction is what exposes anchoring; +- **explicit permission to contradict this brief.** "Every branch of the + forecast was wrong" and "this lane is the wrong lens for this object" are + findings to report, not failures to apologise for. A lane that believes it + must confirm the brief will find a way to. + +If the platform refuses to launch all of them at once, start as many as it +allows and launch the next as each completes. + +**If an agent does not return**, read the notes file you assigned it rather than +dropping its lane. Rows that reached disk are usable; mark the lane partial and +record in `## Not investigated` how far it got. + +*Done when:* every lane has completed, or been declared dead with its notes file +recovered. + +### 4. Refute + +Large runs only. Collect two things: every row at tier **(c)** or **(d)**, and +every `rejected` row whose discriminator looks thin. If there are none, skip. + +Launch one further `math-extractor` in the refutation role. Its agent file +defines the outcomes; hand it the rows and the corpus, and require it to attack +the **adopted general form** as well — vacuity at a degenerate model, a standard +object from the literature the form fails to cover, and any hypothesis attached +to it that is actually provable. + +Dispositions: + +- **refuted** — the row leaves the note, except that a refuted *claim about the + literature* is recorded in `## Rejected formulations and refuted claims` with + the evidence that killed it; +- **promoted to (a)/(b)** — the row stays at its new tier; +- **survives at (c)/(d)** — the row stays, marked `unverified`, and may not + carry the adopted general form on its own; +- **discriminator does not discriminate** — the `rejected` row is demoted to + `preference-only`, which makes it weightless. + +One adversarial pass, not a vote. The tiers already carry the extractor's own +confidence; another vote buys another round of fetching and no new information. + +*Done when:* every collected row has exactly one disposition, and the adopted +general form has been attacked on all three fronts. + +### 5. Merge and write the note + +Write `docs/math/.md` to the format in `references/note-format.md`. + +If any quote you merge carries a macro its source defines — `\lok`, `\A`, `\bC` — +the note opens with the macro preamble that format specifies. Never edit a quote +to make it render. + +Three things are **yours**, not any lane's: + +1. **The adopted general form.** Lane 1 supplies the variants and lane 4 the + discriminators, but they run in parallel and neither sees the other. Write + the paragraph, and cite the discriminator that justifies the choice by its + `(X#)`. If no discriminator justifies it, say that the choice is provisional + — do not manufacture a reason. +2. **The `[ext]` gap.** Compute the set difference between the external results + lane 2 marked and the ones lane 5 actually checked, and put the remainder in + `## Not investigated`. Nobody else can compute it; if you skip it, the note + silently claims coverage it does not have. +3. **`worst-tier`** — the minimum tier over the rows the adopted form and the + main results rest on. Not the average, and not the best. +4. **The variant comparison grid.** Lane 1's (D#) rows are its only input, but + choosing the axes — the columns along which the variants genuinely differ — + needs all the rows at once, which no lane has. The grid is a derived index + and carries no tier; when it disagrees with a (D#) block, the grid is wrong. + +On a re-extraction, carry `## Rejected formulations and refuted claims` and +`## Sources` forward verbatim and append to them; replace every other section. +Add a `revisions:` entry. Carry `implemented-as` forward unchanged too — it is +`math-review`'s field, not yours, and a re-extraction that resets it to `none` +silently deletes the back-link to a formalization that still exists. + +Do not soften or drop what the lanes returned. Rows from different lanes that +touch the same object stay separate; only `## Out of lane` items get +de-duplicated. + +*Done when:* the note exists, every section is present, `## Not investigated` is +non-empty, and the append-only tables have lost no rows. + +### 6. Verify, then report + +Run all five checks against the note. **They are the acceptance criteria, not a +formality** — this is the one step that costs your own tool calls rather than an +agent's, and it is worth it, because a fabricated quote or an invented theorem +number is the worst thing this skill can produce, and a note that renders as a +wall of `ParseError` is the worst thing a reader can be handed. + +The commands below are the detection half; the judgement is yours. Each was +measured against a note written to violate all four, and each flagged the bad +rows without flagging the good ones — but a grep finds candidates, not verdicts, +so read every hit rather than counting them. + +1. **Firewall.** + + ````bash + grep -nE '```lean|^\s*(theorem|lemma|def|structure|class|instance|example)\b' docs/math/.md + ```` + + A Lean fence is a hard fail: remove it and the material around it. A prose + hit — a sentence opening "Definition of …" — is fine. + +2. **Quote check.** For each blockquote, `grep -F` its text against + **`source.flat.txt` of the source that row cites**, not against the cache at + large: matching some other file proves the sentence exists somewhere, which + is not the claim. Use the flattened file, since a quotation crossing a line + break in the original matches there and nowhere else. A quote that does not + match is downgraded to (b) or deleted. Report the counts. + +3. **Locator check.** + + ```bash + grep -nE 'tier \([cd]\)|\| *[cd] *\|' docs/math/.md | + grep -E 'Theorem|Thm|Lemma|Prop|Cor|§|p\. ?[0-9]|eq\. ?\(' + ``` + + Both spellings of the tier are needed: the prose rows write `tier (c)`, while + the `## Hypotheses`, `## Rejected` and `## Sources` rows carry a bare `c` or + `d` in a column. The second alternative costs some false positives on any + one-letter cell, which is the right trade here — a missed (c) row with a + theorem number is the failure this check exists to catch. + + Every hit is a violation: the row must carry the substitution sentence + instead of the number. Then read the `[ext: …]` markers and the + `## Sources` rows for anything on the unfetchable list. + +4. **Discipline check.** + + ```bash + awk -F'|' '/model-dependent/ { w=$6; gsub(/^[ \t]+|[ \t]+$/,"",w) + if (w=="" || w=="—" || w=="-") print NR": no witness: "$2 }' docs/math/.md + awk -F'|' '/\| rejected \|/ { if ($5 !~ /\(X[1-5]\)/) print NR": untyped: "$2 }' docs/math/.md + grep -niE 'harder to formalize|awkward in a proof assistant|not standard|more lemmas' docs/math/.md + ``` + + A `model-dependent` row with no witness becomes `open`. A `rejected` row with + no typed discriminator, or resting on a banned ground, becomes + `preference-only`. Then check by eye that every degeneracy checklist item has + a row — including the ones with no effect, and including the + `intended case is nonvacuous` row (a named instance or an explicit + "none found — ") — and that `## Not investigated` is + present **and** non-empty. Finally, for each `(R#)` carrying a + `Proof route:`, cross-check route against `Depends on:` both ways: an edge a + step consumes but the list omits is the exact omission the field exists to + catch; a listed edge no step consumes is an error in one of the two. + +5. **Render check.** + + ```bash + uv run .claude/skills/math-extract/scripts/check_render.py docs/math/.md + ``` + + Every math span must parse **on its own**, because that is how a markdown + previewer renders it. The script reports two classes of defect and exits + non-zero on either: + + - a command KaTeX does not define — almost always a source's private macro + (`\Tr`, `\A`, `\U`) that leaked out of a quote and into the note's own + prose, or an in-note macro definition, which never carries; + - with the pipeline installed, a **real render of the document** through + markdown-it and the plugin VS Code's preview uses, which catches what an + allowlist cannot: an unbraced argument (`\widetilde\mathcal U`), a + mis-paired delimiter, and *portability hazards* — spans that typeset here + but rely on behaviour engines disagree about, such as a subscripted thin + space (`\mathrm{Tr}\,_2`). + + Enable the real render once per checkout with + `(cd .claude/skills/math-extract && npm install --no-save markdown-it @vscode/markdown-it-katex)`; + `node_modules/` is gitignored. Without it the script still runs, says so, and + leaves brace and span-boundary errors unchecked — a partial check, not a pass. + + **Do not silence a finding with a macro preamble.** Measured through that + plugin, **no** definition form carries to the next math span — not + `\newcommand`, not `\gdef`, not `\global\def` — because each span is + rendered with fresh options. A note cannot define macros for itself. The two + fixes, in `references/note-format.md`, are: plain KaTeX in the note's own + voice, and code (a fenced block, or backticks for an inlined fragment) around + anything verbatim that carries source macros. + +Then update `sources.md`: one row per source attempted this run, and a `Notes` +entry for any locator you adjudicated. **This is the only step that writes to +that file.** + +Report to the user in Japanese, in this shape: + +```markdown +# Math Extract — + +**Note**: `docs/math/.md` — +**Size**: S= sources · D= variants · R= results → , lanes +**Worst tier among load-bearing rows**: () + +## Adopted general form — 判断を仰ぐ点 + + +## Checks +- Firewall: +- Quote check: / matched; downgraded; deleted +- Locator check: tier-(c)/(d) rows, all carrying a statement instead of a number +- Discipline: model-dependent rows, all with witnesses; rejected rows, all with typed discriminators +- Render: math spans parsed independently, ParseErrors <| real KaTeX parse not available — brace errors unchecked> + +## Corpus +| Key | How obtained | Cost | +|---|---|---| + + +## Discriminators found + + +## Per-lane summary +| Lane | rows | forecast | verdict | +|---|---|---|---| + + +## What changed + + +## Refuted + + +## Not investigated + +``` + +Omit an empty optional section; never omit `## Checks`, `## Per-lane summary`, +or `## Not investigated`. + +## Why lanes, not sources + +One agent per source would reconcile nothing. The note's whole value is in the +comparisons — this source defines it that way and that one differently, this +hypothesis is standing there and local here, this result is proved in one place +and asserted in another — and a comparison needs one reader holding both texts +at once. So the lanes cut across the corpus, not through it. + +The cut is by tool budget as much as by topic, which is what keeps the lanes +from duplicating work. Lanes 1 and 2 read the corpus and nothing else. Lane 3 +reads it and argues. Lane 4 constructs objects. Lane 5 is the only one that +searches Mathlib and the web. Two lanes that would contend for the same scarce +tool are paired into one agent at medium size for exactly that reason. + +Conversion is deliberately outside all of this: it is serial, minutes long, and +breaks under concurrency, so it happens once in step 2 and the lanes only read +what it produced. diff --git a/.claude/skills/math-extract/references/ingestion.md b/.claude/skills/math-extract/references/ingestion.md new file mode 100644 index 0000000..80f1900 --- /dev/null +++ b/.claude/skills/math-extract/references/ingestion.md @@ -0,0 +1,160 @@ +# Building the corpus + +How `math-extract` obtains source texts, what each rung costs, and what to do +when one fails. `SKILL.md` step 2 points here; read it when a fetch fails or +when a PDF has to be converted. + +Everything lands under `references//`, which is gitignored. **The cache is +navigation; the note under `docs/math/` is the product.** Nothing here is a +deliverable and the whole directory may be deleted at any time. + +## The ladder + +```bash +uv run .claude/skills/math-extract/scripts/ingest.py "" +``` + +The script walks the rungs in order and stops at the first that yields text. It +prints a JSON summary and exits 0 on success. + +| Rung | Source | Faithfulness | Cost | +|---|---|---|---| +| 1 | `arxiv.org/e-print/` — LaTeX | **the original text** | seconds | +| 2 | `arxiv.org/html/` — LaTeXML | high; formulas re-rendered | seconds | +| 3 | any other URL, or a local `.html`/`.txt`/`.tex` | tags stripped | seconds | +| 4 | a PDF through MinerU, with `--allow-mineru` | **model inference, not text** | minutes, serial | +| 5 | not obtainable | — | — | + +**Rung 1 is the reason to check arXiv even for a published paper.** The LaTeX +source is what the author wrote, so a quotation taken from it is verbatim by +construction and reaches tier (a) with no further work. Every other rung +produces a derived text. + +An arXiv *pdf* URL is not a PDF here: the identifier is recovered from it and +rung 1 runs instead. + +## Output + +``` +references// +├── raw/ the bytes as fetched, unmodified +├── source.txt concatenated text, one line per source line +└── source.flat.txt the same, each paragraph flattened onto one line +``` + +**`source.flat.txt` is what the quote check greps.** A sentence in a LaTeX +source is wrapped across several lines, so a verbatim quotation of it matches +nothing in `source.txt`; flattening paragraphs onto single lines is what makes +`grep -F` usable. Measured on `math-ph/0411058`: a real sentence matches in +`source.flat.txt`, fails in `source.txt`, and a sentence with a plausible clause +appended fails in both. + +The JSON summary carries `verbatim: true` exactly when the text came from rung 1. +That flag is the input to the tier rule: quotes from a `verbatim` cache are +(a); quotes from any other rung are (b) until checked against the original. + +## Exit codes + +| Code | Meaning | What to do | +|---|---|---| +| 0 | text cached | proceed | +| 2 | target uninterpretable | fix the argument | +| 3 | every rung failed, or the conversion failed or timed out | record `not retrieved` in `sources.md` with what was tried; every claim resting on the source is capped at tier (d), no locators | +| 4 | fetched but almost no text | treat as not retrieved unless the cache shows otherwise | +| 5 | the source is a PDF and `--allow-mineru` was not given, or was given and `mineru` is not installed | rung 4, below | + +A non-zero exit **does not stop the extraction**. It lowers what can be claimed: +the source joins the unfetchable list, and every lane is told that no locator may +be written for it. + +## Rung 4 — converting a PDF + +**Opt-in per call.** A PDF without the flag exits 5 and states what the +conversion would cost; the script never installs the converter for you. + +```bash +uv run .claude/skills/math-extract/scripts/ingest.py --allow-mineru +uv run .claude/skills/math-extract/scripts/ingest.py --allow-mineru --pages 40-62 +``` + +The converter installs with a plain `uv sync` — it is the `mineru` dependency +group in `pyproject.toml`, which is in `default-groups`. To skip it deliberately, +`uv sync --no-group mineru` drops ~100 packages including torch. + +The script takes the largest Markdown file MinerU produces and writes +`source.txt` and `source.flat.txt` from it exactly as for the other rungs — so +the quote check works the same way. The JSON summary comes back with +`verbatim: false`, the `backend` used, and a `caveat` field spelling out the +tier consequence. + +### GPU + +The devcontainer passes the host GPU through. **The setting takes effect on a +container rebuild**; the post-rebuild verification procedure lives in +`.devcontainer/gpu-verification.md` and is deliberately self-contained. + +- **The default backend stays `pipeline` even on GPU.** torch picks up CUDA by + itself, so pipeline gets the speedup with no configuration and cannot OOM the + way the VLM backends can. +- `--backend hybrid-engine --effort high` opts into the higher-accuracy VLM + path. **This machine's 8GB of VRAM is that backend's minimum, shared with the + Windows desktop** — on `CUDA out of memory`, drop the flag and rerun; the + pipeline result is the fallback, not a failure. +- Models survive rebuilds in the `hf-models` named volume, and + `mineru-models-download` fetches them ahead of time. The devcontainer pins the + origin (see its comment on `MINERU_MODEL_SOURCE`); to fetch from elsewhere + once, prefix the single command — `MINERU_MODEL_SOURCE=modelscope mineru …` — + rather than unpinning. + +Things that are easy to get wrong, and cost a lot when got wrong: + +- **The script always passes `-b` explicitly.** MinerU 3.x defaults to + `hybrid-engine`, which is the wrong default at 8GB of shared VRAM. +- **Install `mineru[pipeline,vlm]`, not `mineru[all]`.** The `all` extra pulls + in vllm, lmdeploy and mlx — serving stacks that are useless here and large. + `[core]` would also work but drags in gradio. +- **`--pages START-END` takes a page range.** Converting the twenty pages that + matter instead of a four-hundred-page book is the difference between minutes + and an afternoon. Use it. +- `-f/--formula` and `-t/--table` are already on by default. Leave `-l` unset: + the language is detected, and the option's value list does not include a plain + English code. +- **Conversion is serial.** Concurrent runs hit the per-task timeout and fail. + This is why step 2 of the skill builds the corpus before dispatching lanes, + and why the lane briefs say *do not run the converter*. +- Roughly seven minutes for eighteen pages on CPU, far less on GPU; a long + paper on CPU is half an hour. **Four or more PDFs means telling the user the + estimate first.** +- The container already has `libgl1` and `libglib2.0-0`, which MinerU's OpenCV + dependency needs. + +**A converted PDF is model output, not text.** MinerU's formula recognition can +produce plausible, wrong LaTeX, and presenting that as a verbatim quotation is +the worst failure this skill has. A quote from converted Markdown is tier (b) +with `mineru-unchecked` attached, and reaches (a) only after being compared +against the page image. Measured example: on a five-page paper set in Knuth's +small-caps font, the CPU pipeline rendered the byline as +`Don Knuth` — ordinary body text and formulas came out +clean, but unusual typography gets mangled silently. + +## What is deliberately not used + +- **`pymupdf`, `poppler`, `marker-pdf`** — license-incompatible with this + project. This also means the `Read` tool cannot open PDFs here: it shells out + to `pdftoppm`, which is part of poppler and is not installed. +- **Docling** — its formula model makes no useful progress on CPU, and without + it every equation comes out as a placeholder. For a mathematics corpus that is + the whole content. +- **MinerU's Python API** — the CLI is the stable interface; importing the + library pulls in a far heavier dependency graph. + +Recording this here is the point: the next person to look for a PDF converter +should find out in one place what was already tried and why it was rejected. + +## Rate limits and manners + +arXiv asks for roughly one request every three seconds. The script is serial and +the skill fetches the whole corpus in one pass before dispatching, so this takes +care of itself — but a lane that decides to fetch on its own can violate it. The +lane briefs say to read the cache instead, for this reason as well as the +converter's. diff --git a/.claude/skills/math-extract/references/katex-commands.txt b/.claude/skills/math-extract/references/katex-commands.txt new file mode 100644 index 0000000..4da5d2e --- /dev/null +++ b/.claude/skills/math-extract/references/katex-commands.txt @@ -0,0 +1,968 @@ +\! +\# +\$ +\% +\& +\, +\. +\: +\; +\= +\> +\AA +\Alpha +\And +\Bbb +\Bbbk +\Beta +\Big +\Bigg +\Biggl +\Biggm +\Biggr +\Bigl +\Bigm +\Bigr +\Box +\Bra +\Braket +\Bumpeq +\Cap +\Chi +\Colonapprox +\Coloneq +\Coloneqq +\Colonsim +\Complex +\Cup +\DOTSB +\DOTSI +\DOTSX +\Dagger +\Darr +\Delta +\Diamond +\Doteq +\Downarrow +\Epsilon +\Eqcolon +\Eqqcolon +\Eta +\Finv +\Game +\Gamma +\H +\Harr +\Huge +\Im +\Iota +\Join +\KaTeX +\Kappa +\Ket +\LARGE +\LaTeX +\Lambda +\Large +\Larr +\Leftarrow +\Leftrightarrow +\Lleftarrow +\Longleftarrow +\Longleftrightarrow +\Longrightarrow +\Lrarr +\Lsh +\Mu +\N +\Nu +\Omega +\Omicron +\Overrightarrow +\P +\Phi +\Pi +\Pr +\Psi +\R +\Rarr +\Re +\Reals +\Rho +\Rightarrow +\Rrightarrow +\Rsh +\S +\Set +\Sigma +\Subset +\Supset +\Tau +\TeX +\TextOrMath +\Theta +\Uarr +\Uparrow +\Updownarrow +\Upsilon +\Vdash +\Vert +\Vvdash +\Xi +\Z +\Zeta +\^ +\_ +\aa +\acute +\alef +\alefsym +\aleph +\allowbreak +\alpha +\amalg +\angl +\angle +\angln +\approx +\approxcolon +\approxcoloncolon +\approxeq +\arccos +\arcctg +\arcsin +\arctan +\arctg +\arg +\argmax +\argmin +\ast +\asymp +\atop +\backepsilon +\backprime +\backsim +\backsimeq +\backslash +\bar +\barwedge +\bcancel +\because +\begin +\beta +\beth +\between +\bf +\big +\bigcap +\bigcirc +\bigcup +\bigg +\biggl +\biggm +\biggr +\bigl +\bigm +\bigodot +\bigoplus +\bigotimes +\bigr +\bigsqcup +\bigstar +\bigtriangledown +\bigtriangleup +\biguplus +\bigvee +\bigwedge +\binom +\blacklozenge +\blacksquare +\blacktriangle +\blacktriangledown +\blacktriangleleft +\blacktriangleright +\blue +\blueA +\blueB +\blueC +\blueD +\blueE +\bm +\bmod +\bold +\boldsymbol +\bot +\bowtie +\boxdot +\boxed +\boxminus +\boxplus +\boxtimes +\bra +\brace +\brack +\braket +\breve +\bull +\bullet +\bumpeq +\c +\cal +\cancel +\cap +\cdot +\cdotp +\cdots +\centerdot +\cfrac +\ch +\check +\checkmark +\chi +\choose +\circ +\circeq +\circlearrowleft +\circlearrowright +\circledR +\circledS +\circledast +\circledcirc +\circleddash +\clap +\clubs +\clubsuit +\cnums +\colon +\colonapprox +\coloncolon +\coloncolonapprox +\coloncolonequals +\coloncolonminus +\coloncolonsim +\coloneq +\coloneqq +\colonequals +\colonminus +\colonsim +\color +\colorbox +\complement +\cong +\coprod +\copyright +\cos +\cosec +\cosh +\cot +\cotg +\coth +\csc +\ctg +\cth +\cup +\curlyeqprec +\curlyeqsucc +\curlyvee +\curlywedge +\curvearrowleft +\curvearrowright +\dArr +\dag +\dagger +\daleth +\darr +\dashleftarrow +\dashrightarrow +\dashv +\dbinom +\dblcolon +\ddag +\ddagger +\ddddot +\dddot +\ddot +\ddots +\def +\deg +\degree +\delta +\det +\dfrac +\diagdown +\diagup +\diamond +\diamonds +\diamondsuit +\digamma +\dim +\displaystyle +\div +\divideontimes +\dot +\doteq +\doteqdot +\dotplus +\dots +\dotsb +\dotsc +\dotsi +\dotsm +\dotso +\dotsx +\doublebarwedge +\doublecap +\doublecup +\downarrow +\downdownarrows +\downharpoonleft +\downharpoonright +\edef +\ell +\emph +\empty +\emptyset +\end +\enskip +\enspace +\epsilon +\eqcirc +\eqcolon +\eqqcolon +\eqsim +\eqslantgtr +\eqslantless +\equalscolon +\equalscoloncolon +\equiv +\errmessage +\eta +\eth +\exist +\exists +\exp +\expandafter +\fallingdotseq +\fbox +\flat +\footnotesize +\forall +\frac +\frak +\frown +\futurelet +\gamma +\gcd +\gdef +\ge +\geq +\geqq +\geqslant +\gets +\gg +\ggg +\gggtr +\gimel +\global +\gnapprox +\gneq +\gneqq +\gnsim +\goldA +\goldB +\goldC +\goldD +\goldE +\grave +\gray +\grayA +\grayB +\grayC +\grayD +\grayE +\grayF +\grayG +\grayH +\grayI +\green +\greenA +\greenB +\greenC +\greenD +\greenE +\gt +\gtrapprox +\gtrdot +\gtreqless +\gtreqqless +\gtrless +\gtrsim +\gvertneqq +\hArr +\harr +\hat +\hbar +\hbox +\hearts +\heartsuit +\hom +\hookleftarrow +\hookrightarrow +\hphantom +\href +\hslash +\htmlClass +\htmlId +\htmlStyle +\huge +\iff +\iiint +\iint +\image +\imageof +\imath +\impliedby +\implies +\in +\includegraphics +\inf +\infin +\infty +\injlim +\int +\intercal +\intop +\iota +\isin +\it +\jmath +\kaBlue +\kaGreen +\kappa +\ker +\ket +\lArr +\lBrace +\lVert +\lambda +\land +\lang +\langle +\large +\larr +\lbrace +\lbrack +\lceil +\ldotp +\ldots +\le +\leadsto +\left +\leftarrow +\leftarrowtail +\leftharpoondown +\leftharpoonup +\leftleftarrows +\leftrightarrow +\leftrightarrows +\leftrightharpoons +\leftrightsquigarrow +\leftthreetimes +\leq +\leqq +\leqslant +\lessapprox +\lessdot +\lesseqgtr +\lesseqqgtr +\lessgtr +\lesssim +\let +\lfloor +\lg +\lgroup +\lhd +\lim +\liminf +\limsup +\ll +\llap +\llbracket +\llcorner +\lll +\llless +\lmoustache +\ln +\lnapprox +\lneq +\lneqq +\lnot +\lnsim +\log +\longleftarrow +\longleftrightarrow +\longmapsto +\longrightarrow +\looparrowleft +\looparrowright +\lor +\lozenge +\lparen +\lq +\lrArr +\lrarr +\lrcorner +\lt +\ltimes +\lvert +\lvertneqq +\maltese +\mapsto +\maroonA +\maroonB +\maroonC +\maroonD +\maroonE +\mathbb +\mathbf +\mathbin +\mathcal +\mathclap +\mathclose +\mathellipsis +\mathfrak +\mathinner +\mathit +\mathllap +\mathnormal +\mathop +\mathopen +\mathord +\mathpunct +\mathrel +\mathring +\mathrlap +\mathrm +\mathscr +\mathsf +\mathsfit +\mathsterling +\mathstrut +\mathtt +\max +\measuredangle +\medspace +\message +\mho +\mid +\middle +\min +\mintA +\mintB +\mintC +\minuscolon +\minuscoloncolon +\minuso +\mod +\models +\mp +\mu +\multimap +\nLeftarrow +\nLeftrightarrow +\nRightarrow +\nVDash +\nVdash +\nabla +\natnums +\natural +\ncong +\ne +\nearrow +\neg +\negmedspace +\negthickspace +\negthinspace +\neq +\newcommand +\newline +\nexists +\ngeq +\ngeqq +\ngeqslant +\ngtr +\ni +\nleftarrow +\nleftrightarrow +\nleq +\nleqq +\nleqslant +\nless +\nmid +\nobreak +\nobreakspace +\noexpand +\nonumber +\normalsize +\not +\notag +\notin +\notni +\nparallel +\nprec +\npreceq +\nrightarrow +\nshortmid +\nshortparallel +\nsim +\nsubseteq +\nsubseteqq +\nsucc +\nsucceq +\nsupseteq +\nsupseteqq +\ntriangleleft +\ntrianglelefteq +\ntriangleright +\ntrianglerighteq +\nu +\nvDash +\nvdash +\nwarrow +\odot +\oiiint +\oiint +\oint +\omega +\omicron +\ominus +\operatorname +\operatornamewithlimits +\oplus +\orange +\ordinarycolon +\origof +\oslash +\otimes +\over +\overbrace +\overbracket +\overgroup +\overleftarrow +\overleftharpoon +\overleftrightarrow +\overline +\overlinesegment +\overrightarrow +\overrightharpoon +\overset +\owns +\parallel +\partial +\perp +\phantom +\phase +\phi +\pi +\pink +\pitchfork +\plim +\plusmn +\pm +\pmb +\pmod +\pod +\pounds +\prec +\precapprox +\preccurlyeq +\preceq +\precnapprox +\precneqq +\precnsim +\precsim +\prime +\prod +\projlim +\propto +\providecommand +\psi +\purple +\purpleA +\purpleB +\purpleC +\purpleD +\purpleE +\qquad +\quad +\r +\rArr +\rBrace +\rVert +\rang +\rangle +\rarr +\ratio +\rbrace +\rbrack +\rceil +\real +\reals +\red +\redA +\redB +\redC +\redD +\redE +\relax +\renewcommand +\restriction +\rfloor +\rgroup +\rhd +\rho +\right +\rightarrow +\rightarrowtail +\rightharpoondown +\rightharpoonup +\rightleftarrows +\rightleftharpoons +\rightrightarrows +\rightsquigarrow +\rightthreetimes +\risingdotseq +\rlap +\rm +\rmoustache +\rparen +\rq +\rrbracket +\rtimes +\rvert +\scriptscriptstyle +\scriptsize +\scriptstyle +\sdot +\searrow +\sec +\sect +\set +\setminus +\sf +\sh +\sharp +\shortmid +\shortparallel +\show +\sigma +\sim +\simcolon +\simcoloncolon +\simeq +\sin +\sinh +\sixptsize +\small +\smallfrown +\smallint +\smallsetminus +\smallsmile +\smash +\smile +\sout +\space +\spades +\spadesuit +\sphericalangle +\sqcap +\sqcup +\sqrt +\sqsubset +\sqsubseteq +\sqsupset +\sqsupseteq +\square +\stackrel +\star +\sub +\sube +\subset +\subseteq +\subseteqq +\subsetneq +\subsetneqq +\substack +\succ +\succapprox +\succcurlyeq +\succeq +\succnapprox +\succneqq +\succnsim +\succsim +\sum +\sup +\supe +\supset +\supseteq +\supseteqq +\supsetneq +\supsetneqq +\surd +\swarrow +\tan +\tanh +\tau +\tbinom +\tealA +\tealB +\tealC +\tealD +\tealE +\text +\textbf +\textcircled +\textcolor +\textcopyright +\textit +\textmd +\textnormal +\textregistered +\textrm +\textsf +\textstyle +\texttt +\textup +\tfrac +\tg +\th +\therefore +\theta +\thetasym +\thickapprox +\thicksim +\thickspace +\thinspace +\tilde +\times +\tiny +\to +\top +\triangle +\triangledown +\triangleleft +\trianglelefteq +\triangleq +\triangleright +\trianglerighteq +\tt +\twoheadleftarrow +\twoheadrightarrow +\u +\uArr +\uarr +\ulcorner +\underbar +\underbrace +\underbracket +\undergroup +\underleftarrow +\underleftrightarrow +\underline +\underlinesegment +\underrightarrow +\underset +\unlhd +\unrhd +\uparrow +\updownarrow +\upharpoonleft +\upharpoonright +\uplus +\upsilon +\upuparrows +\urcorner +\url +\utilde +\v +\vDash +\varDelta +\varGamma +\varLambda +\varOmega +\varPhi +\varPi +\varPsi +\varSigma +\varTheta +\varUpsilon +\varXi +\varepsilon +\varinjlim +\varkappa +\varliminf +\varlimsup +\varnothing +\varphi +\varpi +\varprojlim +\varpropto +\varrho +\varsigma +\varsubsetneq +\varsubsetneqq +\varsupsetneq +\varsupsetneqq +\vartheta +\vartriangle +\vartriangleleft +\vartriangleright +\varvdots +\vcentcolon +\vcenter +\vdash +\vdots +\vec +\vee +\veebar +\vert +\vphantom +\wedge +\weierp +\widecheck +\widehat +\widetilde +\wp +\wr +\xLeftarrow +\xLeftrightarrow +\xRightarrow +\xcancel +\xdef +\xhookleftarrow +\xhookrightarrow +\xi +\xleftarrow +\xleftequilibrium +\xleftharpoondown +\xleftharpoonup +\xleftrightarrow +\xleftrightharpoons +\xlongequal +\xmapsto +\xrightarrow +\xrightequilibrium +\xrightharpoondown +\xrightharpoonup +\xrightleftarrows +\xrightleftharpoons +\xtofrom +\xtwoheadleftarrow +\xtwoheadrightarrow +\yen +\zeta +\{ +\| +\} +\~ diff --git a/.claude/skills/math-extract/references/note-format.md b/.claude/skills/math-extract/references/note-format.md new file mode 100644 index 0000000..196fb85 --- /dev/null +++ b/.claude/skills/math-extract/references/note-format.md @@ -0,0 +1,557 @@ +# Extraction note format + +The authoritative specification of `docs/math/.md` — the tracked artefact +the `math-extract` skill produces. `SKILL.md` points here rather than restating +it, so this file is the one place to edit when the format changes. + +Each rule carries the reason it exists where the reason is not self-evident. The +reasons are load-bearing: they are what lets you judge a case the rule does not +literally cover. + +**The note records mathematics.** No Lean types, no `def`/`structure`/`class` +sketches, no candidate declaration names, no docstring drafts, no ` ```lean ` +fences anywhere in the file. A statement that Mathlib contains a declaration is +a fact and is allowed; a suggestion about how this project should define +something is not. + +**One note per object, not per source.** The cache under `references//` is +indexed by source; the note is indexed by object; the `## Sources` table is where +the two indices cross. Reconciling several sources into one account is the note's +reason to exist. + +The note is written in **English**, including prose. Verbatim quotes stay in the +source's own language. + +## Skeleton + +````markdown +--- +object: Split inclusion of von Neumann algebras +slug: split-inclusion +status: draft +worst-tier: c +mathlib-rev: +implemented-as: none +revisions: + - 2026-08-14 · · initial extraction · sources: DL84, BU74 +--- + + + +# + +## What this object is for + +<1–3 sentences: what the object does in the theory, and why its generality + matters. No history essay, no motivation essay, nothing about formalization.> + +## Definition + +### Variants as the sources write them + +| (D#) | Source | | | | Tier | +|---|---|---|---|---|---| +| (D1) | [DL84] | pair of algebras | second algebra arbitrary | no vector | a | +| (D2) | [BU74] | net-level | second algebra a commutant | vector required | b | + +**(D1) [DL84] §1** — tier (a) + +> source's own notation. If it carries the source's own macros, give each one +> a local \gdef inside the same $...$ it is used in — see "Source macros" +> below — so it renders as the source intended; fence it only if a macro +> cannot be \gdef'd at all.> + +[tr.] + +**(D2) [BU74]** — tier (b) + + + +`differs from (D1) by:` +`sources claim equivalence:` yes / no / not addressed — + +### Adopted general form + + + +## Notation and conventions + +| (C#) | Axis | This note | Per source | Translation | +|---|---|---|---|---| +| (C1) | trace normalisation | unnormalised | [DL84] unnormalised; [BU74] implicit, inferred from §2 eq. (4) | — | + +## Results and dependencies + +### (R1) + + + +- Source: [DL84] Thm 3.2 · tier (a) · **proved in source** +- Depends on: (R2), (A1), [ext: …] +- Conventions: (C1) +- Proof route: +- Verbatim: + > + +### (R2) … + +## Hypotheses + +| (A#) | Statement | Class | Evidence | Witness | Scope | Tier | Used by | +|---|---|---|---|---|---|---|---| +| (A1) | the net is nuclear | model-dependent | — | | local | b | (R1) | +| (A2) | H is separable | provable | [BU74] §1 | — | standing | b | (R1), (R3) | +| (A3) | … | open | could not separate | — | local | c | (R4) | + +## Degeneracies and boundary cases + +| Case | Effect on the adopted form | Tier | +|---|---|---| +| intended case is nonvacuous | "> | — | +| zero object / scalars | … | — | +| finite-dimensional | … | — | +| commutative | … | — | +| non-separable / non-σ-finite | … | — | +| type III | … | — | +| non-unital / degenerate representation | … | — | +| universally orthogonal index element | … | — | +| quantifier swap: ∀…∃… ↦ ∃…∀… | … | — | +| hypothesis dropped: (A1) | … | — | + +## Rejected formulations and refuted claims + +**Append-only.** + +| id | Candidate or claim | Disposition | Discriminator | Tier | Date | +|---|---|---|---|---|---| +| (X1) | | rejected | **(X3) generality loss** — | a | 2026-08-14 | +| (X2) | | equivalent | — ([BU74] Prop 1.1 proves it under (A2)) | b | 2026-08-14 | +| (X3) | | open — could not separate; tried … | — | c | 2026-08-14 | +| (X4) | | preference-only — no discriminator found | — | d | 2026-08-14 | +| (X5) | *claim*: | refuted | | a | 2026-08-14 | + +## Prior art + +| System | Found | Relation to variants | How searched | Measured at | +|---|---|---|---|---| +| Mathlib | `Mathlib.…` | same as (D2) | lean_local_search "…", lean_loogle "…" | mathlib rev `` | +| Mathlib | could not find | — | | mathlib rev `` | +| Isabelle AFP | — | — | AFP index search "…" | 2026-08-14 | + +## Open questions + + + +## Sources + +**Append-only.** + +| Key | Work | Status | Cache | Version | Tier reached | Retrieved | +|---|---|---|---|---|---|---| +| DL84 | Doplicher, Longo, *Standard and split inclusions of von Neumann algebras*, Invent. Math. 75 (1984) | retrieved | `references/dl-1984-split/` | published | a | 2026-08-14 | +| TAK-I | Takesaki, *Theory of Operator Algebras I* | not retrieved — tried arXiv, DOI, publisher, zbMATH | — | — | d | 2026-08-14 | + +## Not investigated + + +```` + +## Section rules + +### Source macros, and why there is no macro preamble + +Verbatim quotes carry the source's own LaTeX, and sources define their own +macros — `\lok`, `\A`, `\bC`, `\Tr`. Any renderer that typesets `$…$` (KaTeX in +VS Code's Markdown preview, MathJax elsewhere) raises a parse error on every one +of them, and **the fix is never to edit the quote**: those bytes are the evidence +the quote check verifies. + +**The fix is also not a macro preamble — in any definition form.** An earlier +version of this file prescribed one. It does not work, and the failure is silent +until a reader opens the note: + +> Measured through `@vscode/markdown-it-katex`, the plugin VS Code's own Markdown +> preview uses: a `$$` block defining a macro with `\newcommand`, with `\gdef`, +> or with `\global\def` leaves that macro **undefined in every later math +> span**. The plugin renders each span with fresh options, so nothing is carried. +> Bare KaTeX is only slightly better — there `\gdef` survives when the caller +> passes a shared `macros` object, and `\newcommand`, being a local definition, +> never does — but no markdown previewer passes one. +> +> The practical consequence: **a note cannot define macros for itself.** Every +> occurrence throws `Undefined control sequence`. + +**In the note's own voice, always write plain KaTeX** — `\mathcal{A}`, +`\mathrm{Tr}`, `\operatorname{supp}`, `\varphi`. A source's private macro has no +business in a sentence the note itself is asserting; the note is not that +source, and every such import is a rendering failure waiting for a reader. + +**For a verbatim quote, define the macro where it is used.** A `\gdef` does not +survive to the *next* span, but it works perfectly *inside the span that +defines it* — nothing has to survive anywhere. Prepend the definition, copied +verbatim from the source's own preamble, immediately inside the same `$…$` (or +`$$…$$`) the quote already uses: + +``` +$\gdef\lok#1{{\mathcal #1}}\lok{B}$ +``` + +This renders exactly as the source intended — `\lok{B}` still reads as +`\mathcal{B}` — with no cross-span persistence required, so it works in every +renderer this file has had to distrust: VS Code's preview, bare KaTeX, GitHub's. +A span using several macros gets one `\gdef` per macro it actually uses, deduped +in first-use order; a span using none is untouched. Where one macro name means +different things in different sources — HS17's `\A` is `\mathfrak{A}`, WIT18's +is `\mathcal{A}` — inject the definition that belongs to *that quote's own +source*, never the other one, even though the raw name collides. + +This changes what "verbatim" certifies: the displayed span is no longer +byte-identical to the source at that exact point — it carries an audited +`\gdef` prefix ahead of the unchanged quote. That prefix is presentation, not +content: **strip it before running the quote check**, and audit the stripped +definition against the catalogue in the note's own opening comment (see below), +which in turn must be copied from the source's own preamble, not paraphrased — +exactly the discipline the old document-level preamble comment required, now +scoped per macro instead of per document. When a source's macros are +*unrecoverable* — a private `\documentclass` or `\usepackage` absent from the +arXiv package, as with KW20's `Book_KW` — inject a **reconstructed** definition +instead (ordinary mathematical usage, e.g. `\gdef\supp{\operatorname{supp}}`), +label it `reconstructed` in the catalogue, and record the source's +unrecoverability in `sources.md`. The same applies to a genuine LaTeX primitive +KaTeX simply does not implement (`\mbox`, reconstructed as `\mathrm`) — that is +a gap in the renderer, not a macro belonging to any source, but the fix is the +same local `\gdef`. + +**Fall back to code — a fenced block inside the blockquote, or backticks for a +fragment — only when a macro cannot be `\gdef`'d at all**: a `\newenvironment`, +a catcode change, or anything a single substitution macro cannot express. The +bytes stay exactly as fetched, the quote check greps them unchanged, and no +renderer tries to typeset them — but the quote then shows as raw source LaTeX +rather than the intended notation, so this is the fallback, not the first +move. + +Beyond macro names, the other way a note breaks a renderer is an argument that +looks braced and is not: `\widetilde\mathcal U` and `\Delta_\mathcal U` are both +parse errors, because `\widetilde` and `_` take a single token and `\mathcal` +then has no argument. **Brace it**: `\widetilde{\mathcal U}`, +`\Delta_{\mathcal U}`. No allowlist can catch this — only a parser can, which is +why the render check runs one. + +#### Regenerating the KaTeX allowlist + +`references/katex-commands.txt` is what `scripts/check_render.py` calls a known +command. It was produced by harvesting every `\name` literal from a KaTeX build +and then **rendering each one to see whether it actually works**, keeping only +what did — so it contains no guesses, and its misses are conservative rather +than permissive. Commands that cannot be probed in isolation (`\begin`, +`\gdef`, `\left`, the `\bigl` family) were verified separately in a syntactic +context that exercises them. + +To regenerate against a newer KaTeX, from the skill directory: + +```bash +npm install --no-save katex +node -e ' + const fs=require("fs"), katex=require("katex"); + const src=fs.readFileSync(require.resolve("katex/dist/katex.mjs"),"utf8"); + const cand=new Set(); + for (const m of src.matchAll(/["\x27`]\\\\([A-Za-z]+|[^A-Za-z\s"\x27`\\\\])["\x27`]/g)) cand.add("\\"+m[1]); + const ok=[]; + for (const c of [...cand].sort()) + for (const f of [c, c+"{x}", c+"{x}{y}", c+" x"]) + { try { katex.renderToString(f,{throwOnError:true,strict:false}); ok.push(c); break; } catch(e) {} } + fs.writeFileSync("references/katex-commands.txt", ok.join("\n")+"\n"); + console.log(ok.length); +' +``` + +then re-add by hand the contextual commands the standalone probe rejects, each +verified with a form that exercises it, and re-run the render check on every +note under `docs/math/` before committing the new list. + +None of this is mathematics: it is presentation, carries no claim, and needs no +tier and no locator. The check that enforces it is step 6.5 of `SKILL.md`. +### Frontmatter + +- `worst-tier` is the **minimum tier over load-bearing rows** — the rows the + adopted general form and the main results actually rest on. It is the first + thing a reader sees, and it is an honesty indicator: a note whose conclusions + ride on recollection says so at the top. +- `implemented-as` is a **fact-only back-link**, `none` until the object is + implemented and a fully-qualified declaration name afterwards. This skill + writes `none`, because it runs *before* the Lean exists and has nothing to + point at. `math-review` fills the name in — and resets it to `none` when the + declaration is gone — as part of checking that the declaration still matches + the note; those two fields (here and in the `docs/math/README.md` index row) + are the only thing it may write in this file. A re-extraction carries the + field forward unchanged rather than resetting it. + **Why:** a back-link nobody is obliged to maintain decays into a claim that + the object was formalized as something it no longer is, which is worse than + the honest `none` it started as. +- `mathlib-rev` is the Mathlib revision from `lake-manifest.json` at extraction + time. It is what expires the `## Prior art` rows. +- `revisions` carries the history. **The body never does** — a note is the + current best account, not a changelog. + +### What this object is for + +Written by lane 1. Enough for a reader to decide whether this is the object they +mean, and why the generality is the one under discussion. Not a survey. + +### Definition + +- Every variant carries a source, a tier, and a locator where the tier allows + one. **A variant with no source is not a variant** — it belongs under + `## Rejected formulations` as a candidate. +- **The comparison grid is a derived index, not a claim.** The orchestrator + writes it at merge from the (D#) blocks below it, choosing as columns the + axes along which the corpus actually splits (quantification level, what the + second object is, extra data such as a distinguished vector — whatever the + variants genuinely differ on). It is presentation, so it carries no tier and + no locator, and when grid and (D#) block disagree, the grid is wrong. The + verbatim blocks and `differs from:` lines are unchanged by its presence. + **Why:** filling one column per axis is what makes a variant axis visible at + merge time — the adopted general form's generality decisions are made along + exactly these axes, and an axis nobody wrote down is an axis silently + dropped. +- Never merge two sources' formulations into one row because they look alike, + and never renumber a source's own labels. +- `differs from:` states the difference in one sentence. "Slightly different" is + not a difference. +- `sources claim equivalence:` distinguishes an equivalence the literature + asserts (a citation) from one the note asserts (an argument that must be + written out). +- **Adopted general form** is written by the orchestrator at merge, because the + justification comes from lane 4's discriminators and lane 1 cannot see them + while running in parallel. It may never be justified by formalization + convenience. +- **The adopted form is written out, not merely named.** State the definition + as a complete sentence in this note's own conventions — the ambient objects + and their standing assumptions, the quantifiers in order, and the `(A#)` rows + it carries — and only then cite the `(D#)` it follows and the `(X#)` that + justifies it. "(D1), because (X3) shows (D2) drops the non-σ-finite case" is + the *justification*; it is not the definition, and a reader who stops there + has to reconstruct the mathematics from a verbatim quote in some source's own + notation. + **Why:** this paragraph is what the eventual formalization is written + against, and it is what `math-review` compares an elaborated type to. Both + need a statement whose quantifier order and hypotheses are unambiguous in + *one* place; a pointer to a quote in another notation is not that. +- **A canonical display supplements the statement; it never replaces it.** + When the literature writes the object as a standard display — an inclusion + chain, a commuting diagram — transcribing it after the written-out statement + is welcome, but the written-out sentence remains mandatory: a display-only + adopted form is the pointer-only failure the previous rule forbids, in + prettier clothes. +- No proposed notation, no identifiers. + +### Notation and conventions + +One row per axis the corpus actually touches; an irrelevant convention pinned +here is noise. The axes that bite in this domain: trace normalisation, the base +of the logarithm, which inner-product argument is conjugate-linear, ℏ = 1, +the range of a summation, unitality, nondegeneracy of representations, and +whether an order symbol is the Löwner order or containment of algebras. + +A convention a source leaves implicit is recorded as implicit, with the passage +the inference came from. That inference is tier (b) at best. + +### Results and dependencies + +- `proof status` ∈ proved in source / sketched / cited elsewhere / asserted. + Cheap to record and it tells the implementer where the real work is. +- **`Proof route:` — one reason per step.** Optional, and only on rows whose + proof status is `proved in source`. Each step names its justification — a + definition unfolded, a dependency invoked, a substitution, an approximation — + and the (R#)/(A#)/[ext] edge it consumes. **Every edge named in the route + must appear in `Depends on:`, and every `Depends on:` edge of a routed row + must be consumed by some step** — that cross-check is the field's point. + **Why:** a hypothesis the proof uses but the row never lists is the most + expensive omission a formalization inherits, and per-step naming is the + cheapest audit of the dependency list. It also keeps proof status honest: a + route cannot be written for a proof that was only skimmed, so a row that + claims `proved in source` and cannot state its route was read as a sketch. +- Every dependency a source does not prove in the corpus is marked `[ext: …]`. + At tier (c)/(d) it carries no locator and instead carries a one-line statement + of what the external result says — see the substitution rule below. +- One row per result. The same result under two conventions is one row with a + `Conventions:` flag; genuinely different results sharing a name are two rows + plus a lane 1 note. + +### Hypotheses + +- `Class` ∈ `provable` / `model-dependent` / `open`. +- `provable` requires a pointer to a proof — a source and locator, or an + argument written out. A library lacking the supporting lemmas is irrelevant. +- **`model-dependent` requires a named witness object where the hypothesis + fails.** No witness ⇒ the row is `open`, never `model-dependent`. + **Why:** this is the class a later hypothesis field would be justified by, and + AGENTS.md *Prove what is provable; do not defer it* records what happens when + that justification is never demanded: the deferred hypothesis becomes + permanent and the trusted base grows in silence. Demanding a witness at + extraction time is the cheapest place to stop it. +- `Scope` is `standing` (assumed throughout a source) or `local`. Standing + hypotheses receive no dependency edge. +- `Used by` lists the results that need it. A standing hypothesis no result + needs is a finding — say so in the report. + +### Degeneracies and boundary cases + +Every checklist item gets a row, including the ones where nothing happens: "no +effect" is a result, and an unlisted case is an unchecked one. One row per +quantifier swap and per dropped hypothesis. This is coverage, not a quota. + +The `intended case is nonvacuous` row is never omitted: it names an instance — +from the corpus, at that attestation's tier — where the adopted form holds +non-trivially, or records "none found — ". A corpus with no +non-trivial instance is a finding about the adopted form, in the same +discipline as `## Prior art`'s "could not find X, having searched …". +**Why:** the failure witnesses in `## Hypotheses` guard one direction — a +statement that proves too much — and this row guards the other: a definition +nothing satisfies formalizes cleanly and says nothing, and vacuity is cheapest +to catch before the Lean exists. + +### Rejected formulations and refuted claims + +- **Append-only. Rows are never deleted.** A changed disposition is edited in + place with a dated note appended: + `rejected → adopted (2026-09-01: the (X1) separating object was mis-stated)`. + **Why:** the record of what was tried and discarded is the part of an + extraction that a later reader cannot reconstruct, and a revision that + silently drops it destroys exactly the thing that makes re-visiting the design + cheap. +- `Disposition` ∈ `adopted` / `equivalent` / `rejected` / `preference-only` / + `open` / `refuted`. +- `rejected` requires a typed discriminator: **(X1) separating object** (named, + not "some algebra") / **(X2) degeneracy** / **(X3) generality loss** / + **(X4) source disagreement** (a fetched source discards it, with a quote) / + **(X5) conditional equivalence**. +- **"Harder to formalize" is never a discriminator**, nor is "not standard" + (that needs a source and belongs under `## Definition`), nor "equivalent + anyway" (that is the `equivalent` disposition). +- `preference-only` rows are weightless and **may not be cited from any other + section**. They exist only so the next run does not re-explore them. +- A formulation some source actually adopts may not be `rejected` here; it is a + variant under `## Definition`, and the most this table may hold is a + discriminator showing the two are inequivalent. + +### Prior art + +- **Facts only.** What exists, and how it relates to the note's variants. No + proposals about how this project should define, name, or structure anything. +- A search miss is recorded as "could not find X, having searched …" — never + "Mathlib has no X". +- Every row carries the Mathlib revision it was measured against. The row is + void once `lake-manifest.json`'s Mathlib revision moves. Nothing enforces this + automatically: expiry is decided by a reader comparing the row's revision to + the manifest, which is why the revision is written into the row rather than + left implicit in the note's date. A void row is not wrong — it is unmeasured, + and a claim resting on it drops to (c). + +### Sources + +**Append-only.** A source that was attempted and not obtained gets a row anyway, +recording what was tried — "not cited" and "could not be obtained" are different +facts. The cross-object retrieval ledger and the locator adjudications live in +`../sources.md`; this table is the per-note view. + +### Not investigated + +**Never omitted, and never empty.** The last item below always has content, so +an empty section means it was not written rather than that nothing was left +unexamined. State: + +- the `[ext: …]` edges from `## Results` that lane 5 never reached; +- the sources listed as not retrieved, and which rows depend on them; +- the degeneracy checklist items skipped, and why; +- the variants sighted during scoping and not pursued; +- and always, **the unexamined base**: the tier (c)/(d) rows that everything + above stands on. + +The risk lives here, not in what was extracted. + +## The three cross-cutting rules + +### Evidence tiers + +| Tier | What the writer had | What the row may contain | +|---|---|---| +| **(a) quoted** | the source in the cache, opened at the passage | a verbatim quote, a full locator, the cache path | +| **(b) read** | the same, passage read | a paraphrase and a full locator | +| **(c) attested** | another fetched source attributes the claim to this one | the claim and the attester's locator | +| **(d) recalled** | nothing | the claim, plus author and title | + +A row's tier is the **minimum** over what it rests on. + +### The locator substitution rule + +> A full locator — theorem number, section, page, equation number — appears only +> at tier (a) or (b). At (c) only the attesting source's locator. At (d) none. +> **A row forbidden a locator must instead state, in one line, what the cited +> result says.** + +**Why:** a number is cheap to fabricate and expensive to check; a statement is +expensive to fabricate and cheap to check. This repository already carries +locators written without the source in hand; the rule exists so the note does +not add more, and so the substituted sentence becomes something a later reader +can actually falsify. + +### The quote check + +> Every blockquote in the note must survive `grep -F` against +> `source.flat.txt` of **the source that row cites** — not against the cache at +> large. What fails is downgraded to (b) or deleted. + +Matching some other file in the cache proves the sentence exists somewhere, +which is not the claim the row makes. `source.flat.txt` rather than `source.txt` +because a quotation crossing a line break matches only there; see +`ingestion.md`. + +A quote that will not *render* is still a quote. Put it in a fenced code block, +as `Source macros` above requires, never touch the bytes: normalising a formula +so a previewer stops complaining destroys the only thing that makes the row +checkable — and the quote check greps a fenced block exactly as it greps a +blockquote. + +**Why:** it makes tier (a) mechanically decidable instead of self-reported, at a +cost of seconds. Converted PDFs are model output, not text, so a quote taken +from converted Markdown is (b) with `mineru-unchecked` until compared against +the page image; an arXiv LaTeX source is the original and needs no such check. + +**Known limitation.** The check is decidable only while the cache exists, and +the cache under `references//` is untracked and disposable. Once it is +deleted, a tier (a) row is re-checkable only by re-ingesting the source — the +`## Sources` table records the path and enough bibliographic detail to do that, +which is why that table is append-only. A downstream reader who cannot re-ingest +treats an (a) row as an (a) row: the check was run when the row was written, and +the row's locator is what makes the claim falsifiable against a physical copy. diff --git a/.claude/skills/math-extract/scripts/check_render.py b/.claude/skills/math-extract/scripts/check_render.py new file mode 100755 index 0000000..1e695c1 --- /dev/null +++ b/.claude/skills/math-extract/scripts/check_render.py @@ -0,0 +1,270 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.11" +# /// +"""Render check for an extraction note. + +Every math span in `docs/math/.md` must render **on its own**, and that is +the part that bites: **no macro definition survives from one math span to the +next.** Measured through `@vscode/markdown-it-katex`, the plugin VS Code's own +Markdown preview uses: `\\newcommand`, `\\gdef` and `\\global\\def` all fail to +reach the NEXT span — the plugin renders each span with fresh options, so +nothing is carried forward. + +`\\gdef` works fine *within the span that defines it*, though — nothing has to +survive anywhere for that. So the convention this checker enforces is: a +verbatim quote needing a source's own macro carries a local, self-contained +`\\gdef` of exactly that macro, defined and used inside the same `$...$` pair, +e.g. `$\\gdef\\lok#1{{\\mathcal #1}}\\lok{B}$`. That renders exactly as the +source intended, in every renderer, with no persistence required. A `\\gdef` +sitting in a span with nothing else to use it (the old document-level preamble, +reborn one span at a time) is exactly as broken as before, and is flagged the +same way. + + uv run .claude/skills/math-extract/scripts/check_render.py docs/math/.md + +Exit 0 clean, 1 on findings, 2 on a usage error. + +What counts as a math span: `$...$` inline and `$$`-delimited blocks, **outside +fenced code**. Fenced code is exempt — it is the fallback for a macro that +cannot be `\\gdef`'d at all, because the bytes survive untouched there and no +renderer tries to typeset them. + +The allowlist in `references/katex-commands.txt` was generated by probing a real +KaTeX build, one command at a time, and keeping only what actually rendered; it +contains no guesses. Regenerate it against a newer KaTeX with the recipe in +`references/note-format.md`. +""" + +from __future__ import annotations + +import re +import shutil +import subprocess +import sys +from pathlib import Path + +SKILL = Path(__file__).resolve().parent.parent +ALLOWLIST = SKILL / "references" / "katex-commands.txt" +RENDER_JS = SKILL / "scripts" / "render_check.js" + +FIX_ORDER = """Fix by preference order: + 1. In the note's OWN prose, write plain KaTeX (\\mathcal{A}, \\mathrm{Tr}, + \\operatorname{supp}) — a source's private macro has no business there. + 2. In a verbatim quote, give the macro a LOCAL \\gdef inside the same $...$ it + is used in: `$\\gdef\\lok#1{{\\mathcal #1}}\\lok{B}$`. This renders as the + source intended, with no cross-span persistence required, so it is the + default fix, not a fallback. Audit the body against the source's own + preamble (or mark it "reconstructed" when that preamble is unrecoverable) + in the note's opening-comment macro catalogue. + 3. Brace any multi-token argument: \\widetilde{\\mathcal U}, \\Delta_{\\mathcal U}. + 4. Fence a quote (or backtick a fragment) only when a macro cannot be + \\gdef'd at all (a \\newenvironment, a catcode change) — this shows raw + source LaTeX instead of the intended notation, so it is the last resort. + 5. There is still no document-level macro preamble: no definition form + survives from one math span to the next in the renderers this note has + to work in, whatever the definition form.""" + + +def real_render(note: Path) -> tuple[str, int]: + """Parse every span with a real KaTeX build, when one is available. + + This is the stronger half of the check. The allowlist below catches unknown + command *names*; only a parser catches an unbraced argument such as + `\\widetilde\\mathcal U`, an unbalanced delimiter, or a bad environment. + + Returns (report, findings). findings < 0 means the check could not run. + """ + if not (shutil.which("node") and RENDER_JS.is_file()): + return ("real render: skipped (no node)", -1) + if not (SKILL / "node_modules" / "@vscode" / "markdown-it-katex").is_dir(): + return ( + "real render: skipped — enable it with\n" + f" (cd {SKILL} && npm install --no-save markdown-it @vscode/markdown-it-katex)\n" + " node_modules/ is already gitignored, so nothing is added to the tree.", + -1, + ) + proc = subprocess.run( + ["node", str(RENDER_JS), str(note)], capture_output=True, text=True + ) + body = (proc.stdout + proc.stderr).strip() + return (body, 0 if proc.returncode == 0 else 1) + +# `\foo`, or a single non-letter control symbol such as `\<`, `\{`, `\|`. +COMMAND_RE = re.compile(r"\\[A-Za-z]+|\\[^A-Za-z\s]") +# The head of a macro definition: name, then zero or more #N parameters, then +# the opening brace of its body. The body itself is matched separately by +# counting braces, since it is frequently nested (`\gdef\BH{{\cal B}({\cal H})}`) +# and a single non-recursive regex cannot balance that in general. +DEF_HEAD_RE = re.compile( + r"\\(?:gdef|global\s*\\def|def|(?:new|renew|provide)command)" + r"\s*\{?(\\[A-Za-z]+|\\[^A-Za-z\s])\}?((?:#\d+)*)\s*\{" +) + + +def strip_defs(tex: str) -> tuple[str, set[str], bool]: + """Remove every `\\NAME(#n)*{body}` definition from `tex`. + + Returns (remaining_text, names_defined, is_local_form). A definition is + "local form" (\\newcommand/\\renewcommand/\\providecommand) if ANY match is + of that form -- those are the ones that never carry, full stop, and are + flagged regardless of where they appear. + """ + names: set[str] = set() + local_form = False + out = [] + i = 0 + n = len(tex) + while i < n: + m = DEF_HEAD_RE.match(tex, i) + if not m: + out.append(tex[i]) + i += 1 + continue + names.add(m.group(1)) + if re.match(r"\\(?:new|renew|provide)command", m.group(0)): + local_form = True + depth = 0 + k = m.end() - 1 # position of the opening '{' just matched + while True: + if tex[k] == "{": + depth += 1 + elif tex[k] == "}": + depth -= 1 + if depth == 0: + k += 1 + break + k += 1 + i = k + return "".join(out), names, local_form + + +def math_spans(text: str) -> list[tuple[int, str, str]]: + """Yield (line_number, kind, tex) for every span a renderer will typeset.""" + spans: list[tuple[int, str, str]] = [] + in_fence = False + in_block = False + block_start = 0 + block_buf: list[str] = [] + + for lineno, raw in enumerate(text.split("\n"), 1): + stripped = raw.lstrip("> ").rstrip() + if stripped.startswith("```") or stripped.startswith("~~~"): + in_fence = not in_fence + continue + if in_fence: + continue + if stripped == "$$": + if in_block: + spans.append((block_start, "block", "\n".join(block_buf))) + block_buf = [] + else: + block_start = lineno + in_block = not in_block + continue + if in_block: + block_buf.append(raw) + continue + # HTML comments never render. + line = re.sub(r"", "", raw) + for m in re.finditer(r"(? int: + if len(sys.argv) != 2: + print(__doc__) + return 2 + note = Path(sys.argv[1]) + if not note.is_file(): + print(f"not a file: {note}") + return 2 + if not ALLOWLIST.is_file(): + print(f"missing allowlist: {ALLOWLIST}") + return 2 + + supported = {c for c in ALLOWLIST.read_text().split("\n") if c} + text = note.read_text() + spans = math_spans(text) + + findings: list[str] = [] + local_form_lines: dict[str, int] = {} + empty_def_only_lines: list[int] = [] + undefined: dict[str, list[int]] = {} + self_contained = 0 + + for lineno, _kind, tex in spans: + remaining, names, local_form = strip_defs(tex) + if names: + self_contained += 1 + if local_form: + for n in names: + local_form_lines.setdefault(n, lineno) + # A span whose ENTIRE content was definitions, with nothing left to use + # them, is the old preamble anti-pattern reborn one span at a time. + if names and not remaining.strip(): + empty_def_only_lines.append(lineno) + for m in COMMAND_RE.finditer(remaining): + name = m.group(0) + if name in supported or name in names: + continue + undefined.setdefault(name, []).append(lineno) + + for name, lineno in sorted(local_form_lines.items()): + findings.append( + f"{note}:{lineno}: `{name}` is defined with \\newcommand/\\renewcommand/" + f"\\providecommand, which never carries even within its own span — use " + f"\\gdef instead (it works when defined and used inside the same $...$)" + ) + for lineno in empty_def_only_lines: + findings.append( + f"{note}:{lineno}: this span is only macro definitions, with nothing in " + f"the same span to use them — that is the old document-level-preamble " + f"pattern, one span at a time, and it does not carry to the NEXT span " + f"either. Move the definition into the span that actually uses the macro" + ) + + for name, lines in sorted(undefined.items()): + where = ", ".join(str(n) for n in lines[:6]) + more = f" (+{len(lines) - 6} more)" if len(lines) > 6 else "" + findings.append( + f"{note}: `{name}` is not a KaTeX command and has no local \\gdef in the " + f"same span — used at line(s) {where}{more}" + ) + + print(f"{len(spans)} math spans; {self_contained} carry a local macro " + f"definition; {len(undefined)} undefined commands") + + report, rc = real_render(note) + print(report) + + # The pipeline is authoritative when it ran: it decides what a math span is, + # and this scanner's idea of that is an approximation. Its findings are + # reported only as a hint in that case, and are the whole verdict otherwise. + if rc >= 0: + if findings: + print("\n(scanner hints, superseded by the pipeline above:)") + for f in findings: + print(" " + f) + if rc == 0: + print("\nrender check: clean") + return 0 + print() + print(FIX_ORDER) + return 1 + + if not findings: + print("render check: scanner clean; the pipeline did not run, so brace, " + "delimiter and span-boundary errors remain unchecked") + return 0 + print() + for f in findings: + print(f) + print() + print(FIX_ORDER) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.claude/skills/math-extract/scripts/ingest.py b/.claude/skills/math-extract/scripts/ingest.py new file mode 100644 index 0000000..6bcaef5 --- /dev/null +++ b/.claude/skills/math-extract/scripts/ingest.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.10" +# dependencies = [] +# /// +"""Fetch one source into the math-extract corpus cache. + +Usage: + uv run .claude/skills/math-extract/scripts/ingest.py + [--slug SLUG] + [--cache-dir DIR] + [--allow-mineru] + [--pages START-END] + [--backend B] [--effort E] + +Tries the ladder in order and stops at the first rung that yields text: + + 1. arXiv LaTeX source (arxiv.org/e-print/) -- the original text, with + every formula exactly as the author wrote it. Quotes taken from here + need no further checking. + 2. arXiv HTML (arxiv.org/html/) -- LaTeXML output. + 3. Any other URL, or a local .html/.txt/.tex file -- tags stripped. + + 4. A PDF through MinerU's CPU pipeline, but only with --allow-mineru. It + costs minutes, runs one document at a time, and produces model inference + rather than text, so it is opt-in per call and the script will never + install the converter itself. Without the flag a PDF exits 5 and says + what the conversion would cost. An arXiv *pdf* URL is not a PDF for this + purpose: the identifier is recovered from it and rung 1 fetches the LaTeX + instead, which is strictly better. + +Writes into // (default cache-dir: references/, gitignored): + + raw/ the bytes as fetched, unmodified + source.txt concatenated plain text, one line per source line + source.flat.txt the same text with each paragraph flattened onto one line + +`source.flat.txt` is what the skill's quote check greps: a verbatim quotation +spanning a line break in the original matches there and nowhere else. + +Prints a JSON summary to stdout and exits 0 on success. Exit codes: 2 bad +arguments, 3 every rung failed, 4 fetched but effectively empty, 5 the source is +a PDF and needs the converter. +""" + +from __future__ import annotations + +import argparse +import gzip +import io +import json +import re +import shutil +import subprocess +import sys +import tarfile +import unicodedata +import urllib.error +import urllib.request +from dataclasses import dataclass +from datetime import date +from html.parser import HTMLParser +from pathlib import Path + +USER_AGENT = "math-extract/1.0 (Lean formalization research; contact via repository)" +TIMEOUT = 60 +MINERU_TIMEOUT = 3600 +TEX_SUFFIXES = {".tex", ".ltx", ".bbl"} +MIN_USEFUL_CHARS = 400 + + +@dataclass(frozen=True) +class Options: + """Choices the rungs need that are not part of the target itself.""" + + allow_mineru: bool = False + pages: str | None = None + backend: str = "auto" + effort: str | None = None + +# 2202.03357 / 2202.03357v2 / math/0604123 / math-ph/0411058v1 +ARXIV_NEW = re.compile(r"(? None: + super().__init__(convert_charrefs=True) + self.parts: list[str] = [] + self._skip_depth = 0 + + def handle_starttag(self, tag: str, attrs: object) -> None: + if tag in self.SKIP: + self._skip_depth += 1 + elif tag in self.BLOCK: + self.parts.append("\n") + + def handle_endtag(self, tag: str) -> None: + if tag in self.SKIP and self._skip_depth: + self._skip_depth -= 1 + elif tag in self.BLOCK: + self.parts.append("\n") + + def handle_data(self, data: str) -> None: + if not self._skip_depth: + self.parts.append(data) + + def text(self) -> str: + return "".join(self.parts) + + +def fetch(url: str) -> tuple[bytes, str]: + """GET a URL, returning its body and content type.""" + request = urllib.request.Request(url, headers={"User-Agent": USER_AGENT}) + with urllib.request.urlopen(request, timeout=TIMEOUT) as response: + return response.read(), response.headers.get("Content-Type", "") + + +def arxiv_id_of(target: str) -> str | None: + """Recover a bare arXiv identifier from an id, an abs URL, or a pdf URL.""" + if "arxiv.org" in target or not target.startswith(("http://", "https://")): + for pattern in (ARXIV_NEW, ARXIV_OLD): + match = pattern.search(target) + if match: + return match.group(1) + return None + + +def slugify(value: str, limit: int = 60) -> str: + normalised = unicodedata.normalize("NFKD", value).encode("ascii", "ignore").decode() + cleaned = re.sub(r"[^a-zA-Z0-9]+", "-", normalised).strip("-").lower() + return (cleaned[:limit].rstrip("-")) or "source" + + +def derive_slug(target: str, arxiv_id: str | None) -> str: + if arxiv_id: + return "arxiv-" + arxiv_id.replace("/", "-") + if target.startswith(("http://", "https://")): + tail = target.rstrip("/").rsplit("/", 1)[-1] + return slugify(tail or target) + return slugify(Path(target).stem) + + +def flatten(text: str) -> str: + """Collapse each blank-line-separated paragraph onto a single line. + + A quotation that runs across a line break in the source is one contiguous + string here, which is what makes `grep -F` a usable check on it. + """ + paragraphs = re.split(r"\n\s*\n", text) + flattened = (" ".join(paragraph.split()) for paragraph in paragraphs) + return "\n".join(p for p in flattened if p) + "\n" + + +def unpack_eprint(payload: bytes, raw_dir: Path) -> list[tuple[str, str]]: + """Unpack an arXiv e-print into (name, text) pairs of its TeX members.""" + members: list[tuple[str, str]] = [] + try: + with tarfile.open(fileobj=io.BytesIO(payload), mode="r:gz") as archive: + for member in archive.getmembers(): + if not member.isfile(): + continue + if Path(member.name).suffix.lower() not in TEX_SUFFIXES: + continue + handle = archive.extractfile(member) + if handle is None: + continue + body = handle.read().decode("utf-8", errors="replace") + (raw_dir / Path(member.name).name).write_text(body, encoding="utf-8") + members.append((member.name, body)) + except tarfile.ReadError: + # A single-file submission arrives as bare gzip rather than a tarball. + body = gzip.decompress(payload).decode("utf-8", errors="replace") + (raw_dir / "main.tex").write_text(body, encoding="utf-8") + members.append(("main.tex", body)) + # The file carrying \documentclass or \begin{document} comes first: it is + # the one a reader would open, and ordering here orders source.txt. + members.sort(key=lambda item: 0 if re.search( + r"\\documentclass|\\begin\{document\}", item[1]) else 1) + return members + + +def rung_arxiv_source(arxiv_id: str, raw_dir: Path) -> tuple[str, str] | None: + payload, _ = fetch(f"https://arxiv.org/e-print/{arxiv_id}") + if payload[:4] == b"%PDF": + return None # source withdrawn; the HTML rung may still work + members = unpack_eprint(payload, raw_dir) + if not members: + return None + joined = "\n\n".join(f"% ==== {name} ====\n{body}" for name, body in members) + return joined, "arxiv-latex" + + +def rung_html(url: str, raw_dir: Path, stage: str, opts: Options) -> tuple[str, str] | None: + payload, content_type = fetch(url) + if payload[:4] == b"%PDF" or "application/pdf" in content_type: + return handle_pdf_bytes(payload, raw_dir, "downloaded.pdf", url, opts) + (raw_dir / "page.html").write_bytes(payload) + parser = TextExtractor() + parser.feed(payload.decode("utf-8", errors="replace")) + return parser.text(), stage + + +def rung_local(path: Path, raw_dir: Path, opts: Options) -> tuple[str, str] | None: + payload = path.read_bytes() + if payload[:4] == b"%PDF": + return handle_pdf_bytes(payload, raw_dir, path.name, str(path), opts) + (raw_dir / path.name).write_bytes(payload) + body = payload.decode("utf-8", errors="replace") + if path.suffix.lower() in {".html", ".htm"}: + parser = TextExtractor() + parser.feed(body) + return parser.text(), "local-html" + return body, "local-text" + + +def handle_pdf_bytes(payload: bytes, raw_dir: Path, name: str, + target: str, opts: Options) -> tuple[str, str]: + """Send a PDF to the converter, or refuse and say what it would cost.""" + if not opts.allow_mineru: + raise SystemExit(pdf_refusal(target)) + pdf_path = raw_dir / name + pdf_path.write_bytes(payload) + return rung_mineru(pdf_path, raw_dir.parent, opts) + + +def pdf_refusal(target: str) -> int: + report({ + "ok": False, + "reason": "pdf", + "target": target, + "message": "This source is a PDF. Converting it is rung 4: minutes of CPU, " + "one document at a time, and the result is model output rather " + "than text, so quotes taken from it are tier (b) until checked " + "against the page image. Re-run with --allow-mineru (and " + "--pages START-END where only part of the document is needed) " + "once that cost is agreed. See references/ingestion.md.", + }) + return 5 + + +def resolve_backend(opts: Options) -> str: + """Pick the mineru backend. + + `auto` resolves to `pipeline` regardless of GPU: torch uses CUDA on its own + when the device is there, so pipeline is fast on GPU and correct on CPU. + The VLM-based backends (`hybrid-engine`, `vlm-engine`) are opt-in — this + machine's 8GB of VRAM is their minimum, shared with the Windows desktop, + so they can OOM where pipeline cannot. + """ + return "pipeline" if opts.backend == "auto" else opts.backend + + +def find_mineru() -> str | None: + """Locate the converter: project venv first, then PATH. + + This script runs under `uv run` with its own PEP 723 metadata, so uv builds + it an isolated environment and the project venv is *not* on PATH. The + converter lives in that venv (installed by `uv sync`), so look there + explicitly before falling back to PATH. + """ + repo_root = Path(__file__).resolve().parents[4] + candidate = repo_root / ".venv" / "bin" / "mineru" + if candidate.is_file(): + return str(candidate) + return shutil.which("mineru") + + +def rung_mineru(pdf_path: Path, out_dir: Path, opts: Options) -> tuple[str, str]: + """Convert a PDF with MinerU. Never installs anything.""" + binary = find_mineru() + if binary is None: + report({ + "ok": False, + "reason": "mineru-missing", + "target": str(pdf_path), + "message": "The converter is not installed. Run `uv sync` from the " + "repository root — mineru is a locked dependency group in " + "pyproject.toml and is in default-groups, so a plain sync " + "installs it. If someone ran `uv sync --no-group mineru`, " + "that is what removed it. This script will not install it " + "for you.", + }) + raise SystemExit(5) + + mineru_dir = out_dir / "mineru" + mineru_dir.mkdir(parents=True, exist_ok=True) + # The backend is always passed explicitly: MinerU 3.x defaults to + # hybrid-engine, which is not the right default at 8GB of shared VRAM. + backend = resolve_backend(opts) + command = [binary, "-p", str(pdf_path), "-o", str(mineru_dir), "-b", backend] + if opts.effort and backend != "pipeline": + # --effort steers the VLM backends only; pipeline does not take it. + command += ["--effort", opts.effort] + if opts.pages: + start, _, end = opts.pages.partition("-") + command += ["-s", start] + if end: + command += ["-e", end] + + try: + completed = subprocess.run(command, capture_output=True, text=True, + timeout=MINERU_TIMEOUT) + except subprocess.TimeoutExpired: + report({"ok": False, "reason": "mineru-timeout", "target": str(pdf_path), + "message": f"Conversion exceeded {MINERU_TIMEOUT}s. Convert a page " + "range with --pages instead of the whole document."}) + raise SystemExit(3) + if completed.returncode != 0: + report({"ok": False, "reason": "mineru-failed", "target": str(pdf_path), + "returncode": completed.returncode, + "message": completed.stderr[-800:] or "no stderr"}) + raise SystemExit(3) + + markdowns = sorted(mineru_dir.rglob("*.md"), key=lambda p: p.stat().st_size, reverse=True) + if not markdowns: + report({"ok": False, "reason": "mineru-empty", "target": str(pdf_path), + "message": "The converter produced no Markdown. A scanned document " + "may need OCR; see references/ingestion.md."}) + raise SystemExit(4) + return markdowns[0].read_text(encoding="utf-8", errors="replace"), f"mineru-{backend}" + + +def report(payload: dict) -> None: + json.dump(payload, sys.stdout, indent=2, ensure_ascii=False) + sys.stdout.write("\n") + + +def main() -> int: + parser = argparse.ArgumentParser(description="Fetch one source into the corpus cache.") + parser.add_argument("target", help="arXiv id, URL, or local path") + parser.add_argument("--slug", help="override the derived cache slug") + parser.add_argument("--cache-dir", default="references", help="cache root (default: references)") + parser.add_argument("--allow-mineru", action="store_true", + help="permit rung 4 (PDF conversion): minutes of CPU, serial, " + "and the output is model inference rather than text") + parser.add_argument("--pages", metavar="START-END", + help="page range for rung 4, e.g. 40-62 — convert the chapter " + "that matters instead of the whole book") + parser.add_argument("--backend", choices=["auto", "pipeline", "hybrid-engine", "vlm-engine"], + default="auto", + help="mineru backend for rung 4. auto = pipeline (uses the GPU " + "by itself when one is there). The VLM backends are " + "opt-in: 8GB of VRAM is their minimum and it is shared " + "with the Windows desktop, so they can OOM") + parser.add_argument("--effort", choices=["medium", "high"], + help="rung 4 quality knob, forwarded only to the VLM backends") + args = parser.parse_args() + + target = args.target.strip() + if not target: + print("[error] empty target", file=sys.stderr) + return 2 + opts = Options(allow_mineru=args.allow_mineru, pages=args.pages, + backend=args.backend, effort=args.effort) + + arxiv_id = arxiv_id_of(target) + slug = args.slug or derive_slug(target, arxiv_id) + out_dir = Path(args.cache_dir) / slug + raw_dir = out_dir / "raw" + raw_dir.mkdir(parents=True, exist_ok=True) + + attempts: list[dict] = [] + result: tuple[str, str] | None = None + + local_path = Path(target) + if local_path.exists(): + result = rung_local(local_path, raw_dir, opts) + if result is not None: + attempts.append({"rung": result[1], "ok": True}) + else: + ladder = [] + if arxiv_id: + ladder.append(("arxiv-latex", lambda: rung_arxiv_source(arxiv_id, raw_dir))) + ladder.append(("arxiv-html", + lambda: rung_html(f"https://arxiv.org/html/{arxiv_id}", + raw_dir, "arxiv-html", opts))) + if target.startswith(("http://", "https://")): + ladder.append(("url-html", lambda: rung_html(target, raw_dir, "url-html", opts))) + if not ladder: + print(f"[error] cannot interpret target: {target}", file=sys.stderr) + return 2 + + for name, rung in ladder: + try: + result = rung() + except urllib.error.HTTPError as error: + attempts.append({"rung": name, "ok": False, "error": f"HTTP {error.code}"}) + continue + except (urllib.error.URLError, OSError, gzip.BadGzipFile) as error: + attempts.append({"rung": name, "ok": False, "error": str(error)}) + continue + if result is None: + attempts.append({"rung": name, "ok": False, "error": "no text at this rung"}) + continue + attempts.append({"rung": name, "ok": True}) + break + + if result is None: + report({"ok": False, "reason": "unreachable", "slug": slug, + "stage_attempts": attempts, + "message": "Every rung failed. Record the source as not retrieved " + "in sources.md, with what was tried, and cap every claim " + "resting on it at tier (d) — no locators."}) + return 3 + + text, stage = result + (out_dir / "source.txt").write_text(text, encoding="utf-8") + flat = flatten(text) + (out_dir / "source.flat.txt").write_text(flat, encoding="utf-8") + + summary = { + "ok": True, + "slug": slug, + "stage": stage, + "verbatim": stage == "arxiv-latex", + "cache": str(out_dir), + "quote_check_file": str(out_dir / "source.flat.txt"), + "chars": len(text), + "retrieved": date.today().isoformat(), + "arxiv_id": arxiv_id, + "stage_attempts": attempts, + } + if stage.startswith("mineru"): + summary["backend"] = stage.removeprefix("mineru-") + summary["caveat"] = ("Converted, not transcribed: the formulas are model " + "output. Quotes from this cache are tier (b) with " + "mineru-unchecked, and reach (a) only after being " + "compared against the page image.") + if len(flat.strip()) < MIN_USEFUL_CHARS: + summary["ok"] = False + summary["reason"] = "empty" + summary["message"] = ("Fetched, but there is almost no text. Treat the source " + "as not retrieved unless the cache says otherwise.") + report(summary) + return 4 + + report(summary) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.claude/skills/math-extract/scripts/render_check.js b/.claude/skills/math-extract/scripts/render_check.js new file mode 100644 index 0000000..5e62be9 --- /dev/null +++ b/.claude/skills/math-extract/scripts/render_check.js @@ -0,0 +1,152 @@ +// Ground truth for the render check: run an extraction note through the same +// markdown + KaTeX pipeline a previewer uses, and report every ParseError with +// the note's own line number. +// +// Why a real markdown pipeline and not a hand-rolled `$…$` scanner: the scanner +// version of this script reported "0 ParseErrors" on a note that threw two of +// them in VS Code. A markdown renderer decides what a math span *is* — where it +// starts, where it ends, whether a `$` is math at all — and it does not agree +// with a regex. Delimiter pairing across lines, `$` inside table cells, and +// emphasis interacting with `^*` are all places the two diverge. So the check +// renders the document. +// +// Two facts worth keeping in view while reading a failure: +// +// * KaTeX's \newcommand is a *local* definition. It does not survive from one +// math span to the next -- not even when the renderer passes a shared +// `macros` object -- so a `$$`-block preamble of \newcommands leaves every +// later use throwing `Undefined control sequence`. Only \gdef is written +// back, and only a renderer that shares macro state carries it. +// * A command can be perfectly well defined and still fail: `\widetilde\mathcal U` +// and `\Delta_\mathcal U` are parse errors because `\widetilde` and `_` take a +// single token. No allowlist catches that; a parser does. +// +// node render_check.js +// +// Reported in two classes: +// +// ParseError the pipeline could not typeset the span at all. +// portability hazard the span typesets *here* but relies on behaviour engines +// disagree about — `\,_2` (a subscripted thin space) and +// `\Delta_\mathcal U` (an unbraced multi-token argument) +// are the two that reached a reader before this check +// existed. KaTeX 0.16.47 and 0.18.4 both accept them. +// +// Requires markdown-it and @vscode/markdown-it-katex, which is the plugin VS +// Code's own Markdown preview uses: +// +// (cd .claude/skills/math-extract && npm install --no-save markdown-it @vscode/markdown-it-katex) +// +// Exit 0 clean, 1 on findings, 2 if the pipeline is unavailable. + +const fs = require('fs'); + +let MarkdownIt, katexPlugin, katex; +try { + MarkdownIt = require('markdown-it'); + katex = require('@vscode/markdown-it-katex/node_modules/katex'); + katexPlugin = require('@vscode/markdown-it-katex').default + || require('@vscode/markdown-it-katex'); +} catch (e) { + console.error('pipeline unavailable: ' + e.message); + process.exit(2); +} + +const file = process.argv[2]; +if (!file) { console.error('usage: node render_check.js '); process.exit(2); } +const text = fs.readFileSync(file, 'utf8'); + +const errors = []; +const md = new MarkdownIt({ html: true }) + .use(katexPlugin, { + throwOnError: true, + strict: false, + // The plugin swallows the error and emits it as HTML; capture it instead. + errorColor: '#cc0000', + }); + +// The plugin catches KaTeX errors internally and renders them as text, so hook +// the renderer rules to see them rather than trusting an exception to escape. +for (const rule of ['math_inline', 'math_block', 'math_inline_block', 'math_block_eqno']) { + const prev = md.renderer.rules[rule]; + if (!prev) continue; + md.renderer.rules[rule] = function (tokens, idx, options, env, self) { + const out = prev.call(this, tokens, idx, options, env, self); + if (/katex-error|ParseError|Undefined control sequence/i.test(out)) { + const tok = tokens[idx]; + const line = tok.map ? tok.map[0] + 1 : null; + // Re-run the span through KaTeX directly to recover the message text; the + // plugin only leaves a coloured placeholder in the HTML. + let why = 'KaTeX error'; + try { + katex.renderToString(tok.content, { throwOnError: true, strict: false }); + } catch (err) { + why = (err.rawMessage || err.message || why).split('\n')[0]; + } + errors.push({ line, tex: tok.content, why }); + } + return out; + }; +} + +// Portability hazards: constructs that *this* KaTeX build happens to accept but +// that other engines reject. They are the ones that get reported by a reader +// rather than by the check, so they are collected from the spans the pipeline +// actually extracted -- inline code and fenced blocks are excluded for free. +const hazards = []; +const HAZARD_RULES = [ + [/\\[,;:!]\s*[_^]|\\q?quad\s*[_^]/, + 'a spacing command (\\, \\; \\: \\! \\quad) immediately subscripted or superscripted — ' + + 'subscripting a space is meaningless and engines disagree on whether it is an error'], + [/(?:[_^]|\\widetilde|\\overline|\\bar|\\hat|\\tilde|\\vec)\s*\\(?:mathcal|mathrm|mathbb|mathfrak|operatorname)\b/, + 'an unbraced multi-token argument (e.g. \\widetilde\\mathcal U) — brace it: \\widetilde{\\mathcal U}'], +]; +for (const rule of ['math_inline', 'math_block', 'math_inline_block', 'math_block_eqno']) { + const prev = md.renderer.rules[rule]; + if (!prev) continue; + const wrapped = md.renderer.rules[rule]; + md.renderer.rules[rule] = function (tokens, idx, options, env, self) { + const tex = tokens[idx].content; + for (const [re, why] of HAZARD_RULES) if (re.test(tex)) hazards.push({ tex, why }); + return wrapped.call(this, tokens, idx, options, env, self); + }; +} + +// The plugin dumps each KaTeX error object through `console.log` as it goes. +// We report the same errors ourselves, with line numbers, so silence it for the +// duration of the render only. +const realLog = console.log; +console.log = () => {}; +try { + md.render(text); +} finally { + console.log = realLog; +} + +// The token map is only reliable for block tokens; recover inline line numbers +// by locating the offending TeX in the source. +const lines = text.split('\n'); +function locate(tex) { + const needle = tex.trim().split('\n')[0].trim(); + if (!needle) return null; + for (let i = 0; i < lines.length; i++) if (lines[i].includes(needle)) return i + 1; + return null; +} + +for (const e of errors) { + const line = locate(e.tex) || e.line || '?'; + console.log(`line ${line}: ${e.why.slice(0, 160)}`); + console.log(` ${e.tex.replace(/\s+/g, ' ').slice(0, 130)}`); +} + +for (const h of hazards) { + const line = locate(h.tex) || '?'; + console.log(`line ${line}: portability hazard — ${h.why}`); + console.log(` ${h.tex.replace(/\s+/g, ' ').slice(0, 130)}`); +} + +console.log( + `\n${errors.length} ParseError(s) and ${hazards.length} portability hazard(s) ` + + `from the markdown+KaTeX pipeline` +); +process.exit(errors.length + hazards.length ? 1 : 0); diff --git a/.claude/skills/math-extract/sources.md b/.claude/skills/math-extract/sources.md new file mode 100644 index 0000000..6412b09 --- /dev/null +++ b/.claude/skills/math-extract/sources.md @@ -0,0 +1,93 @@ +# Sources ledger + +The `math-extract` skill's memory across runs, keyed by **source**. The notes +under `docs/math/` are keyed by **object**; this file holds what a note +structurally cannot, because it is true of the source no matter which object was +being extracted when it was discovered. + +Step 1 of the skill reads it. Step 6 writes it. Nothing else touches it. + +## What goes in + +**One row per work, not per run.** Three kinds of fact: + +1. **Retrieval attempts.** That a book could not be obtained is true for every + object that cites it. Without this row the next extraction spends the same + ten minutes discovering the same thing. +2. **Locator adjudications.** When someone finally opens a source and finds that + a widely-copied theorem number does not say what it is said to say, the + correction belongs to the source. Recorded once, read forever. This is the + row type that pays for the file. +3. **Edition and version drift.** Preprint versus published numbering, second + editions, arXiv versions. A locator is meaningless without knowing which one + it indexes. + +**What does not go in:** anything about a mathematical object. Definitions, +results, hypotheses, rejected formulations and their discriminators all live in +`docs/math/.md`. A fact that would change if you were extracting a +different object is in the wrong file. + +## Expiry — read this before trusting a row + +- A **`retrieved`** row does not expire. The text does not change. +- A **`not retrieved`** row expires **six months** after `Last tried`. Paywalls + lift, scans appear, authors post copies. A stale `not retrieved` that stops + someone from trying again is the one real danger of this file, so when in + doubt, try again rather than trust the row. +- A **locator adjudication** does not expire, but it is bound to the edition in + `Version/Ed.`. An adjudication made against the second edition says nothing + about the first. +- An **arXiv** row is bound to its version. When a new version appears, locators + taken from the old one may have moved; the mathematics has not. Only the + locators in notes citing that source are affected. + +## Format + +| Key | Work | Status | Cache | Version/Ed. | Last tried | Notes | +|---|---|---|---|---|---|---| + +- **Key** — the short citation key used in the notes (`DL84`, `TAK-I`, + `BHATIA`). Stable; notes cite it. +- **Work** — author(s), *title*, venue or publisher, year. Enough to identify + the work without a locator. +- **Status** — `retrieved` | `partial` (abstract or fragment only) | + `not retrieved` | `no digital copy known`. +- **Cache** — path under `references/`, or `—`. +- **Version/Ed.** — arXiv version, edition, or `published` / `preprint`. +- **Last tried** — `YYYY-MM-DD` of the most recent retrieval attempt. +- **Notes** — what was tried and where, locator adjudications, numbering drift + between editions. Adjudications are written as + `locator X: says ` or + `locator X: not found in this edition`. + +## Entries + +| Key | Work | Status | Cache | Version/Ed. | Last tried | Notes | +|---|---|---|---|---|---|---| +| KOE03 | S. Köster, *Structure of Coset Models*, dissertation, arXiv math-ph/0308031 (2003) | retrieved | `references/arxiv-math-ph-0308031/` | arXiv | 2026-08-14 | arXiv LaTeX (rung 1), verbatim; single source file `mathphkoediss.tex` | +| HS17 | S. Hollands, K. Sanders, *Entanglement measures and their properties in quantum field theory*, arXiv 1702.04924 | retrieved | `references/arxiv-1702.04924/` | arXiv | 2026-08-14 | arXiv LaTeX (rung 1), verbatim. Citation-key trap: HS17's `buchholz_4` = Buchholz–Wichmann 1986, *not* Buchholz 1974; `buchholz_2` = BDF87; `doplicher_4` = DL84 (verified against its bibliography) | +| dB74 | D. Buchholz, *Product states for local algebras*, Comm. Math. Phys. 36 (1974) 287–304 | partial | `references/buchholz-1974-product-states/` | published | 2026-08-15 | Project Euclid PDF; MinerU `hybrid-engine --effort high`, pp. 1–8 of 18; mineru-unchecked. Cor. 2.4 proves product state ⇒ interpolating type I factors; the converse is asserted just after it. **locator p. 292 (Ch. II items a) and b)) is adjudicated**: the page number was recovered by extracting the OCR text layer of `mineru/downloaded/hybrid_auto/downloaded_origin.pdf` with `pypdf`, **independently of MinerU** — the page carries the running head `292 D. Buchholz` and reproduces the passage word for word including the typo `maped`. Tier stays **(b)** (`mineru-cross-checked-against-PDF-text-layer`) because no page *image* comparison is possible: `pdftoppm`, `pdftotext`, `mutool` and `gs` are all absent from this container. The same extraction resolves item b)'s footnote — it reads *This example is due to Araki*, so that construction is **Araki's, not Buchholz's**. The paper carries **three** different separation relations (translation buffer in Thm 2.2, closure in Ch. II a), positive distance in Ch. III); citing "dB74's separation condition" without saying which is ambiguous | +| DL84 | S. Doplicher, R. Longo, *Standard and split inclusions of von Neumann algebras*, Invent. Math. 75 (1984) 493–536 | partial | `references/doplicher-longo-1984-standard-split/` | published | 2026-08-14 | GDZ digitization (volume `PPN356556735_0075`, article div `LOG_0034`, found via the METS file); MinerU `hybrid-engine --effort high`, pp. 1–14 of 44 (§0–§4); mineru-unchecked. locator Prop. 1.2: says joint cyclic-separating vectors exist *under* standard action / properly infinite commutants — citing it for bare "properly infinite on separable ℋ" (as KOE03 does at its source.txt 6666–6669) drops needed hypotheses. §9–10 (the non-split field theories) not converted. Its §0 phrases the split property's separation as `space-like separated by non-zero distance` and cites dB74, whose cached scope (§III, smooth boundaries + positive distance, free neutral massive scalar) is **narrower** than that paraphrase | + + +| HM06 | H. Halvorson, M. Müger, *Algebraic Quantum Field Theory*, in *Handbook of the Philosophy of Physics*, arXiv math-ph/0602036 | retrieved | `references/arxiv-math-ph-0602036/` | arXiv | 2026-08-15 | arXiv LaTeX (rung 1), verbatim; single source file `reconstruction.tex`. Leaves "spacelike separated" **undefined** and writes the causal complement `O'` with no formula, so it cannot be cited for either. Its `\begin{fact}` (strictly ⇒ strongly spacelike separated) carries no proof and no citation. Prop. `frees` is stated for *strongly* but its proof's first line says *strictly* — a typo; the proposition as stated is proved. **Added 2026-08-16 (bicommutant run)**: it states the double commutant theorem in a **two**-condition form (WOT-closed ⟺ `R''=R`, no SOT) inside a `\begin{fact}`, with no proof and no citation, and elects the algebraic condition as its definition. **One of its facts is false as printed**: `If $S$ is a bounded, convex subset of $\bh$, then the weak, ultraweak, and norm closures of $S$ are the same` — the closed unit ball of `K(H)`, `dim H = ∞`, is norm-closed but weakly dense in the unit ball of `B(H)`. Deleting `norm` makes it true, and the corrected version is what a σ-weak/WOT argument would want, so citing the printed form imports the error. Also: its condition 2 is typed `$\alg(\alg{R}')'=\alg{R}$`, where the one-argument `\alg` swallows the parenthesis; its adjoint clause quantifies `x,y` over the *algebra* instead of the Hilbert space; and its weak/strong/ultraweak "seminorms" are written without absolute values | +| GLRV99 | D. Guido, R. Longo, J. E. Roberts, R. Verch, *Charged sectors, spin and statistics in quantum field theory on curved spacetimes*, arXiv math-ph/9906019 | retrieved | `references/arxiv-math-ph-9906019/` | arXiv | 2026-08-15 | arXiv LaTeX (rung 1), verbatim; single source file `main.tex`. **Notation trap: `𝒪^⊥` denotes both the sieve `{𝒪₁ ∈ 𝒦 : 𝒪₁ ⊥ 𝒪}` (§3.1) and the point set `M ∖ J̄(𝒪)` (§2.1), in one paper.** Cross-reference errata: the Extension Theorem's proof cites "Lemma 3.A.4" where 3A.5 is needed and prints three spellings of one label; Lemma 3.6 says `dimension $\geq 2$` then treats dimension two as the remaining case; §3.5 refers to `hatduality` as a Lemma where the environment is a Proposition. locator §3.1 (`source.txt` 1218–1223): the axiom list for ⊥ is a),b),c) **only**, and the source says the list is a floor (`The necessary properties will be introduced as needed`); a **fourth** condition appears at `source.txt` 1878–1882, before Theorem 3.13 | +| NAA13 | P. Naaijkens, *Quantum spin systems on infinite lattices*, arXiv 1311.2717 | retrieved | `references/arxiv-1311.2717/` | arXiv | 2026-08-15 | arXiv LaTeX (rung 1), verbatim; multi-file (`aqft.tex`, `qlattice.tex`, `opalg.tex`, …), macros in `qlattice.tex`. The only corpus source stating `𝒪 = 𝒪″` for double cones, and it states it without proof. Its `𝒫_f(Γ)` **contains `∅`**, with `𝒜(∅) = ℂI` fixed explicitly — so any claim that the lattice index set fails an existence-of-a-⊥-partner condition is false for *this* index set | +| BGL93 | R. Brunetti, D. Guido, R. Longo, *Modular structure and duality in conformal quantum field theory*, arXiv funct-an/9302008 | retrieved | `references/arxiv-funct-an-9302008/` | arXiv | 2026-08-15 | arXiv LaTeX (rung 1), verbatim; single source file `main.tex`. locator Cor. 2.7: discharged by `\proof Immediate, see [\ref(Long1)].` and conditional on Remark 2.6's spectral hypothesis — do not cite it as an unconditional type III₁ result. locator Thm 3.3: its proof cites a **Lemma 3.4 that does not exist**; Lemma 3.2 is meant. Its "distal split property" (§3 assumption (b)) is an **existential over one pair of regions**, not a metric strengthening of the separation relation. Its non-split net `ℬ(𝒪) = 𝒜(π⁻¹𝒪)` is attributed to a **private remark** of Buchholz, so no locator can ever exist for the attribution | +| BFV01 | R. Brunetti, K. Fredenhagen, R. Verch, *The generally covariant locality principle*, arXiv math-ph/0112041 | retrieved | `references/arxiv-math-ph-0112041/` | arXiv | 2026-08-15 | arXiv LaTeX (rung 1), verbatim. **The distributed source contains two complete `\begin{document}…\end{document}` bodies** — `source.txt` lines 118–2851 and 2957–4225 (`source.flat.txt` offsets 3687 and 144453). Only the first is the compiled paper; the second is a shorter earlier draft that even defines `\frakA` differently. **Take every locator from the first body**, and expect `grep -F` to return 2 for shared passages — that is not a duplicate-quote defect. Its `𝒦(M,ḡ)` is defined by two conditions only (relatively compact, causally convex), and openness/connectedness/non-emptiness are forced only by the *next* sentence's demand that each region be an object of `𝔐`; the two readings give different index sets. `cf.\ condition $(ii)$` points at orientation-preservation where causal convexity is condition (i), in **both** bodies, so authorial | +| BF26 | B. Blackadar, I. Farah, *Separable C\*-algebras Without the Countable Axiom of Choice*, arXiv:2602.15812 | retrieved | `references/arxiv-2602.15812/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim; multi-file, macros in `measpreambleu.tex` (`\cstar`, `\cB`, `\cH`) and `25-Cstar-choice.tex` (`\sfS`). **locator `SepRepThm` is adjudicated: the theorem carries no proof.** `source.txt:589–592` shows `\begin{Theorem}\label{SepRepThm}` … `\end{Theorem}` followed directly by a commented-out paragraph — no `\begin{Proof}` environment anywhere after it. Its only justification is the preceding sentence, which establishes faithfulness and never mentions separability of the Hilbert space. The paper is ZF throughout, so its "separable Hilbert space" must be read topologically, not as "has a countable orthonormal basis" — it says itself that the two come apart without countable choice. Two typos: `L.states`' proof opens `$\varphi(x^*x)=\|x\|$` where ‖x‖² is meant (corrected three lines later in the same proof), and `Corollary~8.4.3` is cited to two different works (`blackadar2023hilbert` and `farah2023choice`). Cites three Blackadar works (*Operator Algebras*, *Real Analysis*, *Hilbert spaces*) that were not obtained; anything through those citations is tier (c) | 2026-08-16 | +| LAN98 | N. P. Landsman, *Lecture Notes on C\*-algebras, Hilbert C\*-modules and Quantum Mechanics*, arXiv math-ph/9807030 | retrieved | `references/arxiv-math-ph-9807030/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim; heavy custom macros (`\ca`, `\Hs`, `\rep`, `\om`, `\A`, and a `\renewcommand{\H}`). Inner product conjugate-linear in the **first** argument. Two typos to reproduce in quotes: `non-separable in the nom-topology` (for `norm-`), and Lemma `lots` writes `$\om(A)=a$` where the quantified state is `$\om_a$`. States Krein–Milman **without proof**. Its faithful-state GNS proposition is about the compact operators specifically, not a general C\*-algebra — do not cite it as the general statement. **Added 2026-08-16 (bicommutant run)**: it proves the double commutant theorem in full (Thm `DCT`, unital form) after a finite-dimensional warm-up (`DCT1`), and isolates the projection lemma (`DCTlemma`) that four of its proofs reuse; `DCT`'s general case is written as a *delta* on `DCT1` and is not self-contained without it. It dates the theorem to **1929**, twice. **Internal tension**: it reports von Neumann's original definition as closure meaning *sequential* completeness in WOT, while its own topology definition and `DCT` work with nets, and it never flags the switch. Three typos: the weak neighbourhood basis quantifies over the net member `A_λ` instead of the set variable `B` (as printed the set is all of `B(H)` or empty); `\M^n(\M')` and `\M_n(\M'')` spell one construction two ways inside a single display; and `DCT`'s proof writes `A ∈ \ovl{\M\Ps}` for an operator `A` where `AΨ` is meant | 2026-08-16 | +| SHI12 | V. Shirbisheh, *Lectures on C\*-algebras*, arXiv:1211.3404 | retrieved | `references/arxiv-1211.3404/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim; macros by `\def` not `\newcommand` (`\cs`, `\ss` at `raw/main.tex:65–66`). **Contains no GNS construction and no Gelfand–Naimark representation theorem** — it says so itself: `we presented GNS construction fully in the course, but it is not given in these notes.` Defines a separable Hilbert space **basis-theoretically** and sets the equivalence with topological separability as an exercise. Two defects: `cor:Hilbertcardinality`'s proof assumes only a surjection where a bijection is needed, and its corollary that every separable Hilbert space is unitarily ℓ²(ℕ) is false for finite-dimensional H under its own definition. Its equinumerosity proof makes an unflagged simultaneous choice. **Added 2026-08-16 (bicommutant run)**: it *does* contain the von Neumann bicommutant theorem, with a full proof following Pedersen, and is the **only** source in that corpus whose hypothesis is non-degeneracy rather than `1 ∈ M` — and the only one requiring `M` norm-closed. **Notation adjudication: its "strong-`\ss` operator topology" is the strong-⋆ topology** (seminorms `T ↦ ‖Tx‖ + ‖T*x‖`), *not* the σ-strong topology; matching it to KOS13's σ-strong by spelling is an error. Three defects in the material around the theorem: the proof declares `D : B(H) → B(H^n)` and then defines `D(T) := (Tx_1,…,Tx_n)`, a vector (the intended diagonal ampliation is in its Problem `e:5-22`, which itself states a hypothesis about `X` and a conclusion about `M`); the same proof concludes `there is some $T\in B(H)$` where the argument gives `T ∈ M`; and `prop:basiccommutant`(v)'s display has broken delimiters and missing moduli | 2026-08-16 | +| VER25 | R. Verch, *Lecture Notes on Operator Algebras and Quantum Field Theory*, arXiv:2507.00900 | retrieved | `references/arxiv-2507.00900/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim. Subsection 1.2 states the Haag–Kastler covariance axiom `α_L(𝒜(O)) = 𝒜(L(O))` (see the `LocalNet.Covariance` row in this repository's math-review ledger). **`separable`, `Gelfand`, `Naimark`, `Neumark` all occur 0 times** — but do not conclude it is irrelevant to C\*-algebra extractions: it is the only source in the 2026-08-16 corpus that *defines* faithful state and faithful representation, and the only one stating "the GNS representation of a faithful state is faithful" (asserted, no proof, no citation). Unital throughout; inner product conjugate-linear in the first argument | 2026-08-16 | +| VN30 | J. von Neumann, *Zur Algebra der Funktionaloperationen und Theorie der normalen Operatoren*, Math. Ann. 102 (1930) 370–427 | not retrieved | — | — | 2026-08-16 | The paper the bicommutant theorem is attributed to. Tried: GDZ `gdz.sub.uni-goettingen.de/id/PPN235181684_0102` (HTTP 200 but a JavaScript-only shell — no article list, no download link) and `…/mets/PPN235181684_0102` (returns the SPA HTML, not METS; `/api/mets/…` is 404); EUDML `doc/159384` (HTTP 403). Springer DOI 10.1007/BF01782352 is paywalled and was not pursued; no PDF was converted. **Date discrepancy, unadjudicated**: LAN98 and HIA20 both date the theorem to **1929**, while KOS13's key and SOR23's citation give the 1930 Math. Ann. volume — and those two cite bibliographically identical data, so only one paper is in play. Whether 1929 is a submission year against a 1930 issue cannot be settled without the original. Expires 2027-02-16 | 2026-08-16 | +| CON00 | J. B. Conway, *A Course in Operator Theory*, American Mathematical Society, 2000 | not retrieved | — | — | 2026-08-16 | SOR23 sends its only proof of the double commutant theorem to `section 21 of \cite{conway2000course}`. **Title adjudication**: this is commonly mis-cited as *A Course in Functional Analysis* (a different Conway book); SOR23's own bibliography entry reads *A course in operator theory*, 2000. No copy was sought beyond noting the pointer, so nothing may be claimed about what section 21 says. Expires 2027-02-16 | 2026-08-16 | +| HIA20 | F. Hiai, *Concise lectures on selected topics of von Neumann algebras*, arXiv:2004.02383 | retrieved | `references/arxiv-2004.02383/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim; macros by `\def` (`\cH`, `\<`, `\>`, `\Proj` at `raw/main.tex:32,42,43,62`). A survey: it **states the bicommutant theorem without proof and without a citation to a proof**, and likewise Kaplansky density. Two naming traps in one subsection: it calls the bicommutant theorem *von Neumann's density theorem* (nothing else in the 2026-08-16 corpus does, and it states Kaplansky density separately two paragraphs later), and it uses *commutant theorem* for the **tensor-product** result `(M₁⊗M₂)' = M₁'⊗M₂'`. Its definition says `closed in the weak topology` after introducing both WOT and the σ-weak topology two paragraphs earlier, so which one is meant is not written down. Declares von Neumann algebra ≡ `W*`-algebra, which KOS13 explicitly refuses. Dates the theorem to **1929**. Typo: `\<\xi\otimes\xi_2,\eta_1\otimes\eta_2\>` for `\xi_1` | 2026-08-16 | +| KOS13 | R. P. Kostecki, *W\*-algebras and noncommutative integration*, arXiv:1307.4818 | retrieved | `references/arxiv-1307.4818/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), byte-verbatim — **but its macros cannot be recovered**: `\usepackage{rpk}` and `\usepackage{motta}` at `raw/wstarint-arxiv.tex:35–36` are the author's private packages and are not in the arXiv package, so `\df \comm \N \C \BH \CC \II \zentr \limp` are undefined. A quote from KOS13 survives `grep -F`, but any *mathematical reading* of it is tier **(b)**, not (a). **WOT and SOT never occur anywhere in the document**: it states the double commutant theorem entirely in σ-topologies, and fixes them itself — `these topologies are usually called $\sigma$-strong and $\sigma$-strong-$\star$`. Keeps `W*`-algebra (Sakai predual) and von Neumann algebra as **distinct** notions. Its commutant is relative to an arbitrary ambient algebra. Three defects: it states the commutant's monotonicity **backwards** (`N₁ ⊆ N₂ ⟹ N₁' ⊆ N₂'`, false as printed), it drops the closure from Kreĭn–Milman one sentence after stating it correctly, and `source.txt:3779`/`3799` are a duplicated paragraph | 2026-08-16 | +| SOR23 | J. Sorce, *Notes on the type classification of von Neumann algebras*, arXiv:2302.01958 | retrieved | `references/arxiv-2302.01958/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim; macros at `raw/type_classification.tex:26,30,32,38`, `\ket`/`\bra` from the `physics` package. **Convention trap: its definition of "$*$-subalgebra" includes the identity operator.** Its statement of the double commutant theorem therefore looks false when read with the standard non-unital meaning (witnesses `{0}` and `ℂp` for a proper projection) and is true under its own definition — do not quote that statement without the definition. It also contradicts itself on the unit: the front matter requires it, `def:vN-algebra` omits it. `H` separable is a **standing** assumption declared in the front matter, and is unused by its double-commutant statement. Points its only proof at `section 21 of \cite{conway2000course}`, whose bibliography entry reads *A course in operator theory* (2000) — **not** *A Course in Functional Analysis*. Typo: its weak-convergence clause quantifies over `\ket{x}` only, omitting `\ket{y}` | 2026-08-16 | +| MUR90 / PED79 / DIX77 / TAK79 / BLA06 | Murphy, *C\*-algebras and Operator Theory* (1990); Pedersen, *C\*-algebras and their Automorphism Groups* (1979); Dixmier, *C\*-algebras* (1977); Takesaki, *Theory of Operator Algebras I* (1979); Blackadar, *Operator Algebras* (2006) | not retrieved | — | — | 2026-08-16 | The standard C\*-algebra textbooks. Only a web search was attempted; no legitimate free copy was found for any of them, and no pirated copy was pursued. **The widely-repeated attribution of the separable refinement of Gelfand–Naimark to "Murphy Thm 3.4.1" is unverified** — nobody in this repository has opened the book, so no note may carry that locator. Expires 2027-02-16 | 2026-08-16 | +| UME62 | H. Umegaki, *Conditional expectation in an operator algebra, IV (entropy and information)*, Kodai Math. Sem. Rep. **14** (1962) 59–85 | partial | `references/umegaki-1962-cond-exp-iv/` | published | 2026-08-16 | **Project Euclid is behind Incapsula**: the `.../....pdf` URL returns a bot-block HTML page, and J-STAGE has no `kmj1949` record for this article (404 on both `_pdf` forms). What works is the download endpoint with a browser User-Agent: `curl -L -A "Mozilla/5.0 …" "https://projecteuclid.org/journalArticle/Download?urlId=10.2996%2Fkmj%2F1138844604"` → 2.4 MB PDF. Converted with MinerU `pipeline` (27 pp., GPU), so `source.txt` is model output. **The PDF carries a real OCR text layer**, extracted independently with `pypdf` to `pdftextlayer.txt` (`=== PDFPAGE n ===` separators; journal page = PDF page + 58), which raises cross-checked rows to (b) `mineru-cross-checked-against-PDF-text-layer`. No page-*image* check is possible: `pdftoppm`, `pdftotext`, `mutool`, `gs` are all absent from this container (same limitation as dB74). The text layer's OCR is 1962-vintage and mangles letters (`b`→`δ`, `ρ`→`^`, `≺`→`-<`), so it is usable for prose and structure, **not** for formulas. **MinerU dropped all four footnotes**; they survive only in the text layer. Three adjudications: (i) `locator §1`: the standing hypothesis is `of finite class and sigma-finite` with a faithful normal **finite** trace, *and* the same paragraph descopes it — `this assumption is not necessarily essential`, all theorems holding for semifinite `A` and a semi-trace `by a little or simply modified proofs`, subject to an unverified side condition on subalgebras; so HIA18's "semifinite" attribution is a **defensible generalisation of a claim the source makes about itself**, not an over-attribution. (ii) `locator footnote 7)` (`pdftextlayer.txt:782`): reads condition (8.3) for sufficiency **is satisfied** for a semi-trace — an earlier reading of this line as "is *not necessarily* satisfied" was wrong. Of the four semi-trace footnotes only fn 4) carves anything out, and it concerns the operator-entropy of `L^p` elements. **Unverified against a page image; the OCR is the sole witness.** (iii) `locator §4, Definition 1`: the definition carries only `a ≺ b` (defined in §1 as `s(a) ≤ s(b)`) and `τ(a)=τ(b)=1` — **finite entropy is not in it**; the class `ℰ` is defined separately at the end of §3 and is a hypothesis of the theorems. The phrase **"relative entropy" occurs zero times** in all three extractions | 2026-08-16 | +| HIA18 | F. Hiai, *Quantum f-divergences in von Neumann algebras I. Standard f-divergences*, arXiv:1805.02050; J. Math. Phys. 59 (2018) 102202 | retrieved | `references/arxiv-1805.02050/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim; single source file, macros by `\def` at `raw/main.tex:24–47` (`\bR \bC \cH \BH \Tr \< \> \ffi \sa \fS`). **Citation-key trap: `\bibitem{LR}` is Lesniewski–Ruskai 1999** (*Monotone Riemannian metrics and relative entropy on noncommutative probability spaces*), **not Lieb–Ruskai 1973** — and it is the citation carrying (F-3.1), the integral representation that the whole of §3 rests on. Other keys check out: `Ar5` = Araki, Publ. RIMS 11 (1976) 809–833; `Ar2` = Araki, Publ. RIMS 13 (1977) 173–192; `Ko` = Kosaki, J. Operator Theory 16 (1986) 335–348; `Pe2` = Petz, CMP 114 (1988) 345–349; `Um` = UME62. Cites Araki, never quotes him, so no locator into Araki may be printed as checked. **Label collision**: the example following Theorem 3.5 is labelled `E-12` in the LaTeX and prints as Example 3.6 — the same object. Four transcription slips, none affecting a statement: Example 2.5 writes `\Delta_{\phi,\phi}` where the relative modular operator of a *pair* is meant; the proof of Lemma 3.1 writes `f` for `f_n`; Cor. 4.4(1)'s proof has `1-s(s)` for `1-x(s)` and a `\sigma` where `\rho` belongs; Cor. 4.4(3)'s proof cites (F-2.9) where (F-2.8) is meant; Thm 4.5's proof writes `\sigma(1-{\alpha_0})` for `\sigma(1-e_{\alpha_0})`. **It states no tensor-product additivity** — its Prop. 2.3(4) is direct-sum additivity, and merging the two is an error | 2026-08-16 | +| VED02 | V. Vedral, *The role of relative entropy in quantum information theory*, arXiv:quant-ph/0102094; Rev. Mod. Phys. 74 (2002) 197 | retrieved | `references/arxiv-quant-ph-0102094/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), verbatim. Natural log throughout, and it says why. **Its definition of the relative entropy has no support condition and no `+∞` branch**, and never says where the formula is undefined — so it is a partial function whose domain the source does not state. Its properties F1–F3 are explicitly "left to the reader", and F2 (partial-trace monotonicity, the Lieb–Ruskai content) is what its whole CPTP argument consumes. **Symbol collision: `S_N` denotes both the von Neumann relative entropy and, a page later, the `N`-copy measured relative entropy.** Wording slip: F3 is an equality but is called "This inequality". Citation-key note: HS17's `\cite{vedral}` is Vedral–**Plenio** 1998, a different work | 2026-08-16 | +| KW20 | S. Khatri, M. M. Wilde, *Principles of Quantum Communication Theory: A Modern Approach*, arXiv:2011.04672 | retrieved | `references/arxiv-2011.04672/` | arXiv | 2026-08-16 | arXiv LaTeX (rung 1), byte-verbatim, **4.3 million characters — grep and jump, never read linearly**. **Its macros cannot be recovered**: `raw/sc.tex:1` is `\documentclass{Book_KW}`, a private class absent from the arXiv package, so `\Tr`, `\supp`, `\ket`, `\bra`, `\braket` are undefined in the bytes we hold (same failure mode as KOS13's `\usepackage{rpk}`). A quote survives `grep -F`, but any *rendering* of it needs reconstructed definitions. **The flattener collapses whitespace runs**: a quote copied from `source.txt` that crosses a double space (e.g. `$\rho$ and positive semi-definite`) matches `source.txt` and **fails** `source.flat.txt` — shorten the span rather than editing the bytes. Finite dimensions are a standing assumption *by fiat*. Its second argument is only positive semi-definite, so **its Klein inequality is conditional on `Tr σ ≤ 1`** and is not the same statement as HIA18's (which needs `ρ(1)=σ(1)>0`) or HS17's (both states). It **proves** operator Jensen and Stinespring itself, and states the operator convexity of `x ↦ x log_b x` and of `x^β` on `[-1,0) ∪ [1,2]` as numbered facts, deferring only the proofs — so a note calling `t log t` operator convex an unsourced edge is wrong about this corpus | 2026-08-16 | +| ARA76 / ARA77 | H. Araki, *Relative entropy of states of von Neumann algebras*, Publ. RIMS **11** (1976) 809–833; and *… II*, Publ. RIMS **13** (1977) 173–192 | not retrieved | — | — | 2026-08-16 | The papers the general (type-III-capable) definition is attributed to. Tried for I: Project Euclid `journalArticle/Download?urlId=10.2977%2Fprims%2F1195191148` with a browser UA (returns HTML, not a PDF — unlike the Kodai article, where the same endpoint works); the `.full` article page (404 via WebFetch); `kurims.kyoto-u.ac.jp/~prims/pdf/11-3/11-3-{11..14}.pdf` (404); `ems.press/journals/prims/articles?volume=11` (404). No PDF was converted. **Citation-key trap that survives the miss: WIT18's `\cite{Araki2}` is Araki, *Inequalities in Von Neumann Algebras*, Strasbourg RCP25 22 (1975) 1–25 — NOT the 1977 Publ. RIMS part II.** So "Araki 1976/77, per WIT18" is wrong; attribute 1977 to HIA18 or HS17 only, whose keys were verified. Expires 2027-02-16 | 2026-08-16 | +| KOS86 / PET88 / OP93 / LR99 / HAA75 | Kosaki, *Relative entropy of states: a variational expression*, J. Operator Theory 16 (1986) 335–348; Petz, *A variational expression for the relative entropy*, CMP 114 (1988) 345–349; Ohya–Petz, *Quantum entropy and its use*, Springer 1993; Lesniewski–Ruskai, J. Math. Phys. 40 (1999) 5702–5724; Haagerup, *The standard form of von Neumann algebras*, Math. Scand. 37 (1975) 271–283 | not retrieved | — | — | 2026-08-16 | Not sought — the extraction ran entirely through HIA18's and HS17's reproductions of them. **Ohya–Petz is the load-bearing one**: HS17 rests its *entire* property list (h1)–(h7) on it with no theorem numbers at all, and two of those items (superadditivity in the first argument; the tensor chain rule) appear in no other source of the 2026-08-16 corpus. HIA18 reproduces Kosaki's Theorem 3.2 formula verbatim and says its own differs by the integration range `[1/n,+∞)` vs `[1/n,n]`; whether Kosaki states it on `M_*^+` or on states is **not** recoverable from that reproduction. Expires 2027-02-16 | 2026-08-16 | diff --git a/.claude/skills/math-review/SKILL.md b/.claude/skills/math-review/SKILL.md new file mode 100644 index 0000000..0f64d82 --- /dev/null +++ b/.claude/skills/math-review/SKILL.md @@ -0,0 +1,302 @@ +--- +name: math-review +description: Review the current Lean change — or an explicitly named file, directory, or commit range — from a mathematician's/physicist's viewpoint via parallel per-perspective `math-reviewer` sub-agents: statement fidelity, deferred hypotheses, abstraction/literature conformance, notation/naming/docs, and counterexample models/vacuity. +argument-hint: "[path | dir | main...HEAD]" +disable-model-invocation: true +--- + +Dispatch the math/physics review to `math-reviewer` agents, one per review +perspective. The review methodology (the perspective definitions, the +elaborated-type discipline, the per-agent output format) lives in +`.claude/agents/math-reviewer.md` — never duplicate it here. + +## Process + +### 1. Pin the target + +Take the first non-empty option: + +1. An explicit argument — a file, a directory (expand with Glob), or a + commit range. +2. The union of the local change and the branch change, so that a stray + working-tree edit cannot shadow committed branch work: + - `git diff --name-only --diff-filter=ACMR HEAD -- '*.lean'` (working tree + and index), + - `git ls-files --others --exclude-standard -- '*.lean'` (untracked new + files — `git diff` never lists these), + - `git diff --name-only --diff-filter=ACMR ...HEAD -- '*.lean'`, + where `` is `main`, falling back to `origin/main`, then + `origin/HEAD`; if none resolves, drop this component rather than failing. + +Filter to `.lean` files *before* testing an option for emptiness — reviewing +"no issues" over an empty scope is the worst outcome this skill has. Deleted +files never appear under `--diff-filter=ACMR`; enumerate them separately with +`--diff-filter=D` over the same bases and list them as unreviewed — they have +no elaborated type to inspect. If every option is empty, ask the user what to +review. + +For diff-derived targets, also collect the changed line ranges per file with +`git diff -U0 -- ` — step 2 hands them to the reviewers. An +explicit file or directory target has no diff; its scope is the entire file. + +Announce the resolved target before dispatching, so a wrong default costs one +word to correct. + +**Then resolve the extraction notes.** `docs/math/.md` records what the +literature says about an object, written before its Lean by `/math-extract`; +`references/note-format.md` in that skill states that checking the declaration +still matches the note is *this* skill's job. Read `docs/math/README.md` and +match its index rows to the target: a row's `Implemented as` column names the +declaration, and each note's `implemented-as:` frontmatter field carries the +same back-link. When neither names a target declaration, match by object — +a note on the split inclusion covers `SplitInclusion.lean` whether or not the +back-link was ever written. Collect the absolute paths of the notes that match. + +No note is a normal state, not a violation: most objects here predate the +skill. Missing notes are recorded in `## Not reviewed` (step 4) and nothing +else follows from them — never file a finding for the absence of a note, and +never write one yourself. `/math-extract` owns that file. + +### 2. Spawn one sub-agent per perspective + +**Scale to the target first.** When the scope is small — roughly three target +declarations or fewer, or a small diff in a single file — do not fan out: +launch a **single** `math-reviewer` with no perspective assigned (its agent +file then has it cover all five perspectives itself), and continue with +steps 3 and 4 unchanged. The fan-out below is for real diffs. + +Read `ledger.md` (next to this file) first — you have to hand each reviewer its +own rows. + +Launch five `math-reviewer` agents (Agent tool, +`subagent_type: math-reviewer`) — one per perspective defined in the agent +file: + +1. Statement fidelity +2. Deferred hypotheses +3. Abstraction & literature conformance +4. Notation, naming & documentation +5. Counterexample models & vacuity + +all in a single message. Every agent receives the **same scope**: the full +list of target files with their changed line ranges (the agent expands them to +declarations itself). Write each prompt as a research memo, not a task ticket — +it carries the target, the rows already ruled out, your own guess, and explicit +permission to prove that guess wrong. Each prompt must state: + +- the assigned perspective by number and name, framed as a role: "You are one + of five perspective-specific reviewers running in parallel over this change; + review it under perspective N () only" — do not restate the + perspective's definition, the agent file owns it; +- the target files, with the changed line ranges collected in step 1 — or + *entire file* for an explicit target that has no diff; +- **the absolute paths of the extraction notes resolved in step 1**, or the + explicit statement that none matched. Do not summarise a note into the + prompt: the agent file tells each perspective which section answers its own + question, and a summary would decide that for it. Say only which object each + note covers. When no note matched, say so — silence reads as "nobody + looked", and a reviewer that assumes a note exists will hunt for it and + spend the sweep on filesystem searches; +- a reminder to investigate related code beyond the diff, and to sweep the + whole target rather than stopping at the first finding; +- **the absolute path of the notes file** it must append confirmed findings to + as it goes: `/math-review/perspective-.md`. Pass *your* + scratchpad path, not the agent's — a file written where you cannot read it is + no use when the agent dies; +- **the ledger rows for that perspective**, verbatim, as a *do-not-re-file* + list — together with the expiry rule `ledger.md` defines: a row whose + `Depends on` declaration has changed since the row's `Commit` is void, and + the claim may be filed again; +- **your forecast**: which declaration, and which part of it, you expect this + perspective to catch something in, and why. Require the reviewer to **write + its own prediction down before opening the LSP** and to report the divergence + afterwards. A hit saves a sweep; a miss is itself information, and the written + prediction is what exposes anchoring; +- **a control case to run** — for perspectives 1, 2, 3, and 5 only: "name one + model in which this claim ought to be false, and measure it there". + Perspective 5 owns this by construction; for 1–3 it is a cheap sanity check + on their own reasoning. Perspective 4 gets none — there is no model in which + a docstring is false; +- **explicit permission to contradict this brief**. "Every branch of the + forecast was wrong" and "the assigned lens is the wrong one for this change" + are findings to report, not failures to apologise for. Say so in the prompt — + a reviewer that believes it must confirm the brief will find a way to. + +Do not re-implement the review yourself. If the platform refuses to launch all +five at once, start as many as it allows, wait for one to complete, and +immediately launch the next — every perspective must run before aggregation. + +**If an agent does not return**, do not simply drop its perspective: read the +notes file you assigned it, which it appends to as it works. Findings that +reached disk are usable even when the agent died mid-sweep; mark that perspective +as partial and record in `## Not reviewed` how far it got. + +### 3. Refute the unverified claims + +Run this once every perspective has either completed or been declared +dead/partial with its notes file recovered (step 2). Collect the findings at +evidence tier **(c)** — recalled, not verified — including any recovered from +a dead reviewer's notes. If there are none, skip this step. + +Launch one further `math-reviewer` agent in the refutation role, whose only +job is to attack them. Do not pass it the (a)- and (b)-tier findings — those +are grounded by definition. It returns one of three outcomes per finding, each +with a fixed disposition: + +- **refuted** — drop the finding from the report (or downgrade it, when part + of the claim survives), and write one ledger row; +- **survives, promoted to (a)/(b)** — report it at its new tier; no ledger row; +- **survives at (c)** — report it marked `unverified`, capped at should-fix + (no blocker rests on (c) alone); no ledger row — it was neither refuted nor + grounded, and the ledger records refutations only. + +One adversarial pass, not a vote: the evidence tiers already carry the +reviewer's own confidence, so each extra vote buys nothing but another round of +LSP round trips. + +**Then update `ledger.md`.** Every claim the pass **refuted** gets one row, in +the format that file defines: target by declaration name (never a line +number), perspective, the claim, the refuting evidence, the declarations the +refutation depends on, the commit (`git rev-parse --short HEAD`), and the +date. One row per claim, not per finding. While you are in the file, delete +rows that are void under its expiry rule and rows whose claim was re-filed and +re-adjudicated this run — the new adjudication replaces the old row. This is +the only step that writes to the ledger, and skipping it is what makes the +next run repeat this one's dead ends. + +### 4. Aggregate + +Wait for every perspective (and the refutation pass, if any) to complete. + +**First, reconcile each extraction note's back-link.** For every note resolved +in step 1, check the two facts that go stale on their own: whether +`implemented-as:` names a declaration that still exists (`lean_local_search`, +or `lean_declaration_file`), and whether the `docs/math/README.md` index row +agrees with it. Then: + +- **Back-link absent or stale, and a target declaration implements the note's + object** — write the fully-qualified declaration name into the note's + `implemented-as:` field and into the README index row. These two fields are + the *only* thing this skill may write in `docs/math/`; everything else there + belongs to `/math-extract`, and a review that edits a `## Hypotheses` row has + overwritten an extraction it did not perform. +- **Back-link names a declaration that no longer exists** — set it back to + `none` and say so in the report. A back-link pointing at a deleted + declaration is worse than none: it claims a formalization that is gone. +- **The declaration exists but does not state the note's adopted general + form** — do **not** touch the back-link. That is a perspective 3 finding, and + it is the finding this whole reconciliation exists to surface; silently + rewriting the link would record agreement where there is a divergence. + +Skip this entirely when step 1 found no notes. + +Then emit one markdown report to the chat, grouped by severity. Do not soften, +merge, or drop findings; findings from different perspectives on the same line +stay separate entries. The one exception: the reviewers' `## Out of +perspective` sections (defects fitting no perspective — layout violations, +Mathlib duplicates) may name the same defect several times; de-duplicate those +and file each once, under the severity it deserves. + +**Verify every blocker yourself before printing it.** Open the declaration and +confirm the claim with `lean_goal` / `lean_term_goal` from your own seat — so +that you are not merely relaying. A blocker you could not confirm drops to +should-fix and is marked `aggregator-unverified`; it is not deleted, because +failing to confirm is not refuting. This is the one place the skill spends your +own LSP calls rather than an agent's, and it is worth it: a false blocker costs +the reader more than a missed nit. + +**Attribute claims to whoever made them.** Write "the perspective 2 reviewer +reports X; I confirmed the elaborated type at Y" — not "X is a bug". You are +reporting what the review produced and how far you checked it, and those are two +different statements. Keep them separate in the prose. + +Template: + +```markdown +# Math Review — + +**Scope**: files / declarations +**Verdict**: +**Extraction notes**: + +## 🛑 Blocker +### 1. +- `file:line` `declName` — [/] +- **Problem**: … +- **Fix**: … +- **Verified**: + +## ⚠️ Should-fix +…same shape… + +## 💡 Nit +…same shape… + +## Ranked risks + + +## What this change does not claim + + +## Per-perspective summary +| Perspective | blocker | should-fix | nit | forecast | verdict | +|---|---|---|---|---|---| + + +## Refuted findings + + +## Not reviewed + +``` + +Omit an empty severity section, never an empty summary row, and never +`## Not reviewed`. + +## Why perspectives, not files + +A diff hunk is not a reviewable unit in Lean: a statement's elaborated type is +fixed by binders, `variable` blocks, and `open scoped` lines that sit outside +the hunk, and one hunk routinely spans several declarations. That expansion +belongs to the `math-reviewer` agent; this skill only resolves *which files* +enter the review. + +Nor is a file the unit of judgement: deferred hypotheses are discharged — or +silently left standing — across files, and abstraction drift shows up only +when the definition and its uses are read together. One reviewer per +perspective sees the whole change at once, and caps the fan-out at five agents. +Four of them lean on the shared search tools, so the `lean_loogle` rate limit +(3 requests / 30s) is a genuinely scarce shared budget — each reviewer's agent +file has it prefer `lean_local_search` and spend remote searches per sweep, +not per declaration. Perspective 5 works almost entirely through +`lean_run_code` and `lean_multi_attempt` against the local toolchain, so adding +it costs concurrency but not remote quota. + +## Ledger + +`ledger.md`, next to this file, is the skill's memory across runs. Without it +every run re-derives the same dead ends: a reviewer files a plausible claim, the +refutation pass kills it, the report ships, and the next run files it again. + +Step 3 writes it — one row per claim the refutation pass refuted — and is also +the step that deletes rows voided by expiry or replaced by a re-adjudication. +Step 2 reads it and hands each reviewer its own rows as a do-not-re-file list. +Nothing else touches it. + +The rows are keyed by **declaration name**, because line numbers move. Each +carries the declarations its refutation depends on, because a ledger that +silences a true finding is worse than no ledger: when a `Depends on` declaration +changes relative to the row's `Commit`, the row expires and the claim is open +again. Reviewers are told this rule explicitly, and a reviewer that doubts a row +should re-file rather than stay quiet. diff --git a/.claude/skills/math-review/ledger.md b/.claude/skills/math-review/ledger.md new file mode 100644 index 0000000..e729e24 --- /dev/null +++ b/.claude/skills/math-review/ledger.md @@ -0,0 +1,65 @@ +# Refuted-findings ledger + +A do-not-repeat list for `math-review`. Step 3 (the refutation pass) appends to +it; step 2 hands each perspective reviewer its own rows so the same claim is not +re-filed run after run. + +## What goes in + +**One row per *claim*, not per finding.** The same defect reported twice under +different wording is one claim. Reference the target by **declaration name** — +never by line number, which moves with the next edit. + +Only claims the refutation pass **refuted** are recorded. A finding that +survived refutation — whether promoted to (a)/(b) or still at tier (c) — +belongs in the report, not here. + +## Expiry — read this before trusting a row + +Every refutation rests on something: an elaborated type, an instance, a +docstring. The `Depends on` column names it; the `Commit` column records when +it was measured. **When a declaration listed under `Depends on` has changed +since `Commit`, the row is void and the claim may be filed again.** The check +is mechanical, not a judgement call: + +1. `git diff ..HEAD -- ` for each file housing a `Depends on` + declaration; untouched files mean the row stands. +2. If a file changed, re-fetch the declaration's current elaborated type and + compare it against the evidence quoted in `Refutation`. +3. A `Depends on` declaration that no longer resolves — renamed or deleted — + voids the row unconditionally. + +A stale ledger suppressing a true finding is the one real danger of this file. +When in doubt about whether a row still holds, re-file rather than stay +silent. Rows are never deleted for being old — only by step 3 of the skill, +when they are void under this rule or when the claim is re-filed and +re-adjudicated (the new adjudication replaces the old row). + +## Format + +| Target | P | Claim | Refutation | Depends on | Commit | Date | +|---|---|---|---|---|---|---| + +- **Target** — `path/to/File.lean:declarationName` (no line numbers) +- **P** — perspective 1–5 that filed the claim +- **Claim** — what was asserted to be wrong, in one line +- **Refutation** — the concrete evidence, quotable: an elaborated type or a + fully-qualified declaration name +- **Depends on** — the declaration(s) the refutation rests on; the expiry trigger +- **Commit** — `git rev-parse --short HEAD` when the row was appended; the + baseline the expiry check diffs against +- **Date** — `YYYY-MM-DD` the row was appended + +## Entries + +| Target | P | Claim | Refutation | Depends on | Commit | Date | +|---|---|---|---|---|---|---| + +| `QuantumSystem/Algebra/LocalNet/SplitProperty.lean:ProperContainment` | 3 | The causal collar field `exists_orthogonal_of_properlyContained` is a repository invention carried by no literature definition of the split property — **re-adjudicated 2026-08-15 against the strengthened field, which now also demands `¬ O₃ ≤ O₁`**; this row replaces the `4d8a21b` row, voided by expiry when the field changed | KOE03 defines its own `⋐` by "its *causal complement* `I' := S¹ ∖ Ī` is not the empty set" (`references/arxiv-math-ph-0308031/source.txt:925`); with `I₂` open and `Ī₁` closed, `Ī₁ ⊂ I₂` forces a nonempty connected component `J` of `I₂ ∖ Ī₁`, which is a proper interval with `J ≤ I₂` and `I₁ ⟂ J` — the collar, derived from the source's own separation condition. The added `¬ O₃ ≤ O₁` clause is carried by the same derivation one step further: `J` is nonempty and disjoint from `I₁`, so `¬ J ≤ I₁` is free. dB74 states the thickening shape `O + N ⊂ Ô` that `ofThicken` implements (`references/buchholz-1974-product-states/source.txt:15`), and `ofThicken` proves the clause from strictness of `thicken Λ₁ ⊂ Λ₂` | `ProperContainment`, `ProperContainment.ofThicken` | 4ec09ec (field measured in the **working tree**, not yet committed — re-check on the commit that lands it) | 2026-08-15 | +| `QuantumSystem/Algebra/LocalNet/Net.lean:LocalNet` (also `LocalNet.Faithful`, the `Finset` `CausalOrthogonality` instance, `QuasiLocalAlgebra.lean` module doc) | 4 | The docstring citation "Naaijkens 2012" is a mis-dated citation of NAA13 (arXiv 1311.2717, 2013) with section numbers that match neither work | "Naaijkens 2012" is a different work: P. Naaijkens, *Anyons in Infinite Quantum Systems: QFT in d=2+1 and the Toric Code*, PhD dissertation, Radboud Universiteit Nijmegen, 2012 (handle `2066/92737`). The repo expands the cite itself (`QuantumSystem/Algebra/CStarAlgebra/Representation/Conjugation.lean:45`: "Naaijkens, *Anyons in Infinite Quantum Systems*, 2012, §3.2"), and all cited sections match the thesis TOC (§1.3 "Inductive limits" — quasi-local algebra; §3.2 "Algebraic quantum field theory" — covariance axiom; §3.4 "Quantum lattice systems" — lattice net over 𝒫_f(L)); against NAA13 the same section numbers are nonsense (§1.3 = "Topics not covered"). The note-locator comparison (NAA13 §1.2/§2.4) was made against the wrong work. Residual: a disambiguation nit (expand the bare short cite), not a drift | `LocalNet`, `LocalNet.Faithful` (docstrings); the full cite at `QuantumSystem/Algebra/CStarAlgebra/Representation/Conjugation.lean` | 4ec09ec (working tree) | 2026-08-15 | +| `QuantumSystem/Algebra/LocalNet/Covariance.lean:LocalNet.Covariance` (module doc) | 4 | "Verch 2025 §1.2" appears in no extraction note's source table and could not be verified — suspected phantom citation | Resolves to R. Verch, *Lecture Notes on Operator Algebras and Quantum Field Theory*, arXiv:2507.00900 (2025-07-01); its subsection 1.2 ("Algebraic QFT on Minkowski Spacetime: Haag-Kastler Nets of Local Algebras") states exactly the cited covariance axiom `α_L(𝒜(O)) = 𝒜(L(O))` (source `York-Notes2025.tex` lines 351–353). The extraction notes index only their own corpora, so absence from their source tables is not drift | `LocalNet.Covariance` (module doc) | 4ec09ec (working tree) | 2026-08-15 | +| `QuantumSystem/Algebra/LocalNet/Examples.lean:zeroHom` | 1 | The `zeroHom`/`zeroRep` docstring ("this still exhibits the property rather than trivialising it away") contradicts the repo's own statements that the zero representation trivialises the split property | "Trivialising it away" means annihilation on the zero *space* (where `IsSplitInclusion` is identically false), not trivial satisfaction: the clause "`𝓡(O)` contains `1` regardless, since a von Neumann algebra is unital" is shared verbatim with `SplitProperty.lean`'s `VonNeumannNet.SplitProperty` module prose, whose next sentence states the nonzero-space/zero-space dichotomy the docstring restates; and `Examples.lean`'s own module header concedes the triviality at length ("stated so it is not mistaken for evidence"). The mathematical residue (collapse to `ℂ1` for *any* representation on `ℂ`, sole witness) survives — only the contradiction reading was refuted | `zeroHom` (docstring), `VonNeumannNet.SplitProperty` (module prose), `Examples.lean` module header | 351f5dd (working tree) | 2026-08-15 | +| `QuantumSystem/Algebra/LocalNet/SplitProperty.lean:VonNeumannNet.SplitProperty.isSplitInclusion_commutant` (and `LocalNet.SplitProperty.isSplitInclusion_commutant`) | 4 | The two `isSplitInclusion_commutant` docstrings contradict each other | The two theorems have the same shape (`O₁ ⋐ O₂`, `O₂ ⟂ O_B` ⊢ `IsSplitInclusion 𝓡(O₁) 𝓡(O_B)′`), the second is a one-line specialisation of the first, and the second docstring's summary of the first (Haag duality for the converse, Buchholz's four-term chain) is accurate; no proposition on which they disagree exists in the current tree. Confirmed by the aggregator reading both docstrings in source | `VonNeumannNet.SplitProperty.isSplitInclusion_commutant`, `LocalNet.SplitProperty.isSplitInclusion_commutant` (both docstrings) | 351f5dd (working tree) | 2026-08-15 | +| `QuantumSystem/Algebra/CStarAlgebra/GelfandNaimark.lean:gelfand_naimark_theorem` | 1/3 | The conclusion omits **nondegeneracy** of the representation, which the standard universal-representation statement of Gelfand–Naimark carries — a scope gap in the formalized statement (filed together with a separability half, which is *not* refuted and survives at (c)) | The two existentials are **equivalent**, so nondegeneracy cannot strengthen the conclusion: given isometric `φ : A →⋆ₙₐ[ℂ] 𝓑(H)`, set `K := closure (span (φ(A) H))`; `φ(A)` is `*`-closed so `Kᗮ = {ξ | ∀ a, φ a ξ = 0}`, hence `‖φ a‖ = ‖(φ a)|_K‖` and the corestriction `A → 𝓑(K)` is an isometric `*`-hom that is nondegenerate by construction. Independently, the witness actually used *is* nondegenerate: `GNS.Representation.cyclic : Dense ↑(Submodule.span ℂ {x | ∃ a, self.π a self.ξ = x})` is a field of the GNS triplet, cyclic ⟹ nondegenerate, and an ℓ²-direct sum of nondegenerate representations is nondegenerate. Note the naive route fails and was *not* used: nondegeneracy is not automatic from `Isometry` for a fixed `φ` (`a ↦ φ a ⊕ 0` on `H ⊕ ℂ` is isometric and degenerate) | `GNS.Representation.cyclic`, `GNS.DirectSum.Hilbert`, `GNS.DirectSum.directSumAlgHom`, `GNS.DirectSum.directSumAlgHom_isometry` | 2e21b4b | 2026-08-15 | +| `QuantumSystem/Algebra/VonNeumannAlgebra/StructureTheorem.lean:IsFactor.exists_spatial_tensor_decomposition` (module docs of `StructureTheorem.lean` and `TensorFactor.lean` also cite it) | 4 | Citation "J. Yngvason, arXiv:1401.2652, §5.1, eqs. (38)/(39)" is unverifiable — no `references/` cache exists for it, unlike every other of the nine sources cited in this file set, and neither resolved extraction note lists it in its `## Sources` table | Fetched arXiv:1401.2652 directly (PDF → `pdftotext`): confirmed author J. Yngvason, confirmed §5.1 titled "Causal Independence and Split Property", confirmed eqs. (38)/(39) verbatim-match the docstrings' `𝒜₁ ⊂ 𝒩 ⊂ 𝒜₂′` / tensor-decomposition content, down to which symbol plays which role. The "no cache ⇒ suspicious" inference does not hold — it is a caching-completeness gap (8/9 sources cached), not a correctness signal | `StructureTheorem.lean` module doc + `IsFactor.exists_spatial_tensor_decomposition` docstring, `TensorFactor.lean` module doc | 351f5dd (working tree) | 2026-08-15 | +| `QuantumSystem/Algebra/CStarAlgebra/GelfandNaimark.lean` (module doc) | 4 | The four textbook locators — Murphy *C\*-algebras and Operator Theory* Thm 3.4.1, Pedersen *C\*-Algebras and Their Automorphism Groups* §3.7, Blackadar *Operator Algebras* II.6.4, Takesaki *Theory of Operator Algebras I* I.9.18 — are unverifiable/phantom citations, since the repo's own same-day extraction note `docs/math/separable-faithful-representation.md` records all five books (plus Dixmier) as **not retrieved**, with no `references/` cache and its own `## Not investigated` calling the attribution "unverified" | Independently-fetched bibliographic data (OpenLibrary + a 2007 Library-of-Congress TOC snapshot via the Wayback Machine, for Murphy; Crossref chapter listings for Pedersen/Blackadar/Takesaki) confirms each cited chapter is *exactly* the chapter where the noncommutative Gelfand–Naimark theorem belongs in that book's published structure — Murphy ch. 3 "Ideals and Positive Functionals"; Pedersen ch. 3 "Functionals and Representations"; Blackadar ch. II "C\*-Algebras"; Takesaki ch. I "Fundamentals of Banach Algebras and C\*-Algebras" — not a mismatch to an unrelated topic. This refutes the "phantom citation" reading (chapter-level, tier b). The exact section/theorem digit within each chapter (3.4.1 / §3.7 / II.6.4 / I.9.18) could not be independently pinned down by any source reachable in this sandbox and remains open at tier (c) — reported separately, not as a blocker | module doc of `GelfandNaimark.lean` (no Lean declaration dependency; refutation rests on external bibliographic sources, not on repo code) | a2fa4f5 | 2026-08-16 | diff --git a/.claude/skills/paper-notation-refactor/SKILL.md b/.claude/skills/paper-notation-refactor/SKILL.md deleted file mode 100644 index 26b15ab..0000000 --- a/.claude/skills/paper-notation-refactor/SKILL.md +++ /dev/null @@ -1,324 +0,0 @@ ---- -name: paper-notation-refactor -description: Rewrite Lean 4 code so a `.lean` file looks like the physics / operator-algebra paper it formalises. Two jobs. (1) Swap long-form Mathlib calls for Unicode / mixfix notation — `inner ℂ x y` → `⟪x, y⟫_ℂ`, `Finset.sum Finset.univ` → `∑`, `norm x` → `‖x‖`, `ContinuousLinearMap.comp` / `.comp` → `∘L`, `ContinuousLinearMap.adjoint f` / `f.adjoint` → `f†`, `Complex.I` → `I`, `Real.pi` → `π`, `star a` → `a⋆`. (2) Introduce brand-new `scoped notation` (plus companion `Coe` / `CoeFun` / `FunLike` when a bundled subtype hides a type gap) for domain concepts whose paper symbol has no Mathlib spelling — quantum relative entropy `D(ρ ‖ σ)`, GNS representation `π_ω`, cyclic vector `Ω_ω`, modular operator `Δ_ω`, modular conjugation `J_ω`, KMS state `ω_β`, partial trace `Tr_B`, fidelity `F(ρ, σ)`, local algebra `𝔄(Λ)`, field net `𝔉(𝒪)`. Trigger whenever a user asks for "paper style", "paper notation", "publication style", "textbook style", "論文と同じ見た目", "paper styleにしたい", "教科書っぽく", "読みやすく", or asks to "rewrite / convert / replace / swap / substitute" a long-form call with a Unicode / mixfix symbol — including implicit requests like "clean up this lemma" or "make this nicer" when the target file visibly contains the patterns above. Trigger also for "add scoped notation for X" / "introduce notation `X`" / "X に notation 付けて" / "X に記法を付与 / 付けて / 抽出して" / "use Araki / Nielsen–Chuang / Bratteli–Robinson / Takesaki / Haag / Naaijkens notation" / "references// から記法を取り出す" / "so statements read like the paper". This skill EMPIRICALLY under-triggers — if any of these signals are present, INVOKE IT even when the user phrases the request unusually. Do NOT trigger for proof / tactic refactors (use `lean4:refactor` or `lean4:golf`), for removing unused hypotheses (use `derivable-hypothesis-remover`), for renaming identifiers, for introducing new `def` / `theorem` / `structure`, or for fixing build errors unrelated to notation. ---- - -# paper-notation-refactor - -Rewrite Lean 4 code so a human reading the file sees the same symbols a -physics / operator-algebra paper uses — `⟪x, y⟫_ℂ` instead of -`inner ℂ x y`, `D(ρ ‖ σ)` instead of `quantumRelativeEntropy ρ σ`, `∑ i, f i` -instead of `Finset.sum Finset.univ fun i => f i`. - -## When to invoke - -- The user says "paper style", "textbook style", "publication style", - "make this look like the paper", "rewrite notation", "add notation for - X". -- The user points at a `.lean` file and asks to "clean up the notation", - "make it read more like the paper", or otherwise improve - mathematical surface syntax, and that file has long-form `inner _ _`, - `norm _`, `Finset.sum Finset.univ _`, or domain-specific long-form - calls that have a textbook rendering. -- The user wants to introduce a new symbol (`D(ρ ‖ σ)`, `π_ω`, `Ω_ω`, - `Δ_ω`, `Tr_B`) for an existing Lean definition. - -## When NOT to invoke - -- The user wants to restructure a proof, change tactics, extract a - helper lemma, or improve a proof strategy. Use `lean4:refactor` or - `lean4:golf`. -- The user wants to remove unused hypotheses or weaken typeclass - assumptions. Use `derivable-hypothesis-remover` (sibling skill). -- The user wants to introduce a new mathematical *definition*. This skill - only adds `notation`, `scoped notation`, `syntax` + `macro_rules`, and - narrowly-scoped coercion or callable support (`Coe`, `CoeFun`, minimal - `FunLike`) — never fresh `def`s, `theorem`s, or `structure`s. - -## Modes of operation - -### Mode A — apply existing Mathlib notation - -For each pattern below, the skill grep-scans the target file, confirms the -required scope, and substitutes. Each rewrite is followed by -`lean_diagnostic_messages` on the file; if red, the skill reverts that one -rewrite and continues. - -| long form | paper form | scope to open | -| -------------------------------------------------------- | --------------- | --------------------------------------------- | -| `inner ℂ x y` | `⟪x, y⟫_ℂ` | `open scoped InnerProductSpace` | -| `inner ℝ x y` | `⟪x, y⟫_ℝ` | `open scoped InnerProductSpace` | -| `inner _ x y` (no subscript, 𝕜 inferred) | `⟪x, y⟫` | `open scoped RealInnerProductSpace` or `ComplexInnerProductSpace` depending on 𝕜 | -| `Finset.sum Finset.univ (fun i => f i)` | `∑ i, f i` | `open BigOperators` (usually already implicit) | -| `Finset.prod Finset.univ (fun i => f i)` | `∏ i, f i` | `open BigOperators` | -| `norm x` *(function-call form)* | `‖x‖` | none — `‖·‖` is global | -| `Complex.I` | `I` | `open Complex` / `open scoped Complex` | -| `Real.pi` | `π` | `open Real` / `open scoped Real` | -| `ContinuousLinearMap.comp f g` | `f ∘L g` | none — `∘L` is the canonical infix form | -| `f.comp g` (ContinuousLinearMap dot-form) | `f ∘L g` | **default-apply**: dot-form is idiomatic Lean but the paper form is `∘L` in operator-algebra contexts | -| `ContinuousLinearMap.adjoint f` / `f.adjoint` | `f†` | `open scoped Adjoint` (where exported by Mathlib version) | -| `star a` | `a⋆` | `open scoped ComplexConjugate` (for ℂ) | - -Scope note: Mathlib puts the **subscripted** inner-product bracket -`⟪x, y⟫_𝕜` in the generic `InnerProductSpace` scope -(`Mathlib/Analysis/InnerProductSpace/Defs.lean ~:86`). The field-specific -scopes `RealInnerProductSpace` and `ComplexInnerProductSpace` only -provide the **bare** `⟪x, y⟫` with the field inferred. Choose the scope -based on the paper form you want — if `⟪x, y⟫_ℂ` is the target, open -`InnerProductSpace`; if `⟪x, y⟫` (no subscript, field inferred) is -acceptable, open `ComplexInnerProductSpace` instead. - -See `references/notation-map.md` for the exhaustive table, precedence -quirks, and cases where the scope would re-export too much and should be -kept local. - -### Mode B — reference-paper-driven domain notation - -When a declaration is a named object in the literature but has no -Mathlib notation (e.g. `quantumRelativeEntropy`, `gnsRepresentation`, -`modularOperator`), the skill does **not** invent a rendering from its -own knowledge. Instead, it consumes the project's ingested paper -knowledge base under `/references//` — the output of -`pdf-to-knowledge` / `web-to-knowledge` — and extracts the paper's own -symbol. The paper is the source of truth. - -Workflow: - -1. **Identify the concept.** The user names the declaration directly - ("add paper notation for `quantumRelativeEntropy`"), or gives a - file and the skill enumerates likely domain concepts (`noncomputable - def` whose docstring cites a paper or names a textbook symbol). - -2. **Locate the paper.** In priority order: - a. The declaration's docstring already cites one (e.g. "See Araki - 1976" or "from Nielsen–Chuang §11.4"). - b. The user names one in the invocation. - c. Fall back: list `/references/*/` and ask the user which to - search. - -3. **Open the paper under `references//` and extract the - canonical symbol.** Sources in priority order: - a. `INDEX.md` → "Key concepts" section — often pins the exact - symbol the paper uses. - b. `content.md` (for short papers) or `sections/*.md` (long ones) — - grep for the concept's English name(s) and capture the nearest - LaTeX formula (`$...$` or `$$...$$`). - c. Handle spelling variants explicitly — a paper may use - `D(\rho \| \sigma)`, `D(\rho \parallel \sigma)`, `S(\rho | \sigma)`, - or `D(\rho || \sigma)`. Use **the spelling the cited paper uses**; - never silently normalise across papers. - -4. **Translate LaTeX to Unicode.** Common mappings: `\rho → ρ`, - `\sigma → σ`, `\omega → ω`, `\Delta → Δ`, `\pi → π`, `\Omega → Ω`, - `\| → ‖`, `\parallel → ‖`, `\otimes → ⊗`, `\oplus → ⊕`. The full - table lives in `references/notation-map.md`. - -5. **Design matching Lean notation.** Same rules as before: - - Colocate with the definition; use a namespace that matches the - concept. - - Default to `scoped notation` inside that namespace so importers opt - in via `open scoped …`. Use global `notation` only when the symbol - is universally unambiguous (rare in physics). - - Prefer `scoped notation` for parser-stable bracketed forms such as - `D(ρ ‖ σ)`, `S(ρ)`, or `F(ρ, σ)`. These usually round-trip cleanly - once every placeholder that sits next to an operator-like separator - is marked `:max`. - - **Set placeholder precedence to `:max`** whenever a separator - token inside the notation is also a Lean operator (`‖`, `|`, `⟨`, - `⟩`, `*`, `^`, …). Without `:max` the term parser keeps reading - past the separator looking for a matching close-bracket. - - ```lean - -- Correct — placeholders restricted to max-precedence terms - scoped notation "D(" ρ:max " ‖ " σ:max ")" => quantumRelativeEntropy ρ σ - ``` - - Naive (without `:max`) fails with - `unexpected token ')'; expected '‖', '‖₊' or '‖ₑ'` because `‖ σ ")"` - is parsed as the inside of `‖·‖`. - - - **Lexer-sensitive or unexpander-sensitive forms** (e.g. - `⟪_ | _⟫_ω`, `π_ω`, `Ω_ω`, `Tr_B`, or any bracketed form that still - fails `#guard_msgs`) go through `syntax` + `macro_rules`. - Remember to also provide an `app_unexpander` so the pretty-printer - can round-trip: - - ```lean - syntax:100 "D(" term:max " ‖ " term:max ")" : term - macro_rules | `(D( $ρ ‖ $σ )) => `(quantumRelativeEntropy $ρ $σ) - @[app_unexpander quantumRelativeEntropy] - meta def quantumRelativeEntropy.unexpander : Lean.PrettyPrinter.Unexpander - | `($_ $ρ $σ) => `(D( $ρ ‖ $σ )) - | _ => throw () - ``` - - Note: `meta def` (not plain `def`) may be required if the file is - under `module` / `@[expose] public section`. - - - **Check for collisions** via `lean_loogle` / `lean_leansearch` - before committing a symbol. Shadowing a Mathlib symbol breaks - downstream files silently. - -6. **Pin the result with `#guard_msgs`.** Always round-trip against - the symbol extracted from the paper verbatim. If the pp output - differs, the build fails — this is Layer 2 of the verification. - - ```lean - /-- info: D(ρ ‖ σ) : ℝ -/ - #guard_msgs in #check (D(ρ ‖ σ) : ℝ) - ``` - -7. **Record provenance.** The notation declaration gets a docstring - citing the `references//…` path *and* (if available) a - specific section, page, or heading anchor. This is what lets a - future reader trace the symbol back to the paper. - -8. **Add companion coercion or callable support if paper notation hides - a type gap.** Papers often elide either the distinction between a - bundled subtype (e.g. `DensityMatrix A`) and its underlying carrier - (`A`), or the distinction between a bundled endomorphism / sector and - the function it applies. Choose the smallest mechanism that restores - the paper surface: - - ```lean - instance : Coe (DensityMatrix A) A := ⟨DensityMatrix.toAlg⟩ - ``` - - If the paper notation treats the object as a callable map, prefer a - `CoeFun` instance and add `FunLike` only when downstream API really - needs extensional lemmas or an existing typeclass hierarchy expects - it (illustrative sketch — `ChargedSector` here stands for whatever - bundled endomorphism type the target file actually defines): - - ```lean - instance : CoeFun (ChargedSector A Λ) (fun _ => A → A) := - ⟨fun ρ => ρ.endo⟩ - ``` - - Gate on: (a) the target type has a unique canonical embedding or - application semantics, (b) no existing `Coe` / `CoeFun` / `FunLike` - instance already covers it (check via `lean_loogle`), (c) the - coercion is monomorphic (no implicit typeclass arguments that could - conflict), and (d) the resulting surface form still round-trips under - `#guard_msgs`. If any gate fails, keep the explicit projection and - flag it in `report.md`. - -### Worked Mode-B example — quantum relative entropy from `references/araki-1976/` - -Suppose the user says "add paper notation for `quantumRelativeEntropy` -using `references/araki-1976/`". The skill: - -1. Opens `references/araki-1976/INDEX.md` → `Key concepts` and finds - the entry `Relative entropy $D(\rho \| \sigma)$ — defined in §2`. -2. Opens `references/araki-1976/content.md` §2 and greps for the - formula. Captures `$D(\rho \| \sigma) = \mathrm{Tr}(\rho (\log \rho - \log \sigma))$`. -3. Translates to Unicode: `D(ρ ‖ σ) = Tr(ρ(log ρ − log σ))`. -4. Proposes notation + companion `Coe` + round-trip test. - -Before: - -```lean -namespace QuantumRelativeEntropy -variable {A : Type*} [CStarAlgebra A] - -/-- Umegaki relative entropy. -/ -noncomputable def quantumRelativeEntropy (ρ σ : A) : ℝ := … - -lemma nonneg (ρ σ : DensityMatrix A) : - 0 ≤ quantumRelativeEntropy ρ.toAlg σ.toAlg := … -end QuantumRelativeEntropy -``` - -After (placeholder-at-`:max` precedence form, the skill's default): - -```lean -namespace QuantumRelativeEntropy -variable {A : Type*} [CStarAlgebra A] - -/-- Umegaki relative entropy. See `references/araki-1976/content.md` -§2 for the definition this notation mirrors. -/ -noncomputable def quantumRelativeEntropy (ρ σ : A) : ℝ := … - -/-- Density matrices coerce into the ambient algebra so paper notation -like `D(ρ ‖ σ)` elaborates without explicit `.toAlg` unfolds. -/ -instance : Coe (DensityMatrix A) A := ⟨DensityMatrix.toAlg⟩ - -/-- Paper notation for Umegaki relative entropy as written in Araki -1976, §2 (see `references/araki-1976/content.md`). Open -`scoped QuantumRelativeEntropy` to use. -/ -scoped notation "D(" ρ:max " ‖ " σ:max ")" => quantumRelativeEntropy ρ σ - -/-- info: D(ρ ‖ σ) : ℝ -/ -#guard_msgs in -example (ρ σ : DensityMatrix A) : - @id ℝ (D(ρ ‖ σ)) = quantumRelativeEntropy (ρ : A) (σ : A) := rfl - -lemma nonneg (ρ σ : DensityMatrix A) : 0 ≤ D(ρ ‖ σ) := … -end QuantumRelativeEntropy -``` - -If the placeholders bump into Lean's parser for other reasons (nested -mixfix, exponents, subscripts), use the `syntax` + `macro_rules` + -`app_unexpander` form instead — see step 5 above. - -## Workflow - -1. **Read the target.** Note existing `import`, `open`, `open scoped` - lines at the top. Keep a running list of already-opened scopes so - you don't double-open. -2. **Mode A pass.** Grep for each long-form pattern. For each hit, check - whether the required scope is already open. If not, prefer adding - `open scoped ` at the file top to opening the whole - namespace (smaller blast radius). If adding the scope would re-export - types or instances that could conflict with local definitions, keep - the long form and note it. -3. **Mode B pass.** Scan for domain-concept identifiers that have a - textbook rendering. For each: - 1. Use `lean_loogle` to check Mathlib for existing notation. - 2. Identify the paper (docstring citation, user direction, or list - `references/` and ask). Open it under `references//`. - 3. Extract the canonical symbol verbatim from `INDEX.md` / `content.md` - / `sections/*.md` (grep + nearest LaTeX formula). - 4. Translate LaTeX to Unicode and apply the precedence / - `scoped notation` / `syntax` / `macro_rules` rules per Mode B - step 5. - 5. Propose the notation + companion `Coe` / `CoeFun` / minimal - `FunLike` support if needed; cite the paper path in the notation's - docstring. -4. **Apply changes one declaration at a time.** After each change call - `lean_diagnostic_messages` on the file. If red, revert just that - change and continue with the next candidate. -5. **Full build gate.** After all changes, `lake build ` must pass with no new warnings. -6. **Axiom hygiene.** Run `lean_verify` on at least one representative - theorem in each touched file. Notation rewrites should never - introduce new axioms, but the check is cheap. -7. **Round-trip test for Mode B.** Append a `#guard_msgs` block (or - equivalent `#check` comparison) that pins the pretty-printed form to - the new notation. This is the single most important check — it is - what confirms a *human reader* sees the new form, not just that the - source text contains the new tokens. -8. **Emit `report.md`** in the workspace directory listing: - - Mode A rewrites applied (file:line, before → after). - - Mode B notation + coercion / callable support added (declaration, - chosen symbol, paper reference). - - Candidates deliberately skipped, with the reason (scope conflict, - coercion gate failed, precedence too fragile, user declined). - -## Guardrails - -- Never write `sorry`, `admit`, `axiom`, `set_option`, `unsafe`, or touch - `System` / `Lean.Elab` / `Lean.Meta`. `CLAUDE.md` in the repo root - forbids all of these. If the skill thinks it needs any of them, it - has strayed out of scope — stop and ask. -- Never modify `lakefile.toml`, `lean-toolchain`, or `lake-manifest.json`. -- Never create namespaces named `QuantumSystem` (the module path - already prefixes every declaration). -- Spaces only, 100-column limit, English comments. -- One logical change per commit if the user asks for a commit. - -## References - -- `references/notation-map.md` — full Mode A mapping table, Mode B - cheat sheet (operator algebra, quantum information, modular theory), - `syntax` / `macro_rules` templates, precedence conventions. diff --git a/.claude/skills/paper-notation-refactor/references/notation-map.md b/.claude/skills/paper-notation-refactor/references/notation-map.md deleted file mode 100644 index f0f2563..0000000 --- a/.claude/skills/paper-notation-refactor/references/notation-map.md +++ /dev/null @@ -1,396 +0,0 @@ -# notation-map.md - -Reference for `paper-notation-refactor`. Two parts: - -- **Part 1** — Mode A static mapping table of Mathlib-staple - conversions the skill applies without external input. -- **Part 2** — Mode B crib sheet + recipes for extracting symbols - from the project's ingested reference material under - `/references//`. - -Read this file whenever `SKILL.md`'s inline table does not cover the -pattern at hand, or when deciding how to shape a new `scoped notation` -block. - -## Part 1 — Mode A mapping table (apply existing Mathlib notation) - -### Inner product - -| long form | paper form | scope to open | -| ------------------------------------------------ | -------------- | ----------------------------------------- | -| `inner ℂ x y` | `⟪x, y⟫_ℂ` | `open scoped InnerProductSpace` | -| `inner ℝ x y` | `⟪x, y⟫_ℝ` | `open scoped InnerProductSpace` | -| `inner 𝕜 x y` | `⟪x, y⟫_𝕜` | `open scoped InnerProductSpace` | -| `inner _ x y` (no subscript) | `⟪x, y⟫` | `open scoped RealInnerProductSpace` or `ComplexInnerProductSpace` depending on `𝕜` | - -Caveats: -- In Mathlib, the **subscripted** form `⟪x, y⟫_𝕜` lives in the - generic `InnerProductSpace` scope - (`Mathlib/Analysis/InnerProductSpace/Defs.lean ~:86`). The - field-specific scopes `RealInnerProductSpace` and - `ComplexInnerProductSpace` only define the **bare** `⟪x, y⟫` (field - inferred). This is the opposite of what the naming might suggest — - double-check when choosing a scope. -- Pick the scope based on the paper form you want: - - `⟪x, y⟫_ℂ` (subscript visible) → `open scoped InnerProductSpace` - - `⟪x, y⟫` (subscript hidden) → `open scoped ComplexInnerProductSpace` - or `RealInnerProductSpace` -- If the file already uses `⟪·, ·⟫_𝕜` explicitly (with subscript), - don't collapse to the bare `⟪·, ·⟫` — the explicit form is clearer - at call sites and matches most textbook conventions. - -### Sums, products, finite set operations - -| long form | paper form | scope | -| ------------------------------------------------- | ----------------- | -------------------------------- | -| `Finset.sum Finset.univ (fun i => f i)` | `∑ i, f i` | `open BigOperators` (default on) | -| `Finset.prod Finset.univ (fun i => f i)` | `∏ i, f i` | `open BigOperators` | -| `Finset.sum S (fun i => f i)` | `∑ i ∈ S, f i` | `open BigOperators` | -| `Finset.prod S (fun i => f i)` | `∏ i ∈ S, f i` | `open BigOperators` | -| `∑ i, f i` where `i : Fin n` | keep as is | already paper form | - -Caveats: -- Mathlib auto-opens `BigOperators` in most analytic files; check the - file's imports before adding an explicit `open BigOperators`. -- Do NOT rewrite `Finset.sum S f` when `f` is a bare named function; the - paper form `∑ i ∈ S, f i` is only a win when the summand body is - non-trivial. - -### Norms, absolute values - -| long form | paper form | scope | -| ----------------- | -------------- | ---------------------------------------------- | -| `norm x` | `‖x‖` | global (`Norm` typeclass) | -| `‖x‖₊` | `‖x‖₊` | keep (nonneg-reals norm, distinct symbol) | -| `dist x y` | `dist x y` | keep — no Unicode standard | -| `abs x` | `|x|` | `open scoped Int.Abs` (for `ℤ`), or mathlib's default | -| `Complex.abs z` | `|z|` | after `open Complex` — global `|·|` works on ℂ | - -### Adjoint, star, involution - -| long form | paper form | scope | -| ------------------------------------ | ------------- | ------------------------------------- | -| `ContinuousLinearMap.adjoint f` | `f†` | `open scoped Adjoint` (if exported) | -| `LinearMap.adjoint f` | `f†` | same | -| `star a` | `a⋆` | `open scoped ComplexConjugate` | -| `(starRingEnd ℂ) z` | `conj z` or `z̄` | `open scoped ComplexConjugate` | - -Caveats: -- `†` notation in Mathlib is currently scoped and changes occasionally - across mathlib versions; if `open scoped Adjoint` is unknown, fall - back to `f.adjoint` (still more paper-like than - `ContinuousLinearMap.adjoint f`). -- `star` vs `⋆` — some files prefer the word form for grepability. - Respect existing file conventions; only rewrite a `star` to `⋆` - when the file already has other `⋆` usages, or when the user - explicitly asks. - -### Complex / real scalars - -| long form | paper form | scope | -| ----------------- | ----------- | ----------------------------------------- | -| `Complex.I` | `I` | `open Complex` / `open scoped Complex` | -| `Complex.ofReal x` | `(x : ℂ)` | ascription — use when context is a term | -| `Real.pi` | `π` | `open Real` / `open scoped Real` | -| `Real.exp x` | `exp x` | `open Real` | -| `Real.log x` | `log x` | `open Real` | -| `Real.sqrt x` | `√x` | only if `open Real` AND `√` is in scope | - -Caveats: -- `I` is a short identifier that can shadow local variables named `I`; - check the file first. If there's a local `I`, leave `Complex.I` alone. -- `Complex.ofReal x` written explicitly is sometimes used for - disambiguation. Only collapse to `(x : ℂ)` when the surrounding - context makes the target type unambiguous. - -### Linear maps, operators, algebras - -| long form | paper form | scope / notes | -| ------------------------------------------------- | -------------------------------- | ------------------------------ | -| `ContinuousLinearMap.id ℂ H` | `(1 : H →L[ℂ] H)` or `id` | contextual | -| `LinearMap.comp f g` | `f ∘ₗ g` | infix `∘ₗ` ships globally | -| `ContinuousLinearMap.comp f g` | `f ∘L g` | infix `∘L` ships globally | -| `f.comp g` (ContinuousLinearMap dot-form) | `f ∘L g` | **default-apply**: dot-form is idiomatic Lean; `∘L` is the paper form in operator-algebra | -| `g.comp (h.comp k)` / `(g.comp h).comp k` | `g ∘L h ∘L k` | default-apply; `∘L` is right-associative in Mathlib, but the pp form matches papers | -| `HPow.hPow x n` (numeric literal) | `x ^ n` | global | -| `SMul.smul c x` | `c • x` | global | - -Caveats for `.comp` → `∘L`: - -- **Do** apply the rewrite by default when the composed maps are - continuous linear maps over a common field. This was the iteration-1 - miss — the skill was too conservative about "dot-notation is - idiomatic" and left `.comp` in place even though `∘L` is what a - paper would show. Default policy: **apply unless there's a specific - reason not to**. -- **Skip** the rewrite when: - - The dot-form call-chain is mixed with non-`ContinuousLinearMap` - `.comp` calls (e.g. `Function.comp`, category-theory composition). - Silent retargeting would be wrong. - - The `.comp` appears as part of a lemma *name* inside `simp [...]` - (e.g. `ContinuousLinearMap.comp_apply`) — those are identifiers, - not composition terms. - - The composition is of two morphisms in an algebra whose paper form - uses multiplication (`fg` juxtaposition), not `∘`. In that case - the right paper form is `f * g`, not `f ∘L g`. - -### Simp-only helpers that look like notation - -Do **not** rewrite: -- `RCLike.ofReal`, `Complex.re`, `Complex.im` — these are functions, not - notation; their long form is the canonical spelling. -- `NNReal.toReal`, `ENNReal.toReal`, `Rat.cast` — same reasoning. -- Field projections (`.re`, `.im`, `.1`, `.2`) — already compact. - -## Part 2 — Mode B crib sheet + extraction from `references//` - -Mode B is **reference-paper-driven**: the skill reads the project's -ingested paper knowledge base under `/references//` (produced -by `pdf-to-knowledge` / `web-to-knowledge`) and uses the paper's own -rendering as the source of truth for Lean notation. The crib sheet -below is a fallback for papers the user has not ingested yet. When a -paper *is* ingested, prefer grepping its `content.md` over the -crib sheet. - -### Extracting a symbol from an ingested paper - -Input: a Lean declaration name (`quantumRelativeEntropy`), a -paper slug (`araki-1976`). - -1. `Read /references//INDEX.md` → "Key concepts" section. - Each key concept entry typically pins the exact paper symbol in - LaTeX. If the concept is listed, you're done — copy its symbol and - translate to Unicode (table below). - -2. If not found in `INDEX.md`, fall back to the body: - - Short paper: `Read /references//content.md` and `Grep` - for the concept's English name and its close synonyms: - - "relative entropy" | "quantum relative entropy" | "Umegaki" - - "GNS representation" | "cyclic representation" - - "modular operator" | "Tomita–Takesaki" | "Tomita" - Capture the **first display formula** near each hit — LaTeX is - delimited `$...$` (inline) or `$$...$$` (display). - - Long paper: same, but in `sections/*.md` (pick the section whose - name aligns with the concept). - -3. Handle spelling variants. Do not silently normalise across papers: - - `D(\rho \| \sigma)` vs `D(\rho \parallel \sigma)` vs - `D(\rho || \sigma)` vs `S(\rho | \sigma)` — use whichever the - cited paper uses. The notation's docstring cites which paper - provided the spelling so a reader can diff. - - `\mathrm{Tr}` vs `\mathop{\mathrm{tr}}` vs `\mathrm{tr}` — - same story. - -4. Translate LaTeX to Unicode using the table below. If the LaTeX uses - a token this table doesn't cover, stop and ask the user rather than - guessing. - -### LaTeX → Unicode translation table - -| LaTeX | Unicode | Notes | -| --------------------- | ------- | -------------------------------------- | -| `\rho` | `ρ` | | -| `\sigma` | `σ` | | -| `\tau` | `τ` | | -| `\omega` | `ω` | | -| `\pi` | `π` | | -| `\Omega` | `Ω` | | -| `\Delta` | `Δ` | | -| `\Lambda` | `Λ` | commonly used for bounded regions in AQFT | -| `\alpha` | `α` | | -| `\beta` | `β` | | -| `\mathcal{H}` | `H` | often the Hilbert-space name | -| `\mathcal{A}` | `A` | algebra / C*-algebra | -| `\mathcal{B}(H)` | `𝓑(H)` | bounded operators (this project uses the `𝓑` script-B) | -| `\mathcal{O}` | `𝒪` | U+1D4AA — AQFT double cone | -| `\mathcal{N}` | `𝒩` | U+1D4A9 — net / nest | -| `\mathcal{F}` | `𝓕` | U+1D4D5 — Fourier / filtration | -| `\mathcal{D}` | `𝓓` | U+1D4D3 — domain of an unbounded op | -| `\mathfrak{A}` | `𝔄` | U+1D504 — Fraktur A, Haag–Kastler local algebra | -| `\mathfrak{F}` | `𝔉` | U+1D509 — Fraktur F, field net | -| `\mathfrak{M}` | `𝔐` | U+1D510 — Fraktur M, von Neumann algebra | -| `\mathfrak{B}(H)` | `𝔅(H)` | U+1D505 — Fraktur B variant of bounded ops | -| `\|` / `\parallel` | `‖` | **same symbol as Lean norm** — use `:max` precedence on placeholders | -| `\otimes` | `⊗` | | -| `\oplus` | `⊕` | | -| `\dagger` / `^*` | `†` | adjoint — mathlib's `Adjoint` scope | -| `\overline{X}` / `\bar{X}` | `X̄` | bar — Unicode combining macron | -| `\langle X \rangle` | `⟨X⟩` | bracket | -| `\llbracket X \rrbracket` | `⟦X⟧` | double bracket | -| `\mathrm{Tr}` | `Tr` | keep ASCII, not a Unicode replacement | -| `\log`, `\exp`, `\sin` | `log`, `exp`, `sin` | keep ASCII | -| `\le` / `\leq` | `≤` | | -| `\neq` | `≠` | | -| `\in` | `∈` | | -| `\subset` / `\subseteq` | `⊆` | | -| `\infty` | `∞` | | -| `\sum` | `∑` | | -| `\prod` | `∏` | | -| `\int` | `∫` | | - -### Crib sheet — common symbols (starting point when no paper ingested) - -| concept | typical paper form | starting Lean skeleton (verify against the user's chosen paper before using) | -| -------------------------- | ------------------ | ---------------------------------------------------------------------------- | -| quantum relative entropy | `D(ρ ‖ σ)` | `scoped notation "D(" ρ:max " ‖ " σ:max ")" => quantumRelativeEntropy ρ σ` | -| von Neumann entropy | `S(ρ)` | `scoped notation "S(" ρ:max ")" => vonNeumannEntropy ρ` | -| GNS representation | `π[ω]` (paper: `π_ω`) | `scoped syntax:max "π[" term "]" : term` + `macro_rules | ``(π[ $ω ]) => ``(gnsRepresentation $ω)` — see "Lexer gotcha" below for why the literal `π_ω` form fails | -| GNS cyclic vector | `Ω[ω]` (paper: `Ω_ω`) | same bracket pattern with `"Ω["` | -| modular operator | `Δ[ω]` (paper: `Δ_ω`) | same bracket pattern with `"Δ["` | -| modular conjugation | `J[ω]` (paper: `J_ω`) | same bracket pattern with `"J["` | -| KMS state at `β` | `ω[β]` (paper: `ω_β`) | `scoped syntax:max "ω[" term "]" : term` — NB: the literal subscript `ω_β` is a single identifier at the lexer level; some papers write `\omega^{(\beta)}` instead | -| partial trace over B | `Tr_B` | `syntax "Tr_" term:max : term` + `macro_rules` | -| partial transpose | `ρ^{T_B}` | `syntax term:max "^{T_" term:max "}" : term` + `macro_rules` | -| density-matrix fidelity | `F(ρ, σ)` | `scoped notation "F(" ρ:max ", " σ:max ")" => fidelity ρ σ` | -| trace distance | `T(ρ, σ)` | `scoped notation "T(" ρ:max ", " σ:max ")" => traceDistance ρ σ` | -| commutator | `[A, B]` or `⟦A,B⟧` | `scoped notation "⟦" A:max ", " B:max "⟧" => commutator A B` | -| anti-commutator | `{A, B}` | avoid — collides with set-builder. Use `⦃A, B⦄` instead | - -**Always check the actual paper first.** The crib sheet assumes the -common Araki / Umegaki / Nielsen–Chuang spellings, but (for example) -some textbooks use `S(ρ|σ)` instead of `D(ρ‖σ)` for the same object. -The skill must not silently pick one when the user's paper uses the -other. - -### Lexer gotcha — subscripted prefixes like `α_g`, `π_ω` - -Lean's lexer treats `α_g` (a letter followed by `_` and more letters) as a -**single identifier**, not as `α` + `_g`. `notation "α_" g:max` and similar -attempts therefore fail with `unexpected token ')'` or `expected term`. A -stand-alone numeric subscript (`α_0`) is fine because digits terminate the -identifier lex, but identifier-like subscripts (`g`, `ω`, `β`) will never -parse. - -**Escape hatch:** use a bracketed form instead. - -```lean --- Works: brackets delimit the subscript cleanly -scoped syntax:max "α[" term "]" : term -scoped macro_rules | `(α[ $g ]) => `(gaugeAction $g) - --- Also works: dedicated two-token form with a visible space -scoped notation:max "α " g:max => gaugeAction g -``` - -Apply the same rule to `π_ω`, `Ω_ω`, `Δ_ω`, `J_ω`, `ω_β`: whenever the -subscript is another identifier, use `π[ω]` / `Ω[ω]` / `Δ[ω]` / `ω[β]` or -an explicit space. The crib sheet flags these as "fragile" for exactly -this reason. - -### `meta def` is mandatory inside `module` / `@[expose] public section` - -Every file under `QuantumSystem/` starts with `module` plus -`@[expose] public section` (and any fixture that mimics that header -inherits the constraint). Under that header, the pretty-printer -unexpander **must** be declared as `meta def`, not `def`: - -```lean -@[app_unexpander quantumRelativeEntropy] -meta def quantumRelativeEntropy.unexpander : Lean.PrettyPrinter.Unexpander - | `($_ $ρ $σ) => `(D( $ρ ‖ $σ )) - | _ => throw () -``` - -A plain `def` under this header fails with `declaration must be marked -as 'meta'`. This is true for every Mode B unexpander in this project. - -### Round-trip template - -Always add a `#guard_msgs` block in the same file as the notation -declaration. This is Layer 2 of the verification: - -```lean -/-- info: D(ρ ‖ σ) : ℝ -/ -#guard_msgs in #check (D(ρ ‖ σ) : ℝ) -``` - -If the pretty-printer output differs from the string in the -`/-- info: ... -/` block, the build fails — catching both `notation` -parse errors and unexpander bugs in one check. - -### Picking the right construct - - - -| use case | Lean construct | -| ------------------------------------------- | ---------------------------------- | -| binary infix with one operator token | `infixl:` / `infixr:` | -| prefix with one token | `prefix:` | -| postfix with one token | `postfix:` | -| fixed keyword or bracketed form with parser-stable separators | `scoped notation "…" => …` | -| identifier-like subscripts, lexer-sensitive forms, or anything needing a custom unexpander | `syntax` + `macro_rules` | - -Do not use unscoped/plain `notation` for project-level paper forms. -Bracketed forms such as `D(ρ ‖ σ)`, `S(ρ)`, and `F(ρ, σ)` may use -`scoped notation` so long as operator-adjacent placeholders are marked -`:max` and the resulting form passes `#guard_msgs`. Subscripted or -lexer-sensitive forms such as `π_ω`, `Ω_ω`, `Tr_B`, `⟨_ | _⟩_ω`, or any -form that still fails round-trip should use `syntax` + `macro_rules`. - -### Precedence conventions (Mathlib) - -| level | used for | -| ----- | ------------------------------------------- | -| 20 | `∧`, `∨` (low-level logic) | -| 35 | `≤`, `<`, `=`, `≠` | -| 50 | `+`, `-` | -| 65 | `*`, `/`, `∘`, `•` | -| 70 | `^` | -| 75 | function application, unary prefixes | -| max | bracket-like constructs (`⟨_, _⟩`, `‖_‖`) | - -### Companion coercion / callable-instance checklist - -Before adding `instance : Coe X Y := ⟨f⟩`, `instance : CoeFun X ...`, or a -minimal `FunLike` companion alongside new notation, confirm: - -1. **Canonical.** The embedding or application semantics is unique. - Two competing candidates (e.g. density matrix → operator vs density - matrix → trace-class operator, or two different action maps) means no - coercion / callable instance — keep the explicit `.toAlg`, `.endo`, - or named projection. -2. **Total.** No side-conditions (`h : X.Positive`). If you need a - hypothesis, make a separate named conversion, not a `Coe` / - `CoeFun`. -3. **Monomorphic.** No typeclass arguments on the projection or action - map that could pick the wrong instance. -4. **Non-conflicting.** `lean_loogle` for the relevant shape (`Coe X Y`, - `CoeFun X`, `FunLike X`) returns nothing relevant; grep the target - file for existing `toAlg` / `toOp` / `.endo` / similar projections - that would become redundant or ambiguous. -5. **Choose the smallest tool.** Default to plain `Coe` for carrier - projections, `CoeFun` when the paper reads the object as a function, - and `FunLike` only when downstream API or extensional lemmas actually - need it. Escape hatches: use `CoeHead` if the coercion must fire only - at the head of an elaboration chain (e.g. to break transitivity - loops), and `CoeTC` only when you explicitly want transitive closure. -6. **Round-trip preserved.** The added instance must improve the visible - paper form and still pass the `#guard_msgs` round-trip check. - -If any check fails, **do not add the instance**. Keep the long form and -flag it in `report.md`. - -### Colocation rule - -Place the `scoped notation` block (and its companion coercion / callable -instance) **immediately after the definition it refers to**, inside the -same namespace. This keeps the declaration, its notation, and its -supporting coercion or callability discoverable from a single file -read. Do not scatter them across a `Notation.lean` sidecar unless the -file is already overflowing the 500-line soft limit. - -### Paper-reference policy - -Every new `notation` declaration gets a docstring citing where the symbol -comes from. Accepted citations: - -- Araki, H. (1976). "Relative Entropy of States of von Neumann Algebras". -- Bratteli & Robinson, "Operator Algebras and Quantum Statistical - Mechanics" vol I & II. -- Nielsen & Chuang, "Quantum Computation and Quantum Information". -- Reed & Simon, "Methods of Modern Mathematical Physics" I–IV. -- Takesaki, "Theory of Operator Algebras" I–III. -- Haag, "Local Quantum Physics". - -For a symbol from a paper not listed above, the skill asks the user to -confirm the citation before adding the notation. diff --git a/.claude/skills/pdf-to-knowledge/SKILL.md b/.claude/skills/pdf-to-knowledge/SKILL.md deleted file mode 100644 index 70a53fd..0000000 --- a/.claude/skills/pdf-to-knowledge/SKILL.md +++ /dev/null @@ -1,347 +0,0 @@ ---- -name: pdf-to-knowledge -description: Convert a PDF document (paper, RFC, spec, manual, datasheet) into agent-friendly Markdown with extracted image assets, saved under `references//` so coding agents can consult it across sessions. Use this skill whenever the user includes a PDF URL (links ending in `.pdf`, `arxiv.org/pdf/...`, `arxiv.org/abs/...`, ACM/IEEE/Nature/bioRxiv links, or similar) or asks to "read", "reference", "import", "ingest", "use as knowledge", or "make available" a PDF, even when they do not explicitly say "convert". Also trigger when the user points at a local `.pdf` file path and wants it turned into durable reference material. ---- - -# pdf-to-knowledge - -Turn a PDF into Markdown + images that a coding agent can navigate efficiently. - -## When to invoke - -- The user's message contains a URL ending in `.pdf` or a well-known PDF host - (`arxiv.org/pdf/...`, `arxiv.org/abs/...`, `dl.acm.org/...`, publisher DOIs that - resolve to PDFs). -- The user asks to "use this paper/spec/manual as reference", "add this to - knowledge", "ingest this doc", etc. -- The user gives a local path to a `.pdf` file and wants it made available for - future reference. - -Do **not** invoke when the user only wants a one-shot summary of a PDF without -persisting it; just answer from `Read` in that case. - -## Output contract - -Everything lands under `/references//`. Coding agents can then -`Read` any file in that tree without re-running the conversion. Layout: - -``` -references// -├── INDEX.md # metadata + Summary + Key concepts + ToC (you fill 2 sections) -├── content.md # present when page count <= 20 -├── sections/ # present when page count > 20 and the PDF has H1 headings -│ ├── 01-.md -│ └── ... -├── assets/ # image files referenced from Markdown as "assets/..." -│ └── image_000001_.jpg -└── mineru-raw/ # optional; only written when the user passes --keep-raw - ├── _middle.json - ├── _layout.pdf - └── ... -``` - -Full format spec: `references/output-format.md` (read it when the user asks -about the layout or you need to tweak it). - -## Workflow - -1. **Extract the source.** Pull the PDF URL or local path out of the user's - message. If multiple are present, ask which one to process unless the user - clearly meant all of them (then loop). - -2. **Decide on the output root.** Default to `references/` in the current - working directory. This project has `references/` in `.gitignore`, and the - convention generalises well — only override if the user specifies a path. - -3. **Run the converter.** Invoke the bundled script via `uv run` so - dependencies install on demand into a cached virtualenv: - - ```bash - uv run .claude/skills/pdf-to-knowledge/scripts/convert.py "" - ``` - - The path above assumes the current working directory is the project root - (where `.claude/` lives). If the skill was installed somewhere else, use - the absolute path of `scripts/convert.py` next to this `SKILL.md`. - - Useful flags (pass only when needed): - - `--output-dir ` — override the `references/` root. - - `--slug ` — override the auto-derived slug (arXiv IDs are - preserved; other sources slugify the filename). - - `--max-pages-per-section N` — split threshold (default 20; do not change - unless the user asks). - - `--ocr` — switch MinerU to OCR mode (for scanned PDFs). Default is - `auto`, which picks text extraction when the PDF has a text layer. - - `--keep-raw` — keep MinerU's auxiliary outputs (middle.json, layout.pdf, - span.pdf, model.json, content_list_v2.json) under - `//mineru-raw/`. Off by default — debugging artefacts - only; they add several MB and are unsuitable for agents. - - The script prints a JSON summary with `slug`, `output_dir`, `page_count`, - `image_count`, and `sectioned` — read it to plan the next step. - -4. **Fill in the INDEX.md scaffold.** The script leaves two sections as - `` and an incomplete frontmatter. Your job: - - a. **Extract authors into the frontmatter.** Read the first ~30 lines of - `content.md` (or the first `sections/*.md`) to find the author list — - usually between the title and the abstract. Add an `authors` field to - the YAML frontmatter as a list of display names: - ```yaml - authors: ["Huzihiro Araki", "Masanao Ozawa"] - ``` - If authors cannot be identified, omit the field (do not invent names). - The authors list enables author-name-based search later on - (see the *Searching related work* section below). - - b. **Write Summary.** 3–5 sentences that answer "what is this doc and - why would a coding agent care?". Base it on the actual content, not - the title alone. - - c. **Write Key concepts.** Bullet list of identifiers an agent would - grep for. **Each bullet must start with a backtick-quoted identifier - name**, followed by a short description. This is a hard format - requirement — `scripts/update_concepts.py` keys its cross-document - index off this leading identifier, and bullets without one collapse - into a `Unnamed concepts` bucket. - - Required shape (three tiers, choose the strongest that applies): - ```markdown - - `` — → formalized: QuantumSystem.X.y - - `` — → mathlib: Mathlib.X.y - - `` — → needs formalization - ``` - - Use `→ formalized: ` only when the concept is already - proven in this repository (`QuantumSystem.*` or another local - module). It outranks `→ mathlib:` because a local proof is - stronger evidence than a mathlib match — `gap-filler` - consequently treats these as the highest tier (`formalized` > - `resolved` > `partial` > `gap`). The tier is verified by the - same `verify_mathlib_refs.py` loop as `mathlib`, so a stale - identifier picks up `[UNVERIFIED]` automatically. - - Contents of the bullets: - - For **code-adjacent papers**: function/type names, CLI flags, - config keys, environment variables, error messages, protocol fields. - - For **math papers**: named definitions, canonical theorems, operator - or functor names (e.g. `modular automorphism group`, `relative - modular operator`, `KMS condition`). - - Skip this section entirely (delete the TODO block) if the doc has - genuinely no greppable terminology — do not fabricate identifiers. - - d. **Cross-reference against mathlib** (only if the Lean MCP toolchain is - available in this session — i.e. `lean_local_search`, - `lean_leansearch` are callable). For each Key concept bullet: - - - Run `lean_local_search ` first. If it returns a - declaration that matches, annotate the bullet with - `→ mathlib: Mathlib.Namespace.name`. - - If nothing comes back, **also try an author-name search**: - `lean_local_search ` for each author extracted in 4a, - plus any canonical author cited repeatedly in the text (e.g. - "Takesaki", "Araki", "Connes"). This surfaces modules that cite - the author in docstrings or comments, which often reveal - adjacent formalisations even when the term itself is not in - mathlib. See *Searching related work* below for rationale. - - If both searches miss, annotate the bullet with - `→ needs formalization` so the gap is explicit. - - If the session has no Lean MCP tools, skip this step — do not - invent annotations. - - **Never trust search results blindly.** After annotating, pass - each proposed `→ mathlib: ` through the verification loop - in step 4g — silent mismatches undermine every downstream agent. - - e. **Extract the bibliography into the frontmatter.** Read the end of - `content.md` (or the final `sections/*.md`) for the `## References` - section. Parse each numbered / keyed entry into a YAML list and add - it to the frontmatter as `bibliography`: - ```yaml - bibliography: - - key: Ara76 # short citation key; invented if absent - title: "Relative entropy of states of von Neumann algebras" - authors: ["Huzihiro Araki"] - venue: "Publ. RIMS Kyoto Univ. 11" - year: 1976 - arxiv: null # arXiv ID if present in the entry - doi: null # DOI if present - ingested: false # true only if already inside references/ - ``` - Rules: - - Preserve the entry order from the paper so `key` matches the - in-text citation numbering when possible. - - If the paper uses author-year keys ("Kos85", "TV20"), keep them; - otherwise generate `refNN` using the 1-based index. - - `authors` is always a list, even for a single author. Omit fields - (set to `null`) rather than invent data. - - Skip the section entirely if the paper has no References — do not - fabricate entries. - - `ingested: true` is reserved for the `update_concepts.py` helper - to set later; leave it `false` while writing. - - f. **Refresh the concept index (strict mode).** After INDEX.md is - fully populated, run - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/update_concepts.py \ - --strict - ``` - - The `--strict` flag fails with exit 1 if any Key concepts bullet - does not start with a `` `backtick-quoted` `` identifier — this - is what the cross-document index keys off (step 4c contract). If - it fails, fix the offending bullets and re-run; never commit a - tree in which the validator complains. - - **This project auto-runs `update_concepts.py` via a - `PostToolUse` hook** (`.claude/settings.json`) whenever an - INDEX.md under a `references/` tree is edited. The `--strict` - invocation above is still required — the hook runs the - non-strict form, so format-drift checks need to happen - explicitly. If you want to confirm nothing drifted silently, - append `--check-fresh` on its own invocation; it exits 1 when - any INDEX.md is newer than CONCEPTS.md. - - g. **Verify every mathlib annotation** (only when Lean MCP tools are - available; skip otherwise). Run - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py \ - extract --output /tmp/mathlib-worklist.json - ``` - - For each item in the worklist, call `mcp__lean-lsp__lean_verify` - on the candidates in the order listed. The **first candidate that - `lean_verify` accepts as a valid declaration counts as verified**. - If every candidate for an item fails, the annotation is a silent - false positive — record the failure as - `{"slug": "...", "line": NN, "reason": "..."}` and append it to a - `failed.json` list. - - After the loop, patch the offending bullets with - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py \ - mark-unverified --from /tmp/failed.json - ``` - - The script appends ` [UNVERIFIED] (reason)` to each annotation - so `gap-filler`'s `detect_gaps.py` treats the concept as - `suspect` rather than resolved. It **also stamps every INDEX.md** - with a `mathlib_verified: ` frontmatter field so the - verification cycle is provable. Re-run step 4f after patching so - `CONCEPTS.md` picks up the markers. - - **Always run `mark-unverified`, even if `failed.json` is an empty - list.** The empty-list case is the "happy path" where every - annotation passed; the script still needs to stamp the tree so - `check-done` (step 4h) recognises the cycle as complete. - - h. **Prove the cycle ran.** Run - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py \ - check-done - ``` - - It exits 0 only when every INDEX.md carries a fresh - `mathlib_verified` timestamp and no INDEX.md has been edited - after being stamped. Treat a non-zero exit as a hard failure — - the ingestion is not finished until this check passes. If you - edit an INDEX.md after stamping, re-run step 4g with an updated - `failed.json` (even empty) to re-stamp. - - Keep `INDEX.md` concise (under ~80 lines, including the bibliography). - The point is navigation, not summarisation; the body lives in - `content.md` / `sections/`. - -5. **Report back to the user.** Tell them the `INDEX.md` path, any notable - caveats (OCR was skipped, large doc was split, no images found), a - one-line count of how many concepts had a mathlib match vs needed - formalization, and how many bibliography entries were extracted. - Mention `CONCEPTS.md` if it was refreshed. Do not paste the full - summary into chat — they can read the file. - -## Searching related work - -When annotating concepts against mathlib (step 4d) or — in future — against -external sources, two search axes should be used together: - -1. **Term-based search.** The mathematical term or identifier itself - (`"relative entropy"`, `"modular automorphism"`, `"scaled dot-product - attention"`). This finds definitions and direct restatements. - -2. **Author-name search.** The surname(s) of the paper's authors plus the - canonical author of the concept being formalised (often different — - e.g. Araki wrote about *relative entropy* building on *Takesaki's* - modular theory; both names should be tried). Rationale: mathlib - developers routinely cite sources in docstrings and section comments, - so `lean_local_search "Takesaki"` surfaces declarations that invoke - Takesaki-style theory even when the user's exact term is absent. This - is how neighbouring formalisations get discovered. - -Examples: - -- Concept "modular automorphism group" → `lean_local_search "modular - automorphism"` (term) + `lean_local_search "Takesaki"` (author). -- Concept "Araki's relative entropy" → `lean_local_search "relative - entropy"` + `lean_local_search "Araki"` + `lean_local_search "Connes"` - (Connes-Radon-Nikodym cocycle underlies the construction). - -The same two-axis pattern applies to future web / textbook search skills: -looking up the author's publication list often yields introductions that -fill prerequisite gaps which the primary paper assumes. - -## Why this design - -- **MinerU over Docling for math papers.** Earlier iterations used Docling - as a library; it handled layout / figures well but emitted - `` for every equation by default, and the - opt-in formula VLM was GPU-tuned and failed to make meaningful progress - in 10 minutes on an 18-page paper on CPU. MinerU's `pipeline` backend - decodes the same paper end-to-end in ~7 minutes on CPU, with LaTeX for - every formula (display and inline) and no placeholders. MinerU also - preserves equation numbers via `\tag{...}` and provides a straight JSON - page index. The gain for math formalisation work is qualitative, not - just quantitative. -- **CLI subprocess, not library import.** MinerU's Python API exists but - is less stable than the CLI and pulls in a heavier dependency graph - (vLLM, xformers) when imported directly. Shelling out to `mineru` via - `subprocess.run` sidesteps the Python version churn and lets us install - the tool once per machine with `uv tool install "mineru[all]"`. -- **GPL-free dependencies.** MinerU 3.1+ is Apache 2.0 with commercial - thresholds (100M MAU / $20M monthly revenue) that do not apply here. - `pymupdf`/`poppler`/`marker-pdf` remain off-limits for license reasons. -- **Claude writes Summary / Key concepts, not the script.** Summarisation - and identifier extraction are judgement calls; a scripted heuristic - would be wrong often enough to be harmful. The script handles the - mechanical work (download, convert, chunk, save images, scaffold); the - agent handles the semantic work. - -## Runtime prerequisites - -- **`uv`** must be on `PATH`. The script itself declares no Python deps - (stdlib only) but uses `uv run` for invocation hygiene. -- **`mineru` CLI must be on `PATH`**. One-off install: - `uv tool install "mineru[all]"` (≈ 4 GB of Python packages + model weights - on first run). Subsequent runs reuse the cache and start in a second. -- **Disk space.** MinerU's models plus the `mineru[all]` environment live - in the uv tool directory; budget ~4 GB. Per-paper runtime memory peaks - at 3–4 GB on the Araki-size paper. -- **GPU (optional).** If `nvidia-smi` reports a working GPU the converter - switches MinerU to its `hybrid-auto-engine` backend, which is - dramatically faster on the formula-recognition stage. Otherwise it - silently uses the CPU-only `pipeline` backend. No flag needed. - -## Failure modes to watch for - -- **Network errors on download.** The script exits non-zero; forward the - error and ask the user for a local path. -- **Scanned PDFs (no text layer).** Output Markdown will be nearly empty. Re-run - with `--ocr` after asking the user, since OCR adds minutes. -- **No H1 headings despite >20 pages.** The script falls back to a single - `content.md` — that is intentional, do not try to hand-split. -- **Very large docs (>100 pages).** First-run conversion can take several - minutes on CPU; warn the user before starting so they do not cancel. diff --git a/.claude/skills/pdf-to-knowledge/hooks/README.md b/.claude/skills/pdf-to-knowledge/hooks/README.md deleted file mode 100644 index da65ed3..0000000 --- a/.claude/skills/pdf-to-knowledge/hooks/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# pdf-to-knowledge hooks - -`.claude/settings.json` wires a `PostToolUse` hook that auto-regenerates -`references/CONCEPTS.md` whenever an `INDEX.md` under a `references/` tree -is edited. The hook is optional — nothing else in the skill depends on it. - -## Files - -- `on_index_change.py` — the hook implementation (stdlib-only). -- `README.md` — this file. - -## How the wiring works - -`.claude/settings.json` contains: - -```json -{ - "hooks": { - "PostToolUse": [ - { - "matcher": "Edit|Write|NotebookEdit", - "hooks": [ - { - "type": "command", - "command": "sh -c 'script=\"${CLAUDE_PROJECT_DIR}/.claude/skills/pdf-to-knowledge/hooks/on_index_change.py\"; [ -f \"$script\" ] && exec python3 \"$script\" || exit 0'" - } - ] - } - ] - } -} -``` - -Important details: - -- **`$CLAUDE_PROJECT_DIR`** is provided by Claude Code in every hook - subprocess. It resolves to the project root that contains `.claude/`, - regardless of the editor's current working directory. The command - does **not** hardcode `/workspaces/quantum-system` anywhere — copy - this tree into another project and the hook continues to work without - edits. -- **Existence guard** (`[ -f "$script" ]`): if the hook script has been - moved or deleted, the shell exits 0 instead of erroring. The skill - author can delete `hooks/on_index_change.py` to disable the - behaviour without also editing `settings.json`. -- **`exec python3`**: replaces the shell process with the Python one, - minimising PID churn. -- **POSIX `sh`**: no Bash-isms, so the command runs under any POSIX - shell Claude Code picks. - -## What the hook does - -1. Reads the `PostToolUse` JSON payload from stdin. -2. Extracts `tool_input.file_path`. -3. If the path ends in `INDEX.md` and is under a `references/` directory, - the hook invokes - `.claude/skills/pdf-to-knowledge/scripts/update_concepts.py ` - to rebuild `CONCEPTS.md`. -4. **Fixture paths are skipped** — edits under `skill-creator-tests/` or - any `iteration-*/` ancestor do not trigger regeneration, so frozen - eval outputs stay stable. -5. Any exception is caught and the script exits 0. The hook never blocks - an Edit tool call on its own error. - -## Portability / disabling - -- **Move to a different project**: copy `.claude/skills/pdf-to-knowledge/` - verbatim AND keep the `hooks.PostToolUse` stanza in the new project's - `.claude/settings.json`. No path edits required. -- **Disable**: delete the `hooks` stanza from `.claude/settings.json`, - or delete `hooks/on_index_change.py` itself (the existence guard - makes this a clean no-op). -- **Debug**: add a one-liner inside `main()` that writes to - `/tmp/hook.log`. The hook's stderr surfaces in the Claude Code - transcript as `[concepts-hook]` lines. - -## Limitations - -- The hook relies on `$CLAUDE_PROJECT_DIR` being set. If a future - Claude Code release drops that variable, the `[ -f "$script" ]` - guard turns the hook into a no-op (exit 0) — no crash, but the - auto-refresh stops silently. Monitor `CONCEPTS.md` freshness via - `update_concepts.py --check-fresh` in that case. -- The hook runs on **every** `Edit|Write|NotebookEdit`. For files that - are not INDEX.md, it returns early at step 2 above — one cheap - stdin-JSON parse per tool call. If you notice hook latency, profile - `update_concepts.py` on your references tree; it is stdlib-only and - typically runs in < 200 ms. diff --git a/.claude/skills/pdf-to-knowledge/hooks/on_index_change.py b/.claude/skills/pdf-to-knowledge/hooks/on_index_change.py deleted file mode 100644 index 6c2280d..0000000 --- a/.claude/skills/pdf-to-knowledge/hooks/on_index_change.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python3 -"""PostToolUse hook: refresh ``references/CONCEPTS.md`` after an INDEX.md edit. - -Wired from ``.claude/settings.json`` via: - - { - "hooks": { - "PostToolUse": [ - { - "matcher": "Edit|Write|NotebookEdit", - "hooks": [ - { - "type": "command", - "command": "sh -c 'script=\"${CLAUDE_PROJECT_DIR}/.claude/skills/pdf-to-knowledge/hooks/on_index_change.py\"; [ -f \"$script\" ] && exec python3 \"$script\" || exit 0'" - } - ] - } - ] - } - } - -See ``hooks/README.md`` for the rationale behind the ``sh -c`` wrapper -(CLAUDE_PROJECT_DIR portability + graceful disable when the script is -removed). The hook is deliberately permissive: - -- It exits 0 on any exception so a bad hook never blocks Claude's edit. -- It ignores edits that do not target a ``references//INDEX.md``. -- It does nothing when the edited INDEX.md is inside a skill snapshot or - test-iteration tree (``skill-creator-tests/`` / ``iteration-*/``) — those - dirs contain frozen fixtures that should not auto-refresh. -- Output goes to stderr so the user sees "[concepts-hook] regenerated ..." - inline with their session. -""" - -from __future__ import annotations - -import json -import os -import re -import subprocess -import sys -from pathlib import Path - - -SKIP_PARTS = frozenset({"skill-snapshot", "gap-filler-snapshot", "web-to-knowledge-snapshot"}) -SKIP_PREFIXES = ("iteration-", "iter-") - - -def _is_index_md(path: Path) -> bool: - return path.name == "INDEX.md" and any(p.name == "references" for p in path.parents) - - -def _is_fixture(path: Path) -> bool: - parts = set(path.parts) - if parts & SKIP_PARTS: - return True - # Skip iteration-N style test artefacts so regenerated CONCEPTS.md does - # not disturb frozen eval outputs. - for parent in path.parents: - if parent.name.startswith(SKIP_PREFIXES) and parent.parent.name == "skill-creator-tests" or ( - parent.name == "skill-creator-tests" - ): - return True - return False - - -def _references_root(path: Path) -> Path | None: - """Return the ``references/`` directory that owns this INDEX.md, or None.""" - for parent in path.parents: - if parent.name == "references": - return parent - return None - - -def _run_update_concepts(root: Path) -> None: - script = ( - Path(__file__).resolve().parents[1] / "scripts" / "update_concepts.py" - ) - try: - result = subprocess.run( - ["python3", str(script), str(root)], - capture_output=True, - text=True, - timeout=30, - ) - except Exception as exc: - print(f"[concepts-hook] failed to invoke update_concepts.py: {exc}", file=sys.stderr) - return - message = (result.stdout or result.stderr).strip().splitlines() - for line in message[-3:]: - print(f"[concepts-hook] {line}", file=sys.stderr) - - -def main() -> int: - try: - payload = json.load(sys.stdin) - except (json.JSONDecodeError, OSError): - return 0 - - tool_input = payload.get("tool_input") or {} - raw = tool_input.get("file_path") or "" - if not raw: - # Edit/Write with no resolvable path — nothing to do. - return 0 - - path = Path(raw) - if not path.is_absolute(): - cwd = payload.get("cwd") or os.getcwd() - path = (Path(cwd) / path).resolve() - else: - path = path.resolve() - - if not _is_index_md(path): - return 0 - if _is_fixture(path): - return 0 - - root = _references_root(path) - if root is None or not root.is_dir(): - return 0 - - _run_update_concepts(root) - return 0 - - -if __name__ == "__main__": - try: - raise SystemExit(main()) - except Exception as exc: - # Hook must never block the session on its own errors. - print(f"[concepts-hook] unexpected error: {exc}", file=sys.stderr) - raise SystemExit(0) diff --git a/.claude/skills/pdf-to-knowledge/references/output-format.md b/.claude/skills/pdf-to-knowledge/references/output-format.md deleted file mode 100644 index 7f2995c..0000000 --- a/.claude/skills/pdf-to-knowledge/references/output-format.md +++ /dev/null @@ -1,207 +0,0 @@ -# Output format: `references//` - -Detailed specification of what the skill writes to disk. Read this when you -need to tweak the converter or answer a user question about where something -lives. - -## Directory layout - -``` -references// -├── INDEX.md -├── content.md (page count <= 20, OR > 20 but no H1 headings to split on) -├── sections/ (page count > 20 and the PDF has H1 headings) -│ ├── 01-.md -│ └── 02-.md -├── assets/ -│ ├── image_000001_.jpg -│ └── image_000002_.jpg -└── mineru-raw/ (only with --keep-raw; see the last section for contents) -``` - -Exactly one of `content.md` or `sections/` exists — never both. Keeps the -agent's mental model simple. - -## Slug derivation - -- arXiv URLs (`arxiv.org/abs/2501.12345v1`, `arxiv.org/pdf/2501.12345`) → - `arxiv-2501.12345`. Version suffix is stripped so re-fetching v2 lands in - the same directory. -- Other URLs → slugified filename from the URL path (`.pdf` stripped). -- Local paths → slugified basename. -- `--slug` flag always wins. - -## `INDEX.md` structure - -```markdown ---- -title: "..." -source: "" -pages: -slug: -assets: # number of extracted image files -authors: ["Name1", "Name2"] # populated by Claude in step 4a; omitted if unknown -bibliography: # populated by Claude in step 4e; empty list if the paper has no References - - key: "" - title: "" - authors: ["Name1", ...] - venue: "" - year: - arxiv: "" - doi: "" - ingested: false # true when the referenced paper is already under references/; used by the concept index ---- - -# - -## Summary -<!-- 3-5 sentences, filled in by Claude after conversion --> - -## Key concepts -<!-- bullet list of identifiers; deleted entirely if not applicable. - Each bullet may end with a tier annotation: - - `<term>` — definition/description → formalized: `QuantumSystem.A.b` - - `<term>` — definition/description → mathlib: `Mathlib.A.B.c` - - `<term>` — definition/description → needs formalization ---> - -## Contents -- [<section title>](sections/NN-<slug>.md) -- ... -``` - -Frontmatter is plain YAML — machine-readable. The body is human/agent-readable -navigation. Summary and Key concepts are scaffolded as `<!-- TODO -->` blocks; -Claude replaces them. `authors` is filled by Claude after scanning the first -page of `content.md`; when present it powers author-name-based related-work -search in step 4d. `bibliography` is populated in step 4e and drives the -cross-document "next papers to ingest" suggestions (currently manual, -automated in a future iteration). - -### Concept annotations - -Cross-references live inline on each Key concepts bullet as a trailing -arrow annotation. Three forms are recognised by `update_concepts.py`, -`detect_gaps.py`, and `verify_mathlib_refs.py`: - -- `→ formalized: QuantumSystem.X.y` — locally proven in this repository. - Strongest tier; outranks `resolved` for prioritisation. -- `→ mathlib: Mathlib.X.y` — present in mathlib (term or author search hit - that passed `lean_verify`). Tier `resolved`. -- `→ needs formalization` — no match in either search axis. Tier `gap`. - -A `[UNVERIFIED] (reason)` suffix is appended by -`verify_mathlib_refs.py mark-unverified` when the candidate failed -`lean_verify`; both `mathlib` and `formalized` annotations participate in -that loop. - -They are plain Markdown — agents can `grep -n "→ formalized:"`, -`grep -n "→ mathlib:"`, `grep -n "→ needs formalization"`, or -`grep -n "[UNVERIFIED]"` to slice an INDEX.md by tier. The annotation is -added only when the session has Lean MCP tooling; otherwise it is -omitted. - -### ToC detail - -- **Split mode (sections/)**: one entry per section file in document order. -- **Monolith mode (content.md)**: the "Full content" link, followed by - indented child links for each H2/H3 heading (up to 20) with GitHub-style - anchor fragments — e.g. ``[3 Model Architecture](content.md#3-model-architecture)``. - This lets an agent jump straight to the relevant section without scanning - the whole file. - -## Section splitting rules - -Trigger: `page_count > --max-pages-per-section` (default 20). - -Algorithm: -1. Walk the raw Markdown line by line. -2. Each `# ` line starts a new section. `## `, `### `, etc. are body. -3. Content before the first `# ` (if non-empty) becomes a `front-matter` - section, numbered first. -4. If only one real section is produced, fall back to a single `content.md` - (no splitting, ToC points at the monolith). - -Section filenames are `NN-<heading-slug>.md` where `NN` is a zero-padded -1-based index and the slug is derived the same way as the top-level slug -(lowercase, ASCII-ish, max 60 chars). - -## Image handling - -- MinerU writes extracted images into `<scratch>/<name>/auto/images/` with - hash-named JPG files and references them from the Markdown as - `images/<hash>.jpg`. The post-processor in `scripts/convert.py` rewrites - these references to `assets/<hash>.jpg`, copies **only the referenced** - images into `assets/`, and drops any orphan clips MinerU may have left - behind (mostly per-formula crops MinerU keeps as debugging spare data). -- For pure math papers with no figures MinerU's `images/` directory still - gets populated with formula clips. None of them are referenced from the - Markdown (LaTeX handles the equations), so `assets/` ends up empty — the - converter does not create it in that case. - -## Formulas - -Always on. MinerU emits LaTeX for both inline (`$...$`) and display (`$$...$$`) -math. Equation numbering is preserved via `\tag{...}`. No opt-in flag is -needed; the formula recogniser runs as part of the `pipeline` backend at -modest additional CPU cost (~2 minutes on an 18-page paper). - -## OCR - -Off by default (`--ocr` to enable). Running OCR on a text-native PDF is slow -and often introduces transcription errors. Switch MinerU's method to `ocr` -only when the user confirms the PDF is scanned or when the initial -conversion returned near-empty Markdown. - -## `CONCEPTS.md` (cross-document index) - -Sibling of the per-slug subdirs under `references/`. Regenerated by -`scripts/update_concepts.py` at the end of step 4f in the workflow. - -``` -references/ -├── CONCEPTS.md # this file -├── arxiv-2202.03357/ -│ └── INDEX.md -├── arxiv-1706.03762/ -│ └── INDEX.md -└── ... -``` - -Content: - -```markdown -# Concepts index - -Auto-generated from references/*/INDEX.md. Do not edit by hand — rerun -`.claude/skills/pdf-to-knowledge/scripts/update_concepts.py references/`. - -## <Concept title> - -- [<slug>](<slug>/INDEX.md) — <description from Key concepts bullet> → mathlib: Mathlib.A.B.c -- [<another slug>](<another slug>/INDEX.md) — <description> → needs formalization -``` - -Each section header is a concept title (the backtick-quoted identifier at -the head of a `Key concepts` bullet). Entries are grouped by concept so a -glance reveals which papers cover each term and whether any of them gives -a mathlib mapping. The file is fully derived — editing it by hand is -overwritten on the next rebuild. - -Implementation note: concept keys are normalised to lowercase for -deduplication but displayed in their original case. Bullets with no -backtick-quoted lead identifier fall under a synthetic -"`Unnamed concepts`" heading to avoid silent loss. - -## `mineru-raw/` (optional debug artefacts) - -- **Off by default** since iteration-6; pass `--keep-raw` to the converter - to retain them. When enabled, `<slug>/mineru-raw/` holds MinerU's - auxiliary outputs: - - `<name>_middle.json` — raw DocumentAnalysis JSON (2+ MB) - - `<name>_content_list.json` and `_v2` — per-span structured content - - `<name>_model.json` — layout model outputs - - `<name>_layout.pdf`, `_span.pdf`, `_origin.pdf` — annotated PDFs useful - when triaging a conversion bug -- Not intended for agents to `Read`; the useful signal is already in - `content.md` / `INDEX.md`. Safe to delete. diff --git a/.claude/skills/pdf-to-knowledge/scripts/convert.py b/.claude/skills/pdf-to-knowledge/scripts/convert.py deleted file mode 100755 index 8a73657..0000000 --- a/.claude/skills/pdf-to-knowledge/scripts/convert.py +++ /dev/null @@ -1,397 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.10" -# dependencies = [] -# /// -"""Convert a PDF into agent-friendly Markdown + image assets using MinerU. - -Produces under ``<output-dir>/<slug>/``: - -- ``INDEX.md`` Metadata, ToC, and scaffolded Summary / Key concepts / Bibliography - frontmatter sections that the caller (Claude) fills in after - reading the content. -- ``content.md`` Full Markdown (if total pages <= max-pages-per-section). -- ``sections/`` Split Markdown files (if total pages exceed the threshold and - the document has top-level ``#`` / ``## `` headings to split on). -- ``assets/`` Images referenced from the Markdown. - -This script is a thin post-processor around the MinerU CLI. MinerU's -``pipeline`` backend runs entirely on CPU and, unlike Docling's formula VLM, -decodes mathematical formulas into LaTeX in a few minutes per paper. We gave -up on Docling in iteration-6 after its formula VLM crawled on CPU. - -Usage: - uv run convert.py <pdf-url-or-path> [--output-dir DIR] [--slug SLUG] - [--max-pages-per-section N] [--ocr] - [--keep-raw] - -Prerequisite: - ``mineru`` must be on PATH. One-off install: - uv tool install "mineru[all]" -""" - -from __future__ import annotations - -import argparse -import json -import re -import shutil -import subprocess -import sys -import tempfile -import urllib.parse -import urllib.request -from pathlib import Path - - -def slugify(text: str) -> str: - """Lowercase, ASCII-friendly filesystem slug, capped at 60 chars.""" - text = text.lower().strip() - text = re.sub(r"[^\w\s-]", "", text, flags=re.UNICODE) - text = re.sub(r"[\s_-]+", "-", text) - return text[:60].strip("-") or "document" - - -def derive_slug(source: str) -> str: - """Pick a readable slug from a URL or local path.""" - m = re.search(r"arxiv\.org/(?:pdf|abs)/([\w.-]+?)(?:v\d+)?(?:\.pdf)?(?:[?#]|$)", source) - if m: - return f"arxiv-{m.group(1)}" - if source.startswith(("http://", "https://")): - parsed = urllib.parse.urlparse(source) - basename = Path(parsed.path).stem or parsed.netloc - return slugify(basename) - return slugify(Path(source).stem) - - -def normalise_pdf_url(url: str) -> str: - """Rewrite arxiv ``abs/`` URLs to their ``pdf/`` counterparts.""" - m = re.match(r"^(https?://arxiv\.org)/abs/([\w.-]+?)(?:v\d+)?/?$", url) - if m: - return f"{m.group(1)}/pdf/{m.group(2)}" - return url - - -def download_pdf(url: str, dest: Path) -> None: - req = urllib.request.Request(url, headers={"User-Agent": "pdf-to-knowledge/1.0"}) - with urllib.request.urlopen(req, timeout=120) as response, dest.open("wb") as out: - while chunk := response.read(65536): - out.write(chunk) - - -def _heading_level(line: str) -> int: - """Return the Markdown heading level (1..6) of ``line``, or 0 if not a heading.""" - m = re.match(r"^(#{1,6}) \S", line) - return len(m.group(1)) if m else 0 - - -def split_markdown_by_top_headings(md: str) -> list[tuple[str, str]]: - """Split Markdown at its top-most heading level. Returns ``[(title, body), ...]``.""" - has_h1 = any(_heading_level(line) == 1 for line in md.splitlines()) - split_level = 1 if has_h1 else 2 - - sections: list[tuple[str, str]] = [] - current_title: str | None = None - current_body: list[str] = [] - for line in md.splitlines(keepends=True): - if _heading_level(line.rstrip("\n")) == split_level: - if current_title is not None: - sections.append((current_title, "".join(current_body))) - current_title = line.lstrip("#").strip() - current_body = [line] - else: - if current_title is None: - current_title = "front-matter" - current_body = [] - current_body.append(line) - if current_title is not None: - sections.append((current_title, "".join(current_body))) - return sections - - -def extract_document_title(md: str, fallback: str) -> str: - """Return the first heading (any level) in ``md``, or ``fallback``.""" - for line in md.splitlines(): - m = re.match(r"^#{1,6} (.+)", line) - if m: - return m.group(1).strip() - return fallback - - -def _github_anchor(heading: str) -> str: - """Compute a GitHub-style Markdown anchor for a heading.""" - slug = heading.strip().lower() - slug = re.sub(r"[^\w\s-]", "", slug, flags=re.UNICODE) - slug = re.sub(r"\s+", "-", slug) - return slug.strip("-") - - -def extract_toc_headings(md: str, max_entries: int = 20) -> list[tuple[int, str]]: - """Extract H2/H3 headings from ``md`` for navigation.""" - entries: list[tuple[int, str]] = [] - for line in md.splitlines(): - m = re.match(r"^(##{1,2}) (.+)", line) - if m: - level = len(m.group(1)) - title = m.group(2).strip() - entries.append((level, title)) - if len(entries) >= max_entries: - break - return entries - - -def _flat_toc_for_content(md: str) -> list[str]: - """Build ToC Markdown lines for the single-file ``content.md`` case.""" - lines = ["- [Full content](content.md)"] - for level, title in extract_toc_headings(md): - indent = " " if level == 2 else " " - anchor = _github_anchor(title) - lines.append(f"{indent}- [{title}](content.md#{anchor})") - return lines - - -def _has_gpu() -> bool: - """Detect an NVIDIA GPU via ``nvidia-smi``. Returns False on any error. - - MinerU's `hybrid-auto-engine` backend uses GPU when available and is - dramatically faster than `pipeline` for the formula-recognition stage. - Falls back to CPU `pipeline` cleanly when `nvidia-smi` is absent. - """ - if shutil.which("nvidia-smi") is None: - return False - try: - result = subprocess.run( - ["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"], - capture_output=True, - text=True, - timeout=5, - ) - except (subprocess.TimeoutExpired, OSError): - return False - return result.returncode == 0 and bool(result.stdout.strip()) - - -def run_mineru(pdf_path: Path, output_base: Path, ocr: bool) -> tuple[Path, Path, Path]: - """Invoke the MinerU CLI. Returns (md_path, images_dir, content_list_json).""" - if shutil.which("mineru") is None: - print( - "[error] 'mineru' not found on PATH. Install with: " - "uv tool install \"mineru[all]\"", - file=sys.stderr, - ) - sys.exit(3) - - method = "ocr" if ocr else "auto" - # Auto-select backend: hybrid-auto-engine uses GPU when available and - # can be >5x faster for formula-heavy papers; pipeline is CPU-only. - backend = "hybrid-auto-engine" if _has_gpu() else "pipeline" - print(f"[info] MinerU backend: {backend}", file=sys.stderr) - cmd = [ - "mineru", - "-p", str(pdf_path), - "-o", str(output_base), - "-b", backend, - "-l", "en", - "-m", method, - ] - print(f"[info] Running: {' '.join(cmd)}", file=sys.stderr) - subprocess.run(cmd, check=True) - - name = pdf_path.stem - auto_dir = output_base / name / "auto" - md_path = auto_dir / f"{name}.md" - images_dir = auto_dir / "images" - content_list_json = auto_dir / f"{name}_content_list.json" - if not md_path.is_file(): - print(f"[error] MinerU did not produce {md_path}", file=sys.stderr) - sys.exit(4) - return md_path, images_dir, content_list_json - - -def page_count_from_content_list(content_list_json: Path) -> int: - """Read max ``page_idx`` from MinerU's content_list.json and return page count.""" - try: - data = json.loads(content_list_json.read_text(encoding="utf-8")) - except Exception: - return 0 - max_idx = 0 - for item in data: - idx = item.get("page_idx") - if isinstance(idx, int): - max_idx = max(max_idx, idx) - return max_idx + 1 if data else 0 - - -_IMAGE_REF = re.compile(r"!\[[^\]]*\]\(images/([^)]+)\)") - - -def rewrite_and_collect_image_refs(md: str) -> tuple[str, set[str]]: - """Rewrite ``images/...`` references to ``assets/...`` and return referenced names.""" - referenced: set[str] = set() - - def _sub(match: re.Match) -> str: - name = match.group(1) - referenced.add(name) - return match.group(0).replace("images/", "assets/", 1) - - return _IMAGE_REF.sub(_sub, md), referenced - - -def main() -> int: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("source", help="PDF URL or local path") - parser.add_argument( - "--output-dir", - default="references", - help="Parent directory; output is placed under <output-dir>/<slug>/", - ) - parser.add_argument("--slug", default=None, help="Override the auto-derived slug") - parser.add_argument( - "--max-pages-per-section", - type=int, - default=20, - help="Split into sections/ if the document exceeds this many pages (default: 20)", - ) - parser.add_argument( - "--ocr", - action="store_true", - help="Use MinerU's OCR method (for scanned PDFs). Default is 'auto'.", - ) - parser.add_argument( - "--keep-raw", - action="store_true", - help=( - "Keep MinerU's raw auxiliary outputs (middle.json, layout.pdf, span.pdf, " - "model.json, content_list_v2.json) under <output-dir>/<slug>/mineru-raw/. " - "Off by default — they are debugging artefacts unsuitable for agents." - ), - ) - args = parser.parse_args() - - source: str = args.source - slug = args.slug or derive_slug(source) - output_root = Path(args.output_dir).expanduser().resolve() - output_dir = output_root / slug - output_dir.mkdir(parents=True, exist_ok=True) - assets_dir = output_dir / "assets" - - # Resolve input to a local path. - if source.startswith(("http://", "https://")): - fetch_url = normalise_pdf_url(source) - tmpdir = Path(tempfile.mkdtemp(prefix="pdf-to-knowledge-")) - pdf_path = tmpdir / f"{slug}.pdf" - print(f"[info] Downloading {fetch_url}", file=sys.stderr) - download_pdf(fetch_url, pdf_path) - else: - pdf_path = Path(source).expanduser().resolve() - if not pdf_path.exists(): - print(f"[error] Not found: {pdf_path}", file=sys.stderr) - return 2 - - # Run MinerU into a fresh scratch directory so we fully own the layout. - mineru_scratch = Path(tempfile.mkdtemp(prefix="mineru-")) - md_path, images_src, content_list_json = run_mineru( - pdf_path, mineru_scratch, ocr=args.ocr - ) - - page_count = page_count_from_content_list(content_list_json) - print(f"[info] Pages: {page_count}", file=sys.stderr) - - md_text = md_path.read_text(encoding="utf-8") - md_text, referenced_images = rewrite_and_collect_image_refs(md_text) - - # Copy referenced images only — MinerU occasionally extracts formula clips - # that are not referenced from the Markdown; keeping them clutters agents. - image_count = 0 - if referenced_images and images_src.is_dir(): - assets_dir.mkdir(exist_ok=True) - for name in referenced_images: - src = images_src / name - if src.is_file(): - shutil.copy(src, assets_dir / name) - image_count += 1 - - # Optionally keep MinerU's raw auxiliary outputs for debugging. - if args.keep_raw: - raw_dir = output_dir / "mineru-raw" - raw_dir.mkdir(exist_ok=True) - for aux in md_path.parent.iterdir(): - if aux.name == md_path.name or aux == images_src: - continue - if aux.is_file(): - shutil.copy(aux, raw_dir / aux.name) - - # Clean up the scratch tree now that we have what we need. - shutil.rmtree(mineru_scratch, ignore_errors=True) - - # Write content.md / sections/ based on page count. - content_md = output_dir / "content.md" - title = extract_document_title(md_text, slug.replace("-", " ").title()) - - sectioned = False - toc_lines: list[str] = [] - if page_count > args.max_pages_per_section: - parts = split_markdown_by_top_headings(md_text) - real_parts = [(t, b) for t, b in parts if t != "front-matter" or b.strip()] - if len(real_parts) > 1: - sections_dir = output_dir / "sections" - sections_dir.mkdir(exist_ok=True) - for i, (section_title, body) in enumerate(real_parts, start=1): - sec_slug = slugify(section_title) - sec_file = sections_dir / f"{i:02d}-{sec_slug}.md" - sec_file.write_text(body, encoding="utf-8") - toc_lines.append(f"- [{section_title}](sections/{sec_file.name})") - sectioned = True - else: - content_md.write_text(md_text, encoding="utf-8") - toc_lines.extend(_flat_toc_for_content(md_text)) - else: - content_md.write_text(md_text, encoding="utf-8") - toc_lines.extend(_flat_toc_for_content(md_text)) - - # Scaffold INDEX.md (Claude fills Summary / Key concepts / authors / - # bibliography / mathlib annotations per SKILL.md steps 4a-4f). - index = [ - "---", - f"title: {json.dumps(title, ensure_ascii=False)}", - f"source: {json.dumps(source, ensure_ascii=False)}", - f"pages: {page_count}", - f"slug: {slug}", - f"assets: {image_count}", - "---", - "", - f"# {title}", - "", - "## Summary", - "", - "<!-- TODO: 3-5 sentence summary of what this document covers and why a", - " coding agent would consult it. Fill in after reading the content. -->", - "", - "## Key concepts", - "", - "<!-- TODO: Bullet list of identifiers an agent would grep for. Each bullet", - " MUST start with a `backtick-quoted` identifier — this drives the", - " cross-document concept index. Omit the section if no greppable", - " terminology exists (e.g. pure prose papers). -->", - "", - "## Contents", - "", - *toc_lines, - "", - ] - (output_dir / "INDEX.md").write_text("\n".join(index), encoding="utf-8") - - summary = { - "slug": slug, - "output_dir": str(output_dir), - "index_path": str(output_dir / "INDEX.md"), - "page_count": page_count, - "image_count": image_count, - "sectioned": sectioned, - } - print(json.dumps(summary, indent=2, ensure_ascii=False)) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.claude/skills/pdf-to-knowledge/scripts/update_concepts.py b/.claude/skills/pdf-to-knowledge/scripts/update_concepts.py deleted file mode 100755 index 80bae8a..0000000 --- a/.claude/skills/pdf-to-knowledge/scripts/update_concepts.py +++ /dev/null @@ -1,344 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.10" -# dependencies = [] -# /// -"""Rebuild ``<references-root>/CONCEPTS.md`` from every ``INDEX.md`` under it. - -Runs after a new PDF is ingested so concepts mentioned across multiple -documents appear in one cross-document index. The script is intentionally -dependency-free so it can execute with ``python3 update_concepts.py``. - -Usage: - python3 update_concepts.py <references-root> - -Example: - python3 update_concepts.py ./references - -Behaviour: -- Walks each subdirectory of ``<references-root>`` that contains an - ``INDEX.md`` and extracts bullets under ``## Key concepts``. -- Groups entries by the leading backtick-quoted identifier, so - ``- `modular automorphism group` via Tomita-Takesaki ... → needs formalization`` - is keyed by ``modular automorphism group``. -- Writes ``<references-root>/CONCEPTS.md`` with one section per concept, - sorted alphabetically by display name; existing file is overwritten. -- Bullets without a backtick-led identifier fall under - ``Unnamed concepts`` so nothing is silently lost. -""" - -from __future__ import annotations - -import argparse -import re -import sys -from collections import defaultdict -from pathlib import Path - - -KEY_CONCEPTS_HEADER = re.compile(r"^##\s+Key concepts\s*$", re.IGNORECASE) -NEXT_H2 = re.compile(r"^##\s+") -BULLET = re.compile(r"^\s*-\s+(.*)$") -LEADING_CODE = re.compile(r"^`([^`]+)`(.*)$") -# Characters that plausibly separate a concept name from its description when -# the author forgot to wrap the name in backticks (iteration-5 fallback). -PLAIN_SEPARATOR = re.compile(r"\s*(?:[—–:]|\s-\s|\()\s*") -ANNOTATION_SPLIT = re.compile(r"\s*→\s") - - -def _fallback_concept_name(body: str) -> tuple[str, str]: - r"""Best-effort concept extraction for bullets that lack a backtick head. - - Strategy: strip any trailing ``→ mathlib:`` / ``→ needs formalization`` - annotation, then split on the first em-dash, en-dash, colon, " - ", or - opening parenthesis. The left side becomes the concept name; the right - side (plus the original annotation) becomes the body. If no separator is - found, collapse names longer than 60 chars so they do not dominate the - concept index header. - """ - # Preserve the annotation so it travels with the bullet in CONCEPTS.md. - annotation = "" - parts = ANNOTATION_SPLIT.split(body, maxsplit=1) - head = parts[0].strip() - if len(parts) == 2: - annotation = "→ " + parts[1].strip() - - m = PLAIN_SEPARATOR.search(head) - if m: - name = head[: m.start()].strip() - rest = head[m.end() :].strip() - # If we split on "(" leave the content so the user sees the full - # phrasing, but close a dangling paren if the close-paren was the - # last char. - if m.group(0).startswith("(") and rest.endswith(")"): - rest = rest[:-1].rstrip() - else: - name = head - rest = "" - - # Avoid absurdly long "names" when the bullet is really one long sentence. - if len(name) > 60: - name = name[:60].rstrip(" -") + "…" - - body_parts = [p for p in (rest, annotation) if p] - return name, " ".join(body_parts) - - -def extract_key_concepts(md: str) -> list[tuple[str, str]]: - """Return ``(concept_name, rest_of_bullet)`` pairs from an INDEX.md body. - - Preference order: - 1. Leading ```backtick``-quoted identifier (contracted format from step 4c). - 2. Heuristic fallback (see ``_fallback_concept_name``). - 3. Empty name — bullet lands under ``Unnamed concepts``. - """ - lines = md.splitlines() - inside = False - collected: list[tuple[str, str]] = [] - - for line in lines: - if KEY_CONCEPTS_HEADER.match(line): - inside = True - continue - if inside and NEXT_H2.match(line): - break - if not inside: - continue - bullet = BULLET.match(line) - if not bullet: - continue - body = bullet.group(1).strip() - if not body or body.startswith("<!--"): - continue - m = LEADING_CODE.match(body) - if m: - name = m.group(1).strip() - rest = m.group(2).lstrip(" -—:").rstrip() - else: - name, rest = _fallback_concept_name(body) - collected.append((name, rest)) - return collected - - -def load_index_files(references_root: Path) -> dict[str, Path]: - """Return ``{slug: index_md_path}`` for every ``<slug>/INDEX.md`` under the root.""" - mapping: dict[str, Path] = {} - for entry in sorted(references_root.iterdir()): - if not entry.is_dir(): - continue - index = entry / "INDEX.md" - if index.is_file(): - mapping[entry.name] = index - return mapping - - -def build_concepts_md(references_root: Path, script_rel: str) -> str: - """Render the final ``CONCEPTS.md`` text.""" - # group[display_name] = list of (slug, rest_of_bullet) - grouped: dict[str, list[tuple[str, str]]] = defaultdict(list) - display_name: dict[str, str] = {} - - for slug, index_path in load_index_files(references_root).items(): - text = index_path.read_text(encoding="utf-8", errors="replace") - for name, rest in extract_key_concepts(text): - key = name.lower() if name else "" - if name and key not in display_name: - display_name[key] = name - grouped[key].append((slug, rest)) - - out: list[str] = [ - "# Concepts index", - "", - "Auto-generated from references/*/INDEX.md. Do not edit by hand —", - f"rerun `{script_rel} references/` to refresh.", - "", - ] - - if not grouped: - out.append("_No concepts found yet._") - out.append("") - return "\n".join(out) - - def sort_key(k: str) -> tuple[int, str]: - # Non-empty names first, sorted alphabetically; unnamed bucket last. - return (1 if k == "" else 0, display_name.get(k, "Unnamed concepts").lower()) - - for key in sorted(grouped, key=sort_key): - header = display_name.get(key, "Unnamed concepts") if key else "Unnamed concepts" - out.append(f"## {header}") - out.append("") - for slug, rest in grouped[key]: - link = f"[{slug}]({slug}/INDEX.md)" - if rest: - out.append(f"- {link} — {rest}") - else: - out.append(f"- {link}") - out.append("") - - return "\n".join(out) - - -def audit_freshness(references_root: Path) -> dict: - """Return the list of ``<slug>/INDEX.md`` newer than ``CONCEPTS.md``. - - Used by ``--check-fresh`` to surface staleness caused by edits that - skipped the concept-index refresh (T3-A guard). ``CONCEPTS.md`` absent - is itself a staleness signal. - """ - concepts = references_root / "CONCEPTS.md" - missing_concepts = not concepts.is_file() - concepts_mtime = concepts.stat().st_mtime if concepts.is_file() else 0.0 - newer: list[dict] = [] - for slug, index_path in load_index_files(references_root).items(): - idx_mtime = index_path.stat().st_mtime - # Small skew tolerance (1 s) so a CONCEPTS.md written in the same - # second as the INDEX.md still passes. - if idx_mtime > concepts_mtime + 1: - newer.append( - { - "slug": slug, - "path": str(index_path), - "index_mtime": idx_mtime, - "concepts_mtime": concepts_mtime, - } - ) - return { - "concepts_present": not missing_concepts, - "concepts_path": str(concepts), - "concepts_mtime": concepts_mtime, - "newer_index_files": newer, - "is_fresh": not missing_concepts and not newer, - } - - -def audit_strict(references_root: Path) -> list[dict]: - r"""Return a list of ``{slug, line, body}`` violations of the format contract. - - The contract (SKILL.md step 4c) says every Key concepts bullet must - start with a ``\`backtick\`-quoted`` identifier. This auditor flags: - - - Bullets that do not start with a backtick token at all (the ones - that collapse into the synthetic ``Unnamed concepts`` bucket). - - Bullets whose leading backtick contains whitespace but no - punctuation (ambiguous identifier). - - The returned list is empty when the tree complies. - """ - violations: list[dict] = [] - for slug, index_path in load_index_files(references_root).items(): - text = index_path.read_text(encoding="utf-8", errors="replace") - lines = text.splitlines() - inside = False - for idx, line in enumerate(lines, start=1): - if KEY_CONCEPTS_HEADER.match(line): - inside = True - continue - if inside and NEXT_H2.match(line): - break - if not inside: - continue - bullet = BULLET.match(line) - if not bullet: - continue - body = bullet.group(1).strip() - if not body or body.startswith("<!--"): - continue - if not LEADING_CODE.match(body): - violations.append( - { - "slug": slug, - "index_path": str(index_path), - "line": idx, - "body": body, - } - ) - return violations - - -def main() -> int: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("references_root", help="Directory that contains per-slug subdirs") - parser.add_argument( - "--strict", - action="store_true", - help=( - "Exit non-zero if any Key concepts bullet does not start with a " - "backtick-quoted identifier. Use in CI / post-ingestion hooks " - "to catch format drift that would otherwise silently collapse " - "into the 'Unnamed concepts' bucket." - ), - ) - parser.add_argument( - "--check-fresh", - action="store_true", - help=( - "Read-only staleness check: exit 1 if any references/<slug>/INDEX.md " - "was modified after references/CONCEPTS.md (or CONCEPTS.md is " - "missing). Does not regenerate — combine with a separate " - "invocation to refresh. Use in hooks / CI to fail fast when the " - "concept index falls out of sync." - ), - ) - args = parser.parse_args() - - references_root = Path(args.references_root).expanduser().resolve() - if not references_root.is_dir(): - print(f"[error] not a directory: {references_root}", file=sys.stderr) - return 2 - - if args.check_fresh: - report = audit_freshness(references_root) - if report["is_fresh"]: - print(f"[ok] CONCEPTS.md fresh: {report['concepts_path']}") - return 0 - if not report["concepts_present"]: - print( - f"[fail] {report['concepts_path']} does not exist — run " - "update_concepts.py without --check-fresh to generate it.", - file=sys.stderr, - ) - else: - print( - f"[fail] {len(report['newer_index_files'])} INDEX.md file(s) " - "newer than CONCEPTS.md — re-run update_concepts.py to refresh:", - file=sys.stderr, - ) - for item in report["newer_index_files"]: - print(f" {item['slug']} {item['path']}", file=sys.stderr) - return 1 - - if args.strict: - violations = audit_strict(references_root) - if violations: - print( - f"[fail] {len(violations)} Key concepts bullet(s) miss the " - "leading `backtick` identifier contract:", - file=sys.stderr, - ) - for v in violations: - snippet = v["body"][:100] + ("…" if len(v["body"]) > 100 else "") - print( - f" {v['slug']} INDEX.md:{v['line']} {snippet}", - file=sys.stderr, - ) - print( - "[hint] rewrite each offending bullet so it starts with a " - "`backtick-quoted` concept name — this is what " - "scripts/update_concepts.py keys off for the cross-document " - "index (SKILL.md step 4c).", - file=sys.stderr, - ) - return 1 - - script_rel = ".claude/skills/pdf-to-knowledge/scripts/update_concepts.py" - md = build_concepts_md(references_root, script_rel) - target = references_root / "CONCEPTS.md" - target.write_text(md.rstrip() + "\n", encoding="utf-8") - - concept_count = sum(1 for line in md.splitlines() if line.startswith("## ")) - print(f"[ok] wrote {target} ({concept_count} concepts)") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py b/.claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py deleted file mode 100644 index 6920f42..0000000 --- a/.claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py +++ /dev/null @@ -1,576 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.10" -# dependencies = [] -# /// -r"""Audit ``→ mathlib: ...`` and ``→ formalized: ...`` annotations. - -This script does not itself call the Lean toolchain — the actual existence -check is performed by the caller (Claude) via the -``mcp__lean-lsp__lean_verify`` MCP tool, one symbol at a time. The script -is the mechanical half: - -- ``extract`` Walk the references root, pull every ``→ mathlib: ...`` and - ``→ formalized: ...`` annotation into a JSON worklist - (each item carries a ``kind`` field discriminating the - two). The caller feeds each candidate symbol to - ``lean_verify`` and records failures in a ``failed.json``. -- ``mark-unverified`` Given the caller's ``failed.json``, rewrite each - offending INDEX.md bullet so the annotation becomes - ``→ mathlib: \`<path>\` [UNVERIFIED]`` (or - ``→ formalized: \`<path>\` [UNVERIFIED]``) — a marker that - ``gap-filler``'s ``detect_gaps.py`` reads as ``suspect`` - status. - -Rationale: we do not want to trust Claude's judgement on whether a Lean -declaration exists. The T1-A remediation is to externalise verification -to the Lean MCP and mark anything that can not be proven to exist. The -same loop applies to ``→ formalized:`` — local declarations rot when -files are renamed, so verifying them on every ingestion catches drift. - -Usage: - python3 verify_mathlib_refs.py extract <references-root> [--output worklist.json] - python3 verify_mathlib_refs.py mark-unverified <references-root> \\ - --from failed.json [--dry-run] - -The ``failed.json`` schema expected by ``mark-unverified`` is: - - [ - { - "slug": "arxiv-2202.03357", - "line": 47, - "reason": "Mathlib.FakeName.notAReal not found by lean_verify" - }, - ... - ] -""" - -from __future__ import annotations - -import argparse -import datetime as _dt -import json -import re -import subprocess -import sys -from pathlib import Path - - -# Path to the sibling skill's ``issues.py`` (gap-filler owns the tracker -# format; this script is its biggest auto-producer). The lookup is -# best-effort: if the file is missing we silently skip issue creation so -# the verification cycle still works on a partial install. -_ISSUES_SCRIPT = ( - Path(__file__).resolve().parents[2] / "gap-filler" / "scripts" / "issues.py" -) - - -KEY_CONCEPTS_HEADER = re.compile(r"^##\s+Key concepts\s*$", re.IGNORECASE) -NEXT_H2 = re.compile(r"^##\s+") -BULLET = re.compile(r"^\s*-\s+(.*)$") -# Splits a bullet body around a ``→ mathlib:`` or ``→ formalized:`` marker. -# Group 2 captures which kind matched so each worklist item carries a -# ``kind`` field (``mathlib`` vs ``formalized``). -ANNOT_SPLIT = re.compile( - r"(\s*→\s*(mathlib|formalized)\s*:\s*)", re.IGNORECASE -) -TICKED = re.compile(r"`([^`]+)`") -UNVERIFIED_MARKER = "[UNVERIFIED]" -VERIFIED_FIELD = "mathlib_verified" -STALE_SKEW_SECONDS = 5 - - -def _read_slug(md_text: str, fallback: str) -> str: - in_front = False - for line in md_text.splitlines(): - if line.strip() == "---": - if not in_front: - in_front = True - continue - break - if in_front: - m = re.match(r"^slug\s*:\s*(.+)$", line) - if m: - return m.group(1).strip().strip('"').strip("'") - return fallback - - -def _extract_concept_name(body: str) -> str: - m = re.match(r"^`([^`]+)`", body) - if m: - return m.group(1).strip() - head = body.split("—", 1)[0].split(":", 1)[0].split("(", 1)[0].strip() - return head[:80] - - -IDENT_OR_QUALIFIED = re.compile(r"^[A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*$") - - -def _collect_candidates(annotation_body: str, concept: str) -> list[str]: - """Enumerate things the caller should try passing to ``lean_verify``. - - Order of preference: - 1. Backticked tokens in the annotation (both module paths and decl names). - 2. Bare ``Foo.Bar.baz`` tokens in the annotation (typical of bullets - where the author wrapped only the concept, not the mathlib path). - 3. The bullet's leading backticked concept head itself — often a decl - name (e.g. ``VonNeumannAlgebra``) that exists without any extra - hint in the annotation. - - Duplicates are preserved (order matters for the verifier; we try the - more specific candidate first) but obvious module-path tokens are - kept because ``lean_verify`` can also check a namespace. - """ - candidates: list[str] = [] - - for token in TICKED.findall(annotation_body): - token = token.strip() - if IDENT_OR_QUALIFIED.match(token): - candidates.append(token) - - for bare in re.findall(r"[A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)+", annotation_body): - if bare not in candidates: - candidates.append(bare) - - concept = concept.strip() - if concept and IDENT_OR_QUALIFIED.match(concept) and concept not in candidates: - candidates.append(concept) - - return candidates - - -def iter_index_files(root: Path): - for entry in sorted(root.iterdir()): - if not entry.is_dir(): - continue - index = entry / "INDEX.md" - if index.is_file(): - yield entry.name, index - - -def extract(root: Path) -> dict: - items: list[dict] = [] - for slug_dirname, index_path in iter_index_files(root): - text = index_path.read_text(encoding="utf-8", errors="replace") - slug = _read_slug(text, slug_dirname) - lines = text.splitlines() - inside = False - for idx, line in enumerate(lines, start=1): - if KEY_CONCEPTS_HEADER.match(line): - inside = True - continue - if inside and NEXT_H2.match(line): - break - if not inside: - continue - bullet = BULLET.match(line) - if not bullet: - continue - body = bullet.group(1).strip() - if not body or body.startswith("<!--"): - continue - parts = ANNOT_SPLIT.split(body, maxsplit=1) - # ANNOT_SPLIT has two capture groups: ``(full_marker, kind)`` — - # so ``re.split`` returns ``[before, full_marker, kind, after]``. - if len(parts) < 4: - continue - full_marker, kind, after = parts[1], parts[2].lower(), parts[3] - annotation = full_marker + after - if UNVERIFIED_MARKER in annotation: - continue # already flagged; leave alone - concept = _extract_concept_name(body) - candidates = _collect_candidates(after, concept) - if not candidates: - continue - items.append( - { - "slug": slug, - "index_path": str(index_path), - "line": idx, - "kind": kind, - "concept": concept, - "raw_annotation": annotation.strip(), - "candidates": candidates, - } - ) - return { - "references_root": str(root), - "total_items": len(items), - "items": items, - } - - -def _frontmatter_span(text: str) -> tuple[int, int] | None: - """Return ``(open_line, close_line)`` of the YAML fences (1-indexed, inclusive).""" - lines = text.splitlines() - if not lines or lines[0].strip() != "---": - return None - for i in range(1, len(lines)): - if lines[i].strip() == "---": - return (1, i + 1) - return None - - -def _set_frontmatter_field(index_path: Path, field: str, value: str) -> bool: - text = index_path.read_text(encoding="utf-8", errors="replace") - span = _frontmatter_span(text) - if span is None: - return False - had_trailing_nl = text.endswith("\n") - lines = text.splitlines(keepends=True) - open_ln, close_ln = span - field_re = re.compile(rf"^{re.escape(field)}\s*:\s*.*$") - # Replace existing field if present. - for idx in range(open_ln, close_ln - 1): - stripped = lines[idx].rstrip("\n") - if field_re.match(stripped): - lines[idx] = f"{field}: {value}\n" - index_path.write_text("".join(lines), encoding="utf-8") - return True - # Otherwise insert directly before the closing fence. - insert_at = close_ln - 1 - lines.insert(insert_at, f"{field}: {value}\n") - patched = "".join(lines) - if not had_trailing_nl and patched.endswith("\n"): - patched = patched.rstrip("\n") - index_path.write_text(patched, encoding="utf-8") - return True - - -def _read_frontmatter_field(index_path: Path, field: str) -> str | None: - text = index_path.read_text(encoding="utf-8", errors="replace") - span = _frontmatter_span(text) - if span is None: - return None - lines = text.splitlines() - field_re = re.compile(rf"^{re.escape(field)}\s*:\s*(.+)$") - for idx in range(span[0], span[1] - 1): - m = field_re.match(lines[idx]) - if m: - return m.group(1).strip().strip('"').strip("'") - return None - - -def _bullet_metadata(line: str) -> dict: - """Extract the kind / concept / candidate visible on a Key concepts bullet.""" - info = {"kind": "mathlib", "concept": "", "candidate": ""} - m_kind = re.search( - r"→\s*(formalized|mathlib)\s*:\s*(.*)$", line, re.IGNORECASE - ) - if m_kind: - info["kind"] = m_kind.group(1).lower() - # First backticked or qualified token after the marker is the - # candidate the verifier rejected. - tail = m_kind.group(2) - m_tick = re.search(r"`([^`]+)`", tail) - if m_tick: - info["candidate"] = m_tick.group(1).strip() - else: - m_bare = re.search(r"[A-Za-z_][\w.]+", tail) - if m_bare: - info["candidate"] = m_bare.group(0).strip() - m_concept = re.search(r"`([^`]+)`", line) - if m_concept: - info["concept"] = m_concept.group(1).strip() - return info - - -def _mark_one( - index_path: Path, line_no: int, reason: str, *, dry_run: bool -) -> dict | None: - """Patch the bullet at *line_no* with ``[UNVERIFIED]``. - - Returns ``None`` if no patch was applied (out-of-range or already - flagged). Otherwise returns a record carrying the bullet's - ``kind`` / ``concept`` / ``candidate`` metadata so the caller can - open a matching tracker issue. - """ - text = index_path.read_text(encoding="utf-8", errors="replace") - lines = text.splitlines(keepends=True) - if line_no < 1 or line_no > len(lines): - print(f"[warn] out-of-range line {line_no} in {index_path}", file=sys.stderr) - return None - original = lines[line_no - 1] - if UNVERIFIED_MARKER in original: - return None # nothing to do - had_trailing_nl = original.endswith("\n") - stripped = original.rstrip("\n") - metadata = _bullet_metadata(stripped) - # Append the marker at the very end of the bullet; reason optional. - if reason: - patched = f"{stripped} {UNVERIFIED_MARKER} ({reason})" - else: - patched = f"{stripped} {UNVERIFIED_MARKER}" - lines[line_no - 1] = patched + ("\n" if had_trailing_nl else "") - if dry_run: - print(f"[dry-run] {index_path}:{line_no}: {stripped!r} -> {patched!r}") - return metadata - index_path.write_text("".join(lines), encoding="utf-8") - return metadata - - -def _open_issue_for_failure( - references_root: Path, - *, - slug: str, - metadata: dict, - reason: str, -) -> dict | None: - """Best-effort issue creation via the sibling ``issues.py`` CLI. - - Returns the parsed JSON output of ``issues.py add`` on success, or - ``None`` when the script is unavailable (partial install) or the - invocation fails. - """ - if not _ISSUES_SCRIPT.is_file(): - return None - kind = "unverified-formalized-ref" if metadata.get("kind") == "formalized" else "unverified-mathlib-ref" - cmd = [ - sys.executable, - str(_ISSUES_SCRIPT), - "add", - str(references_root), - "--kind", - kind, - "--opened-by", - "verify_mathlib_refs.mark_unverified", - ] - if slug: - cmd += ["--slug", slug] - if metadata.get("concept"): - cmd += ["--concept", metadata["concept"]] - if metadata.get("candidate"): - cmd += ["--candidate", metadata["candidate"]] - if reason: - cmd += ["--reason", reason] - cmd += [ - "--suggested-action", - "Re-search a candidate or open gap-filler to ingest a better source.", - ] - try: - result = subprocess.run( - cmd, capture_output=True, text=True, check=True, timeout=15 - ) - except (subprocess.SubprocessError, OSError) as exc: - print(f"[warn] issues.py add failed for {slug}: {exc}", file=sys.stderr) - return None - out = result.stdout.strip() - try: - return json.loads(out) if out else None - except json.JSONDecodeError: - return None - - -def mark_unverified( - root: Path, failures: list[dict], *, dry_run: bool, create_issues: bool = True -) -> dict: - marked = 0 - skipped = 0 - per_slug: dict[str, list[int]] = {} - issues_opened: list[dict] = [] - for idx, failure in enumerate(failures, start=1): - if not isinstance(failure, dict): - print(f"[warn] failure #{idx} is not an object", file=sys.stderr) - skipped += 1 - continue - slug_raw = failure.get("slug") - if not isinstance(slug_raw, str) or not slug_raw.strip(): - print(f"[warn] failure #{idx} has no usable slug", file=sys.stderr) - skipped += 1 - continue - slug = slug_raw.strip() - line_raw = failure.get("line") - try: - if isinstance(line_raw, bool): - raise ValueError - line_no = int(line_raw) - except (TypeError, ValueError): - print( - f"[warn] failure #{idx} has invalid line {line_raw!r}", - file=sys.stderr, - ) - skipped += 1 - continue - if line_no < 1: - print( - f"[warn] failure #{idx} has out-of-range line {line_no}", - file=sys.stderr, - ) - skipped += 1 - continue - reason_raw = failure.get("reason") - reason = reason_raw.strip() if isinstance(reason_raw, str) else "" - index_path = root / slug / "INDEX.md" - if not index_path.is_file(): - print(f"[warn] missing INDEX.md for slug {slug!r}", file=sys.stderr) - skipped += 1 - continue - metadata = _mark_one(index_path, line_no, reason, dry_run=dry_run) - if metadata is not None: - marked += 1 - per_slug.setdefault(slug, []).append(line_no) - if create_issues and not dry_run: - opened = _open_issue_for_failure( - root, slug=slug, metadata=metadata, reason=reason - ) - if opened: - issues_opened.append({"slug": slug, **opened}) - else: - skipped += 1 - # After patching (real mode), stamp every INDEX.md with a fresh - # ``mathlib_verified`` marker so ``check-done`` can prove the - # verification cycle actually ran on this tree. - stamped: list[str] = [] - if not dry_run: - timestamp = _dt.datetime.now(_dt.timezone.utc).isoformat(timespec="seconds") - for slug_dirname, index_path in iter_index_files(root): - if _set_frontmatter_field(index_path, VERIFIED_FIELD, timestamp): - stamped.append(slug_dirname) - return { - "marked": marked, - "skipped": skipped, - "per_slug": per_slug, - "dry_run": dry_run, - "stamped_slugs": stamped, - "issues_opened": issues_opened, - } - - -def check_done(root: Path) -> dict: - """Confirm every INDEX.md under ``root`` carries a fresh verification marker. - - Returns a report with ``ok`` (True when the tree is verified clean), - ``unverified`` (slugs whose INDEX.md lacks the stamp or has a - malformed timestamp) and ``stale`` (slugs whose INDEX.md was edited - after the last verification). - """ - unverified: list[dict] = [] - stale: list[dict] = [] - ok_slugs: list[str] = [] - for slug_dirname, index_path in iter_index_files(root): - stamp_raw = _read_frontmatter_field(index_path, VERIFIED_FIELD) - if not stamp_raw: - unverified.append( - {"slug": slug_dirname, "path": str(index_path), "reason": "no mathlib_verified field"} - ) - continue - try: - stamp = _dt.datetime.fromisoformat(stamp_raw) - if stamp.tzinfo is None: - stamp = stamp.replace(tzinfo=_dt.timezone.utc) - except ValueError: - unverified.append( - { - "slug": slug_dirname, - "path": str(index_path), - "reason": f"unparseable timestamp {stamp_raw!r}", - } - ) - continue - mtime = _dt.datetime.fromtimestamp( - index_path.stat().st_mtime, _dt.timezone.utc - ) - if mtime > stamp + _dt.timedelta(seconds=STALE_SKEW_SECONDS): - stale.append( - { - "slug": slug_dirname, - "path": str(index_path), - "verified_at": stamp.isoformat(), - "mtime": mtime.isoformat(), - } - ) - continue - ok_slugs.append(slug_dirname) - return { - "ok": not unverified and not stale, - "verified_slugs": ok_slugs, - "unverified": unverified, - "stale": stale, - } - - -def main() -> int: - parser = argparse.ArgumentParser(description=__doc__) - sub = parser.add_subparsers(dest="cmd", required=True) - - p_extract = sub.add_parser("extract", help="Extract mathlib annotation worklist") - p_extract.add_argument("references_root") - p_extract.add_argument("--output", default=None) - - p_mark = sub.add_parser("mark-unverified", help="Patch INDEX.md with [UNVERIFIED]") - p_mark.add_argument("references_root") - p_mark.add_argument("--from", dest="failures_path", required=True) - p_mark.add_argument("--dry-run", action="store_true") - p_mark.add_argument( - "--no-issues", - action="store_true", - help=( - "Suppress automatic issue creation under <root>/issues/. By " - "default each newly stamped [UNVERIFIED] bullet opens an " - "idempotent tracker entry so the false positive is not " - "forgotten across sessions." - ), - ) - - p_check = sub.add_parser( - "check-done", - help="Exit 1 if any INDEX.md lacks a fresh mathlib_verified stamp", - ) - p_check.add_argument("references_root") - - args = parser.parse_args() - - root = Path(args.references_root).expanduser().resolve() - if not root.is_dir(): - print(f"[error] not a directory: {root}", file=sys.stderr) - return 2 - - if args.cmd == "extract": - report = extract(root) - payload = json.dumps(report, indent=2, ensure_ascii=False) - if args.output: - Path(args.output).write_text(payload + "\n", encoding="utf-8") - print( - f"[ok] wrote {args.output}: {report['total_items']} mathlib annotations", - file=sys.stderr, - ) - else: - print(payload) - return 0 - - if args.cmd == "mark-unverified": - failures_raw = Path(args.failures_path).read_text(encoding="utf-8") - try: - failures = json.loads(failures_raw) - except json.JSONDecodeError as exc: - print(f"[error] bad JSON: {exc}", file=sys.stderr) - return 2 - if not isinstance(failures, list): - print("[error] failures JSON must be a list of objects", file=sys.stderr) - return 2 - summary = mark_unverified( - root, - failures, - dry_run=args.dry_run, - create_issues=not args.no_issues, - ) - print(json.dumps(summary, indent=2, ensure_ascii=False)) - return 0 - - if args.cmd == "check-done": - report = check_done(root) - print(json.dumps(report, indent=2, ensure_ascii=False)) - if report["ok"]: - return 0 - print( - f"[fail] {len(report['unverified'])} unverified + " - f"{len(report['stale'])} stale INDEX.md under {root}", - file=sys.stderr, - ) - return 1 - - return 2 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.claude/skills/web-to-knowledge/SKILL.md b/.claude/skills/web-to-knowledge/SKILL.md deleted file mode 100644 index 2531f73..0000000 --- a/.claude/skills/web-to-knowledge/SKILL.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -name: web-to-knowledge -description: Convert an HTML web page (Wikipedia article, nLab page, textbook chapter, blog post, documentation page) into agent-friendly Markdown with preserved LaTeX math and downloaded image assets, saved under `references/<slug>/` so coding agents can consult it across sessions. Use this skill whenever the user includes a URL that points at a human-readable HTML page (links like `en.wikipedia.org/wiki/...`, `ncatlab.org/nlab/show/...`, textbook TOC pages, software docs) or asks to "read", "reference", "import", "ingest", "use as knowledge", or "make available" a web page, even when they do not explicitly say "convert". Also trigger when the user wants to expand their existing `references/` with related articles that cite-or-are-cited-by a paper already under `references/`. Do NOT trigger for PDF URLs — those belong to the `pdf-to-knowledge` skill. ---- - -# web-to-knowledge - -Turn a web page into Markdown + images that a coding agent can navigate -efficiently. Output is shape-compatible with `pdf-to-knowledge` so the -shared `scripts/update_concepts.py` (lives in `pdf-to-knowledge`) indexes -concepts across both skills. - -## When to invoke - -- The user's message contains a URL ending in `/wiki/...`, `ncatlab.org/...`, - a documentation host, or anything that clearly resolves to an HTML page - (not `.pdf`). -- The user asks to "use this wiki/article/page as reference", "add this to - knowledge", "ingest this doc", etc. -- A previous `pdf-to-knowledge` ingestion referenced a Wikipedia or - textbook page as prerequisite knowledge and the user wants to pull it in - too — e.g. "also grab the Wikipedia article on modular automorphism group - from the references I just ingested". - -Do **not** invoke for: -- **PDF URLs** — use `pdf-to-knowledge` instead. This skill's parser assumes - HTML and will fail on `application/pdf`. -- One-shot "summarise this page" requests where the user does not want - persistent reference material — `WebFetch` is sufficient. - -## Output contract - -Everything lands under `<cwd>/references/<slug>/`, matching the -`pdf-to-knowledge` layout so `update_concepts.py` aggregates both. - -``` -references/<slug>/ -├── INDEX.md # metadata + Summary + Key concepts + External links + ToC (you fill 3 sections) -├── content.md # page Markdown with preserved LaTeX math and relative image links -└── assets/ # downloaded images referenced from content.md -``` - -Slug conventions: -- Wikipedia → `wiki-<kebab-title>` (e.g. `wiki-tomita-takesaki-theory`). -- nLab → `nlab-<kebab-title>` (e.g. `nlab-relative-entropy`). -- Other hosts → `web-<basehost>-<basename>`. - -Full format spec: `references/output-format.md`. - -## Workflow - -1. **Extract the URL.** Pull the web-page URL out of the user's message. - If multiple are present, ask which one to process unless the user - clearly meant all of them (then loop). - -2. **Decide on the output root.** Default to `references/` in the current - working directory (same convention as `pdf-to-knowledge` — already - gitignored in this project). - -3. **Run the fetcher.** Invoke the bundled script via `uv run` so the HTML - parsing dependencies install on demand into a cached virtualenv: - - ```bash - uv run .claude/skills/web-to-knowledge/scripts/fetch.py "<url>" - ``` - - Useful flags: - - `--output-dir <dir>` — override the `references/` root. - - `--slug <name>` — override the auto-derived slug. - - The script prints a JSON summary with `slug`, `output_dir`, `image_count`, - `formula_count`, and `final_url` (after redirects). - -4. **Fill in the INDEX.md scaffold.** The script leaves three sections as - `<!-- TODO -->`: - - a. **Summary** — 3–5 sentences answering "what does this page cover and - why would a coding agent consult it?". Base it on the actual content, - not the title alone. Read `content.md` first. - - b. **Key concepts** — bullet list of identifiers an agent would grep - for. **Each bullet must start with a `` `backtick-quoted` `` - identifier** — this is a hard format requirement so that - `scripts/update_concepts.py` (in `pdf-to-knowledge`) can aggregate - concepts across web and PDF sources under unified headers. Skip the - section entirely if the page has no greppable terminology. - - c. **External links** — optional free-form list of adjacent references - the page links to. Especially useful for Wikipedia / nLab articles - that contain "See also" sections pointing at related concepts: list - them here so a future agent knows what to ingest next. Two formats - are acceptable: - ``` - - [Modular conjugation](https://en.wikipedia.org/wiki/Modular_conjugation) — J operator on standard form - - [KMS state](https://ncatlab.org/nlab/show/KMS+state) — companion thermodynamic condition - ``` - or a single paragraph. Skip if the page has no meaningful outbound links. - - d. **Mathlib / local cross-reference** — same technique as - `pdf-to-knowledge` step 4d. For each Key concepts bullet, pick the - strongest tier that applies: - - - `→ formalized: QuantumSystem.X.y` if the concept is already proven - locally in this repository (verified via `lean_verify`). - - `→ mathlib: Mathlib.X.y` if a `lean_local_search <term>` or - `lean_local_search <author-name>` hit verifies cleanly. - - `→ needs formalization` if both searches miss. - - Skip entirely if the Lean MCP toolchain is unavailable. - - **Every annotation must pass through the step-6 verification - loop** — do not trust `lean_local_search` hits at face value. - - Keep `INDEX.md` concise (under ~80 lines). - -5. **Refresh the concept index (strict mode).** Call - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/update_concepts.py \ - --strict <references-root> - ``` - - (The script lives in `pdf-to-knowledge` — it is source-agnostic and - indexes any `<slug>/INDEX.md` under the root.) `--strict` fails with - exit 1 if any Key concepts bullet does not start with a - `` `backtick-quoted` `` identifier. Fix the offending bullets and - re-run; never leave format drift unfixed. - -6. **Verify every mathlib annotation** (only when Lean MCP tools are - available). Run - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py \ - extract <references-root> --output /tmp/mathlib-worklist.json - ``` - - Feed each candidate symbol to `mcp__lean-lsp__lean_verify`. Record - every item whose candidates all fail into a `failed.json`, then: - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py \ - mark-unverified <references-root> --from /tmp/failed.json - ``` - - This appends `[UNVERIFIED]` markers so `gap-filler` treats the - concepts as `suspect` rather than trusting the raw search hit, and - stamps every INDEX.md with a `mathlib_verified` timestamp in its - frontmatter. **Always run `mark-unverified`, even with an empty - `failed.json`** — the empty case is the happy path but still needs - to stamp the tree. Finish with - - ```bash - python3 .claude/skills/pdf-to-knowledge/scripts/verify_mathlib_refs.py \ - check-done <references-root> - ``` - - It exits 0 only when every INDEX.md carries a fresh stamp; treat - a non-zero exit as a hard failure. Re-run step 5 after patching so - `CONCEPTS.md` reflects the markers. - -7. **Report back to the user.** Tell them the `INDEX.md` path, the - formula/image counts, and whether any mathlib matches surfaced - (and how many were downgraded to `[UNVERIFIED]` in step 6). Do not - paste the full summary into chat — they can read the file. - -## Why this design - -- **Share the output shape with `pdf-to-knowledge`.** A single - `references/<slug>/` convention and a single `CONCEPTS.md` across both - skills means an agent does not have to know whether a concept came from - a paper or a wiki — only that it was ingested. -- **Preserve LaTeX from HTML.** BeautifulSoup lets us surgically unwrap - MediaWiki's `<span class="mwe-math-element">` (which bundles MathML + - fallback image + `{\displaystyle ...}` annotation) into plain - `$...$`/`$$...$$` LaTeX. MathJax `<script>` tags and nLab's iTeX-rendered - `<math>` are handled similarly. A single-pass approach with - `trafilatura` silently drops every math element, so we skip it. -- **No full-text summarisation in the script.** Same rationale as - `pdf-to-knowledge`: Claude fills Summary / Key concepts / Mathlib - annotations because those are judgement calls that a regex cannot make - correctly. -- **GPL-free dependencies.** `beautifulsoup4`, `markdownify`, `lxml` are - all MIT/BSD. - -## Runtime prerequisites - -- **`uv`** on `PATH`. First invocation installs `beautifulsoup4`, - `markdownify`, `lxml` into a cached environment (~50 MB). -- **Network access** for both the page fetch and downloaded images. -- No external binaries or OS-level packages are required. - -## Failure modes to watch for - -- **Paywalled / login-gated pages.** The fetcher sends no cookies. If the - page returns HTML that omits the real content (common on Elsevier, - Springer, IEEE), the resulting `content.md` will be mostly nav / abstract. - Prefer the open-access version, or hand the user the paywall URL back. -- **JS-rendered pages.** MathJax rendered purely at client side (with no - `<math>` tag and no `<script type="math/tex">`) will lose formulas. - Wikipedia pages are safe; nLab is safe; some personal blogs and MkDocs - sites are not. Warn the user if `formula_count: 0` on a page you expected - to contain math. -- **arxiv.org URLs.** Route the user to `pdf-to-knowledge` — the arXiv - abstract page is low-signal and the companion PDF is much richer. -- **Image hotlinking failures.** Some CDNs block non-browser user agents. - Individual image failures are logged but the conversion still succeeds - with the original `<img src>` URL left in place for the affected image. diff --git a/.claude/skills/web-to-knowledge/references/output-format.md b/.claude/skills/web-to-knowledge/references/output-format.md deleted file mode 100644 index 42aff66..0000000 --- a/.claude/skills/web-to-knowledge/references/output-format.md +++ /dev/null @@ -1,119 +0,0 @@ -# Output format: `references/<slug>/` - -Per-page layout produced by `scripts/fetch.py`. Matches the -`pdf-to-knowledge` output contract so `update_concepts.py` can treat PDF -and web sources uniformly. - -## Directory layout - -``` -references/<slug>/ -├── INDEX.md # metadata + scaffolded Summary / Key concepts / External links + ToC -├── content.md # full page Markdown with preserved LaTeX math -└── assets/ # downloaded images referenced from content.md (only present when the page has images) -``` - -Exactly `INDEX.md` and `content.md` always exist. `assets/` is created -only when at least one image was downloaded. - -## Slug derivation - -- Wikipedia (`en.wikipedia.org/wiki/<Title>`) → - `wiki-<kebab-title>`. Hyphens and underscores unify, en-dashes collapse - (e.g. `Tomita–Takesaki_theory` → `wiki-tomita-takesaki-theory`). -- nLab (`ncatlab.org/nlab/show/<title>`) → `nlab-<kebab-title>` - (e.g. `relative+entropy` → `nlab-relative-entropy`). -- Everything else → `web-<basehost>-<path-basename>` - (e.g. `docs.python.org/3/library/pathlib.html` → - `web-python-pathlib`). -- `--slug` flag always wins. - -## `INDEX.md` structure - -```markdown ---- -title: "Page title" -source: "<original-url-given-to-the-tool>" -final_url: "<final-url-after-redirects>" -slug: <str> -assets: <int> -formulas: <int> # count of LaTeX math spans recovered from the HTML ---- - -# <title> - -## Summary -<!-- 3-5 sentences, filled in by Claude --> - -## Key concepts -<!-- bullet list; each bullet MUST start with `backtick-quoted` identifier --> - -## External links -<!-- optional: adjacent articles / related pages an agent may want to ingest next --> - -## Contents -- [Full content](content.md) - - [<H2 title>](content.md#<anchor>) - - ... -``` - -Frontmatter is plain YAML — machine-readable. The body is -human/agent-readable navigation. `Summary`, `Key concepts`, and -`External links` are scaffolded as `<!-- TODO -->` blocks; Claude replaces -them when filling in the workflow step 4. - -### ToC - -Always `- [Full content](content.md)` followed by indented entries for the -page's `##`/`###` headings (up to 30) with GitHub-style anchor fragments -(`content.md#foo-bar`). - -## Math handling - -- `<span class="mwe-math-element">` (Wikipedia wrapper): the MathML child - is parsed for `<annotation encoding="application/x-tex">TeX</annotation>` - or `alttext="TeX"`. The whole span — including the fallback `<img>` — - collapses to a single `$...$` / `$$...$$`. -- `<math display="block">` and plain `<math>` (nLab, some pages): same - parsing. -- `<img class="mwe-math-fallback-image-{inline,display}" alt="TeX">`: - handled as a last resort (for pages that do not inline MathML). -- MathJax `<script type="math/tex">...</script>` and - `<script type="math/tex; mode=display">...</script>`: extracted. -- `{\displaystyle ... }` wrappers injected by Wikipedia's MathML renderer - are unwrapped so the emitted LaTeX reads naturally. - -If a page appears to render math entirely client-side (no `<math>` / no -`math/tex` scripts), the `formulas` count drops to 0; the conversion still -succeeds for the prose but formulae are lost. - -## Image handling - -- Every `![alt](src)` in the generated Markdown triggers a fetch. `src` is - URL-joined to the page's final URL to resolve relative links. -- Images save to `assets/<slugified-stem>.<ext>` (max 60 chars). Collisions - are disambiguated with `-1`, `-2`, etc. -- Inline `data:` URIs are dropped — they usually represent small icons - (bullets, separators) that agents do not care about. -- Supported extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.webp`. - Anything else is written with `.img` extension and still referenced, - but agents should check content-type manually. - -Image download failures log a warning and leave the original `src` URL in -place, so the conversion never aborts mid-run because of a dead CDN. - -## Cross-document concept index - -`web-to-knowledge` does **not** ship its own `update_concepts.py`. The -`pdf-to-knowledge` script is source-agnostic — it scans every -`<slug>/INDEX.md` under the references root regardless of whether the -slug originated from a PDF or a web page. Run it once after any new -ingestion: - -```bash -python3 .claude/skills/pdf-to-knowledge/scripts/update_concepts.py references/ -``` - -The resulting `references/CONCEPTS.md` groups bullets from papers and -wikis under unified concept headers, which is the whole point of the -shared output shape. diff --git a/.claude/skills/web-to-knowledge/scripts/fetch.py b/.claude/skills/web-to-knowledge/scripts/fetch.py deleted file mode 100755 index 6bdf3ef..0000000 --- a/.claude/skills/web-to-knowledge/scripts/fetch.py +++ /dev/null @@ -1,481 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.10" -# dependencies = [ -# "beautifulsoup4>=4.12", -# "markdownify>=0.14", -# "lxml>=5.0", -# ] -# /// -"""Fetch a web page and save it as agent-friendly Markdown + image assets. - -Mirrors the output layout of ``pdf-to-knowledge``'s ``convert.py`` so that -``scripts/update_concepts.py`` (from that skill) aggregates concepts across -PDF and web sources into a single ``references/CONCEPTS.md``. - -Produces under ``<output-dir>/<slug>/``: - -- ``INDEX.md`` Metadata, ToC, scaffolded Summary / Key concepts / bibliography - sections that the caller (Claude) fills in after reading - ``content.md``. -- ``content.md`` Full page Markdown with preserved LaTeX math (``$...$`` / - ``$$...$$``) and relative image links. -- ``assets/`` Downloaded images referenced from ``content.md``. - -Design choices: -- HTML parsing uses BeautifulSoup + markdownify (both permissive licenses). - ``trafilatura`` was considered for content extraction but it strips - ``<math>`` elements silently; doing the cleanup ourselves lets us preserve - LaTeX via ``alt`` attributes and ``annotation encoding="application/x-tex"``. -- No section splitting: web pages do not have a natural page count; agents - jump via the H2/H3 anchors in the INDEX.md ToC instead. - -Usage: - uv run fetch.py <url> [--output-dir DIR] [--slug SLUG] -""" - -from __future__ import annotations - -import argparse -import json -import re -import sys -import urllib.parse -import urllib.request -from pathlib import Path -from typing import Iterable - - -def slugify(text: str) -> str: - text = text.lower().strip() - # Treat connectors as word separators BEFORE stripping non-word chars so - # nLab titles like "relative+entropy" stay "relative-entropy" and - # Wikipedia titles like "Tomita–Takesaki_theory" (with en-dash) keep - # the split. - text = re.sub(r"[\s_+\-\u2010-\u2015]+", "-", text) - text = re.sub(r"[^\w-]", "", text, flags=re.UNICODE) - text = re.sub(r"-+", "-", text) - return text[:80].strip("-") or "page" - - -def derive_slug(url: str) -> str: - """Pick a descriptive slug based on known hosts and URL path.""" - parsed = urllib.parse.urlparse(url) - host = parsed.netloc.lower() - path = urllib.parse.unquote(parsed.path) - - # Wikipedia: https://en.wikipedia.org/wiki/<Title> - m = re.match(r"^/wiki/([^/?#]+)$", path) - if m and "wikipedia.org" in host: - return f"wiki-{slugify(m.group(1))}" - - # nLab: https://ncatlab.org/nlab/show/<title> - m = re.match(r"^/nlab/show/(.+)$", path) - if m and "ncatlab.org" in host: - return f"nlab-{slugify(m.group(1))}" - - # Generic: host-path slug - base_host = host.split(".")[-2] if "." in host else host - tail = Path(path).name or Path(path).stem or "index" - return f"web-{base_host}-{slugify(tail)}" - - -def fetch_html(url: str) -> tuple[str, str]: - """Download HTML. Returns (html_text, final_url) — the URL may redirect.""" - req = urllib.request.Request( - url, - headers={ - "User-Agent": "web-to-knowledge/1.0 (+https://github.com/anthropics/claude-code)" - }, - ) - with urllib.request.urlopen(req, timeout=60) as resp: - final_url = resp.url - raw = resp.read() - charset = resp.headers.get_content_charset() or "utf-8" - return raw.decode(charset, errors="replace"), final_url - - -def find_main_content(soup): - """Locate the substantive body of the page using common conventions.""" - for selector in [ - "main", - "article", - "[role=main]", - ".mw-parser-output", - "#mw-content-text", - "#content", - ]: - found = soup.select_one(selector) - if found: - return found - return soup.body or soup - - -def strip_non_content(root) -> None: - """Remove navigation, footers, scripts, styles, and known sidebars.""" - noisy_tags = ["script", "style", "nav", "footer", "aside", "noscript", "iframe"] - for tag in root.find_all(noisy_tags): - tag.decompose() - # Wikipedia / nLab specific noise. - noise_class_patterns = [ - r"mw-editsection", - r"mw-cite-backlink", - r"navbox", - r"navigation-not-searchable", - r"reference-list-only", - r"printfooter", - r"toc\b", - r"sidebar", - r"noprint", - r"catlinks", - ] - compiled = re.compile("|".join(noise_class_patterns)) - for tag in root.find_all(class_=compiled): - tag.decompose() - for tag in root.find_all(id=re.compile(r"^(toc|siteSub|contentSub|footer|mw-navigation|catlinks|jump-to-nav)$")): - tag.decompose() - - -_MATHML_TEX_ENCODING = re.compile(r"x-tex|TeX", re.IGNORECASE) -_DISPLAYSTYLE_WRAPPER = re.compile(r"^\s*\{\\displaystyle\s+(.+)\}\s*$", re.DOTALL) - - -def _strip_displaystyle(tex: str) -> str: - """Unwrap ``{\\displaystyle ...}`` added by Wikipedia MathML rendering.""" - tex = tex.strip() - m = _DISPLAYSTYLE_WRAPPER.match(tex) - if m: - inner = m.group(1).strip() - # Only unwrap when braces balance in the inner body, to avoid breaking - # LaTeX with intentional outer braces. - if inner.count("{") == inner.count("}"): - return inner - return tex - - -def _extract_tex_from_math(math) -> str | None: - ann = math.find("annotation", attrs={"encoding": _MATHML_TEX_ENCODING}) - if ann and ann.get_text(strip=True): - return ann.get_text(strip=True) - if math.get("alttext"): - return math["alttext"].strip() - return None - - -def preserve_math_tags(root, NavigableString) -> int: - """Rewrite common math embeddings to inline/display LaTeX. - - Returns the number of formulas found. Handles: - - - Wikipedia wrapper ``<span class="mwe-math-element">`` containing both a - ``<math>`` and a fallback ``<img>`` — collapsed to a single LaTeX span. - - Bare ``<math>`` with ``<annotation encoding="application/x-tex">TeX</annotation>`` - or ``alttext="TeX"`` (nLab, MediaWiki without the wrapper). - - Bare ``<img class="mwe-math-fallback-image-*" alt="TeX">`` without the - wrapper span. - - MathJax ``<script type="math/tex">...</script>`` / - ``<script type="math/tex; mode=display">``. - - ``{\\displaystyle ...}`` wrappers that Wikipedia injects around every - inline MathML expression are unwrapped so the emitted LaTeX reads - naturally. - """ - count = 0 - - # 1. Wikipedia math-element wrappers — unify the MathML + image fallback. - for span in root.find_all("span", class_="mwe-math-element"): - math = span.find("math") - tex = _extract_tex_from_math(math) if math else None - if not tex: - img = span.find("img", class_=re.compile(r"mwe-math-fallback-image")) - if img and img.get("alt"): - tex = img["alt"].strip() - if not tex: - continue - classes = span.get("class") or [] - display = any("mwe-math-element" in c and "display" in c for c in classes) - if not display and math is not None: - display = math.get("display") == "block" - tex = _strip_displaystyle(tex) - wrapped = f"$$\n{tex}\n$$" if display else f"${tex}$" - span.replace_with(NavigableString(wrapped)) - count += 1 - - # 2. Bare MathML elements not wrapped by MediaWiki. - for math in root.find_all("math"): - tex = _extract_tex_from_math(math) - if not tex: - continue - display = math.get("display") == "block" - tex = _strip_displaystyle(tex) - wrapped = f"$$\n{tex}\n$$" if display else f"${tex}$" - math.replace_with(NavigableString(wrapped)) - count += 1 - - # 3. Bare Wikipedia fallback images. - for img in root.find_all("img", class_=re.compile(r"mwe-math-fallback-image")): - alt = img.get("alt", "").strip() - if not alt: - continue - classes = img.get("class") or [] - display = any("display" in c for c in classes) - alt = _strip_displaystyle(alt) - wrapped = f"$$\n{alt}\n$$" if display else f"${alt}$" - img.replace_with(NavigableString(wrapped)) - count += 1 - - # 4. MathJax scripts. - for script in root.find_all( - "script", attrs={"type": re.compile(r"math/tex", re.IGNORECASE)} - ): - tex = script.get_text().strip() - if not tex: - continue - type_attr = script.get("type") or "" - display = "display" in type_attr.lower() - tex = _strip_displaystyle(tex) - wrapped = f"$$\n{tex}\n$$" if display else f"${tex}$" - script.replace_with(NavigableString(wrapped)) - count += 1 - - return count - - -def download_image(url: str, dest: Path) -> bool: - """Fetch ``url`` to ``dest``. Returns True on success, False otherwise.""" - try: - req = urllib.request.Request( - url, headers={"User-Agent": "web-to-knowledge/1.0"} - ) - with urllib.request.urlopen(req, timeout=30) as resp: - data = resp.read() - dest.write_bytes(data) - return True - except Exception as exc: - print(f"[warn] image download failed: {url} ({exc})", file=sys.stderr) - return False - - -def safe_image_name(url: str) -> str: - """Derive a filename from an image URL, preserving the extension.""" - parsed = urllib.parse.urlparse(url) - raw = Path(urllib.parse.unquote(parsed.path)).name or "image" - # Collapse overly long / weird chars; keep extension. - stem = Path(raw).stem - suffix = Path(raw).suffix.lower() - if suffix not in {".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"}: - suffix = ".img" - return slugify(stem)[:60] + suffix - - -def collect_and_rewrite_images( - md_text: str, base_url: str, assets_dir: Path -) -> tuple[str, int]: - """Download ``![...](src)`` images to ``assets/`` and rewrite refs. - - Returns the rewritten Markdown and the number of images saved. - """ - pattern = re.compile(r"!\[([^\]]*)\]\(([^)\s]+)(?:\s+\"[^\"]*\")?\)") - seen: dict[str, str] = {} - saved = 0 - - def replace(match: re.Match) -> str: - nonlocal saved - alt = match.group(1) - src = match.group(2) - if src.startswith("data:"): - return "" # drop inline data URIs - if src in seen: - return f"![{alt}](assets/{seen[src]})" - abs_url = urllib.parse.urljoin(base_url, src) - name = safe_image_name(abs_url) - # Avoid collisions with an incrementing counter. - candidate = name - n = 1 - while (assets_dir / candidate).exists(): - candidate = f"{Path(name).stem}-{n}{Path(name).suffix}" - n += 1 - assets_dir.mkdir(parents=True, exist_ok=True) - if download_image(abs_url, assets_dir / candidate): - seen[src] = candidate - saved += 1 - return f"![{alt}](assets/{candidate})" - return f"![{alt}]({src})" # keep original on failure - - return pattern.sub(replace, md_text), saved - - -def extract_page_title(soup, fallback: str) -> str: - for selector in ["h1.firstHeading", "h1", "title"]: - el = soup.select_one(selector) - if el and el.get_text(strip=True): - return el.get_text(strip=True) - return fallback - - -# Common LaTeX control sequences that survive markdownify only when the page -# already served inline ``$...$`` text (rare) or when MathJax rendered to -# HTML without an accompanying ``<math>`` / ``<script type=math/tex>`` node. -# Seeing many of these with ``formula_count == 0`` strongly suggests the -# page uses JS-only rendering and our math preservation layer missed it. -_MATH_HINTS = re.compile( - r"\\(?:log|frac|sum|int|prod|sqrt|alpha|beta|gamma|delta|sigma|tau|phi|psi|lambda|rho|Omega|Delta|partial|times|cdot|mapsto|rightarrow|Rightarrow|forall|exists|mathbb|mathcal|mathrm|operatorname|ldots|cdots)\b" -) - - -def diagnose_math_loss(md_text: str, formula_count: int) -> str | None: - """Return a warning string when formulas appear to be silently lost. - - Heuristic: fetch.py preserved zero math blocks, but the body still - contains many raw LaTeX control sequences. This is the - pattern you get from a page whose math was rendered purely by - client-side MathJax without a paired ``<math>`` or - ``<script type=math/tex>`` element — our server-side HTML parsing - cannot see the result, so nothing gets wrapped in ``$...$``. - """ - if formula_count > 0: - return None - hits = len(_MATH_HINTS.findall(md_text)) - if hits < 5: - return None - return ( - f"{hits} LaTeX-like tokens (e.g. \\log, \\frac, \\sum) appear in the " - "markdown but formula_count is 0 — the page likely renders math " - "entirely client-side. Formulas will NOT round-trip as LaTeX; " - "treat the extracted content.md as lossy for equations." - ) - - -def _heading_level(line: str) -> int: - m = re.match(r"^(#{1,6}) \S", line) - return len(m.group(1)) if m else 0 - - -def _github_anchor(heading: str) -> str: - slug = heading.strip().lower() - slug = re.sub(r"[^\w\s-]", "", slug, flags=re.UNICODE) - slug = re.sub(r"\s+", "-", slug) - return slug.strip("-") - - -def _build_toc(md: str, max_entries: int = 30) -> list[str]: - lines = ["- [Full content](content.md)"] - count = 0 - for line in md.splitlines(): - m = re.match(r"^(##{1,2}) (.+)", line) - if not m: - continue - level = len(m.group(1)) - title = m.group(2).strip() - indent = " " if level == 2 else " " - anchor = _github_anchor(title) - lines.append(f"{indent}- [{title}](content.md#{anchor})") - count += 1 - if count >= max_entries: - break - return lines - - -def main() -> int: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("url", help="URL of the page to fetch") - parser.add_argument( - "--output-dir", - default="references", - help="Parent directory; output goes under <output-dir>/<slug>/", - ) - parser.add_argument("--slug", default=None, help="Override the auto-derived slug") - args = parser.parse_args() - - # Lazy imports so --help works without deps installed. - from bs4 import BeautifulSoup, NavigableString - from markdownify import markdownify - - print(f"[info] Fetching {args.url}", file=sys.stderr) - html_text, final_url = fetch_html(args.url) - slug = args.slug or derive_slug(final_url) - output_root = Path(args.output_dir).expanduser().resolve() - output_dir = output_root / slug - output_dir.mkdir(parents=True, exist_ok=True) - - soup = BeautifulSoup(html_text, "lxml") - title = extract_page_title(soup, slug.replace("-", " ").title()) - - article = find_main_content(soup) - # Work on a fresh parsed copy to avoid mutating the original for title lookup. - article_soup = BeautifulSoup(str(article), "lxml") - strip_non_content(article_soup) - formula_count = preserve_math_tags(article_soup, NavigableString) - print(f"[info] Preserved {formula_count} math blocks", file=sys.stderr) - - md_text = markdownify( - str(article_soup), - heading_style="ATX", - escape_asterisks=False, - escape_underscores=False, - strip=["a"] if False else None, # keep links by default - ) - md_text = re.sub(r"\n{3,}", "\n\n", md_text).strip() + "\n" - - assets_dir = output_dir / "assets" - md_text, image_count = collect_and_rewrite_images(md_text, final_url, assets_dir) - - warning = diagnose_math_loss(md_text, formula_count) - if warning: - print(f"[warn] {warning}", file=sys.stderr) - - (output_dir / "content.md").write_text(md_text, encoding="utf-8") - - toc_lines = _build_toc(md_text) - index = [ - "---", - f"title: {json.dumps(title, ensure_ascii=False)}", - f"source: {json.dumps(args.url, ensure_ascii=False)}", - f"final_url: {json.dumps(final_url, ensure_ascii=False)}", - f"slug: {slug}", - f"assets: {image_count}", - f"formulas: {formula_count}", - "---", - "", - f"# {title}", - "", - "## Summary", - "", - "<!-- TODO: 3-5 sentence summary of what this page covers and why a coding", - " agent would consult it. Fill in after reading content.md. -->", - "", - "## Key concepts", - "", - "<!-- TODO: Bullet list of identifiers an agent would grep for. Each bullet", - " MUST start with a `backtick-quoted` identifier — this drives the", - " cross-document concept index shared with pdf-to-knowledge. Omit the", - " section if the page has no greppable terminology. -->", - "", - "## External links", - "", - "<!-- TODO: Optional — list related pages the current one links out to", - " (especially other Wikipedia / nLab articles an agent may want to", - " ingest next). Free-form; not a strict YAML schema. -->", - "", - "## Contents", - "", - *toc_lines, - "", - ] - (output_dir / "INDEX.md").write_text("\n".join(index), encoding="utf-8") - - summary = { - "slug": slug, - "output_dir": str(output_dir), - "index_path": str(output_dir / "INDEX.md"), - "image_count": image_count, - "formula_count": formula_count, - "final_url": final_url, - "math_loss_warning": warning, - } - print(json.dumps(summary, indent=2, ensure_ascii=False)) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7cb3f10..4482db0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,9 +17,31 @@ }, "ghcr.io/thamaji/devcontainer-features/gitleaks:latest": {} }, - "onCreateCommand": "rm -rf .lake/ && lake exe cache get && npm ci", + // Both cache volumes are created root-owned by Docker, so chown them before + // anything writes: without this MinerU dies at model download with + // "[Errno 13] Permission denied: '/home/vscode/.cache/huggingface/hub'". + "onCreateCommand": "sudo chown -R vscode:vscode /home/vscode/.cache/uv /home/vscode/.cache/huggingface && rm -rf .lake/", + "postCreateCommand": "lake exe cache get && uv sync && uv run lefthook install", + // Pin where MinerU fetches models from. Its default (`auto`) probes + // HuggingFace and silently falls back to ModelScope, so the download origin + // depends on the network. The env var outranks ~/mineru.json, which MinerU + // rewrites itself after every successful download. + "containerEnv": { + "UV_LINK_MODE": "copy", + "UV_CACHE_DIR": "/home/vscode/.cache/uv", + "MINERU_MODEL_SOURCE": "huggingface" + }, + // Pass the host GPU through (Docker Desktop bundles the NVIDIA runtime). + // If the container fails to start on a machine without a GPU, drop this line. + "runArgs": [ + "--gpus", + "all" + ], "mounts": [ - "source=lean-project-commandhistory,target=/commandhistory,type=volume" + "source=lean-project-commandhistory,target=/commandhistory,type=volume", + // MinerU models (~1.5-4GB) and uv's wheel cache (torch ~2GB) survive rebuilds. + "source=hf-models,target=/home/vscode/.cache/huggingface,type=volume", + "source=uv-cache,target=/home/vscode/.cache/uv,type=volume" ], // Configure tool-specific properties. "customizations": { diff --git a/.gitignore b/.gitignore index b88588e..5eca900 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.lake +.venv/ node_modules/ lefthook-local.yml @@ -8,3 +9,5 @@ skill-creator-tests/ # Ignore until https://github.com/microsoft/vscode-remote-release/issues/11616 is fixed. devcontainer-lock.json + +worktrees/ diff --git a/.npmrc b/.npmrc deleted file mode 100644 index cffe8cd..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -save-exact=true diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/AGENTS.md b/AGENTS.md index 213623d..bc97b67 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,11 +6,19 @@ - `QuantumSystem.lean` — aggregate root that re-exports every module. - `scripts/mk_all.lean` — regenerates the aggregate. - `lakefile.toml`, `lean-toolchain`, `lake-manifest.json` — pinned toolchain and manifest. +- `docs/math/` — extraction notes: what the literature says about an object, written + before its Lean. Mathematics only, no Lean. Produced by `/math-extract`. ## Working Principles **Think before coding.** - Read the target file and its importers before editing. +- Before designing a new object, check `docs/math/README.md` for an extraction + note on it and read the note if one exists — its adopted general form is the + statement the Lean is meant to realise, and its `## Hypotheses` table already + separates the provable from the genuinely model-dependent. A note is + mathematics, not a design: it never says how to define anything in Lean, and + no note is a normal state, not a blocker. - State your assumptions about the goal, the existing lemmas, and the proof skeleton before typing tactics. Capture the goal with `lean_goal` rather than guessing the shape from the file context. @@ -21,27 +29,57 @@ - When stuck on a goal, search closing lemmas with `lean_state_search` / `lean_hammer_premise`, then verify with `lean_multi_attempt` before editing. -- If Lean reports `expected '{' or indented tactic sequence`, fix indentation - first — almost always a whitespace issue, not a tactic bug. - **Why:** tactics that compile by accident can mask unsoundness; this - project mandates a fully axiom-free codebase. - -**Simplicity first.** -- Formalize only what the current task requires. No speculative - generalizations, no helper lemmas "for later", no premature abstraction - across `CStarAlgebra` / `StarAlgebra` / `NormedAlgebra`. -- Prefer the most direct proof that closes the goal over the cleverest one. - If `simp` / `linarith` / `aesop` suffices, do not unfold by hand. - **Why:** every extra declaration is surface area to maintain and to keep - axiom-free; speculative API decays faster than it earns interest. - -**Surgical changes.** -- Edit only files demanded by the task. Resist opportunistic renames, - whitespace fixes, or namespace reshuffles in unrelated proofs. -- Do not rewrite existing proofs that already compile. If a proof is ugly - but correct, leave it; flag it in review rather than touching it. - **Why:** Mathlib-style review is line-noise sensitive, and unrelated edits - break `git blame` and inflate merge conflicts. + +**Abstraction first.** +- Build the general interface before the concrete model — even when the source + literature treats only a specific case. Introduce the abstract structure and + the mathematically conventional form from the *first* commit, not as a later + refactor, then instantiate the concrete model the task needs. +- Do not specialise to a concrete model because the + immediate task uses only that case, and do not weaken hypotheses to + match whatever fragment Mathlib currently has the most lemmas for — + follow the literature, even when it forces you to build supporting + API that Mathlib does not yet provide. + +**Do not bridge what should be unified.** +- When Mathlib already provides an object — a type copy, a topology, a + structure — use it. Do not reimplement it locally and then paper over the + mismatch with a conversion lemma, an `Equiv`, or a `Homeomorph` between the + two copies. +- If such a local reimplementation already exists, migrate to the Mathlib + object and delete the local one. Do not add a bridge to keep both alive. +- The same applies to two local spellings of one notion (image vs preimage + form, bundled vs unbundled): pick one and state every result in it. +- A conversion lemma is acceptable only when both sides are genuinely outside + your control — both already in Mathlib, or the local object carries structure + the Mathlib one cannot. + **Why:** a bridge makes the duplication permanent. Every later lemma must + then pick a side and be transported across, and a `ForMathlib/` copy of + something Mathlib already has can never be upstreamed — which is the only + reason that directory exists. + +**Prove what is provable; do not *defer* it.** +- Do not introduce a `class` / `structure` field (or a `def … : Prop` + hypothesis) that stands in for a theorem when that theorem has a known + mathematical proof — *even when Mathlib lacks the supporting lemmas, and + even when proving it is out of scope for the current change.* +- A hypothesis class is acceptable **only** for genuinely model-dependent + inputs that are false for some objects in the class *and* for which no known + universal proof exists. + **Why:** deferred hypotheses became permanent here. Once a + `Has…` field is wired in, discharging it later costs far more than proving it + up front and the trusted base grows silently. Restricting hypotheses to the + irreducible (a) inputs — and proving or descoping everything else — is what + keeps that base bounded. + +**Match the code to the docs, not the docs to the code.** +- When a docstring or module comment claims more than the code actually + establishes, raise the code to meet the claim — strengthen the statement, + discharge the missing hypothesis, or generalise the definition. Do not + weaken the documentation to match a thinner implementation. + **Why:** the documentation records the *intended* theorem; trimming it to + match a shortfall silently shrinks the goal and hides the gap instead of + closing it. **Goal-driven verification (Definition of Done).** - A change is done only when `lake build` completes with no new errors or @@ -53,65 +91,37 @@ - When a tactic fails to close a goal, do not stack `try` / `<;>` to silence the error — re-inspect the goal with `lean_goal` and address the actual mismatch. -- Never report a task as successful until the above checks pass. - **Why:** "looks right" is not a soundness gate; the kernel is, and - downstream modules can still break even when the edited file type-checks - in isolation. - -## Editing Hygiene - -- Spaces only, never tabs. - **Why:** Mathlib style; mixed whitespace breaks `lake exe runLinter`. -- Never modify `lakefile.toml`, `lean-toolchain`, or `lake-manifest.json`. - **Why:** the toolchain and manifest are pinned intentionally; accidental edits cascade into reproducibility failures. -- Write comments in English. -- Never create namespaces or sections named `QuantumSystem`. - **Why:** the module path already prefixes every declaration; an extra namespace would produce `QuantumSystem.QuantumSystem.Foo`. - -## Prohibited Tokens - -The following tokens are strictly prohibited, grouped by reason. - -- *Unsound or deferred proofs:* `sorry`, `admit`, `axiom`. - **Why:** the project targets a fully axiom-free formalization; assumptions smuggled into structure fields count as axioms too. -- *Global configuration and unsafe code:* `set_option`, `unsafe`. - **Why:** these mutate kernel or elaborator behavior project-wide, or bypass soundness. -- *Compiler and metaprogramming internals:* `System`, `open System`, `Lean.Elab`, `Lean.Meta`, `Lean.Compiler`. - **Why:** this is a mathematics repository, not a tactic-library repository; depending on internals creates brittle code. - -## Commit Style - -`lefthook` + `commitlint` enforce this; the accepted vocabulary is: - -- Conventional Commits: `feat` / `fix` / `chore` / `docs` / `refactor` / `test` / `perf`. -- Lowercase type, colon, imperative subject. Example: `feat: add GNS faithfulness lemma`. -- One logical change per commit. -## Style Guidelines +## Plan Mode & Responses -The Mathlib contribute templates are authoritative; the bullets below distill what actually comes up during edits. +**In plan mode, a question deserves an answer — not a plan.** +- When the user asks a question under plan mode, reply with the + AskUserQuestion tool and answer *only* what was asked. Do not invent a + problem statement or start drafting a plan the user never requested. -**Naming.** -- `lowerCamelCase` for terms and definitions (`gnsRepresentation`, `isPureState`). -- `UpperCamelCase` for types, structures, and propositions (`CStarAlgebra`, `IsState`). -- Theorem names use `_` as word separator (`norm_add_le`, `inner_self_nonneg`). -- Prefer the `_of_` pattern for implications (`continuous_of_lipschitz`); `iff` joins equivalences; `not_` prefixes negations. +**Numbered steps first, prose second.** +- Do not narrate a whole plan in prose. Lead with the concrete steps as a + numbered list, then add brief supplementary notes after it. + **Why:** the reader has ADHD; a response that demands sustained attention + to parse is a response that does not get read. Keep it scannable. -**Layout.** -- 100-column line limit. -- 2-space indentation; `by` stays on the same line as the goal it opens unless the resulting line would exceed the limit. -- Hoist shared hypotheses into `variable` blocks; keep explicit/implicit arity consistent with sibling lemmas. -- Align `calc` steps on the relation; use `·` (centered dot) for focused goals, not `case _ =>`. +**Do not force a plan that does not fit.** +- If the request is judged infeasible or inappropriate to turn into an + implementation plan, do not force one. State the grounds for that + judgement, then offer alternative approaches that are viable for the + current codebase, or point to references worth investigating further. -**Docstrings.** -- Every public declaration gets a `/-- ... -/` docstring whose first sentence is a self-contained summary. -- Module docs (`/-! # Title ... -/`) at the top of each file describe the content and any non-obvious conventions. +## Lint Rules -**References** (fetch when a rule above is ambiguous): +Editing hygiene, prohibited tokens, commit style, and style guidelines live in +`.claude/skills/lint-fix/references/rules.md`. Read that file when a hygiene or +style question arises while editing — it is the authoritative statement of the +rules and stands on its own. -- https://github.com/leanprover-community/leanprover-community.github.io/blob/lean4/templates/contribute/doc.md -- https://github.com/leanprover-community/leanprover-community.github.io/blob/lean4/templates/contribute/naming.md -- https://github.com/leanprover-community/leanprover-community.github.io/blob/lean4/templates/contribute/style.md +To fix a change rather than consult the rules, run `/lint-fix`: it applies the +safe fixes, verifies with `lake build`, and reports what it deliberately left +alone. It writes to the working tree, so it runs only when you ask for it by +name. ## Source of Truth diff --git a/QuantumSystem.lean b/QuantumSystem.lean index 2f72535..94b6104 100644 --- a/QuantumSystem.lean +++ b/QuantumSystem.lean @@ -5,52 +5,78 @@ public import QuantumSystem.Algebra.CStarAlgebra.GNS.Construction public import QuantumSystem.Algebra.CStarAlgebra.GNS.DirectSum public import QuantumSystem.Algebra.CStarAlgebra.GNS.PureState public import QuantumSystem.Algebra.CStarAlgebra.GNS.Representation +public import QuantumSystem.Algebra.CStarAlgebra.GNS.Separable public import QuantumSystem.Algebra.CStarAlgebra.PureState public import QuantumSystem.Algebra.CStarAlgebra.QuasiState +public import QuantumSystem.Algebra.CStarAlgebra.Representation +public import QuantumSystem.Algebra.CStarAlgebra.Representation.Conjugation +public import QuantumSystem.Algebra.CStarAlgebra.Representation.DirectSum +public import QuantumSystem.Algebra.CStarAlgebra.Representation.Family +public import QuantumSystem.Algebra.CStarAlgebra.Representation.Hom +public import QuantumSystem.Algebra.CStarAlgebra.Representation.Irreducible +public import QuantumSystem.Algebra.CStarAlgebra.Representation.UnitaryEquiv public import QuantumSystem.Algebra.CStarAlgebra.State public import QuantumSystem.Algebra.CStarAlgebra.State.Continuity public import QuantumSystem.Algebra.CStarAlgebra.State.Faithful -public import QuantumSystem.Algebra.LocalNet +public import QuantumSystem.Algebra.LocalNet.Covariance +public import QuantumSystem.Algebra.LocalNet.Examples +public import QuantumSystem.Algebra.LocalNet.Net +public import QuantumSystem.Algebra.LocalNet.QuasiLocalAlgebra +public import QuantumSystem.Algebra.LocalNet.SplitProperty public import QuantumSystem.Algebra.Star.DoubleCommutant.SOTClosedSubAlgebra +public import QuantumSystem.Algebra.Star.DoubleCommutant.TFAE public import QuantumSystem.Algebra.Star.DoubleCommutant.WOTClosedSubAlgebra +public import QuantumSystem.Algebra.VonNeumannAlgebra.Basic +public import QuantumSystem.Algebra.VonNeumannAlgebra.SpatialDecomposition +public import QuantumSystem.Algebra.VonNeumannAlgebra.SplitInclusion +public import QuantumSystem.Algebra.VonNeumannAlgebra.StructureTheorem +public import QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor +public import QuantumSystem.Algebra.VonNeumannAlgebra.TypeI public import QuantumSystem.Analysis.CFC.Diagonal public import QuantumSystem.Analysis.Entropy.KroneckerProduct -public import QuantumSystem.Analysis.Entropy.Regularize +public import QuantumSystem.Analysis.Entropy.MutualInformation public import QuantumSystem.Analysis.Entropy.RelativeEntropy -public import QuantumSystem.Analysis.Entropy.SSA +public import QuantumSystem.Analysis.Entropy.StrongSubadditivity public import QuantumSystem.Analysis.Entropy.VonNeumannEntropy public import QuantumSystem.Analysis.Matrix.Effros public import QuantumSystem.Analysis.Matrix.HermitianFunctionalCalculus public import QuantumSystem.Analysis.Matrix.LiebConcavity public import QuantumSystem.Analysis.Matrix.Order -public import QuantumSystem.Analysis.Matrix.PartialTrace public import QuantumSystem.Analysis.Matrix.Pinching public import QuantumSystem.Analysis.Matrix.PosDef public import QuantumSystem.Channel +public import QuantumSystem.ForMathlib.Algebra.Colimit.DirectLimitStar +public import QuantumSystem.ForMathlib.Algebra.Star.PartialIsometry public import QuantumSystem.ForMathlib.Analysis.Calculus.Deriv.Sign public import QuantumSystem.ForMathlib.Analysis.Complex.Basic +public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.DirectLimit public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.HilbertSpace public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.Ideal public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.NonUnital public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.Unital public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.WeakDual -public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.Adjoint +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.DiagonalAmplification +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.FiniteRank public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.InvariantSubspace -public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.l2Space +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.RankOne public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.TensorProduct +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.TensorProductCompletion public import QuantumSystem.ForMathlib.Analysis.LocallyConvex.StrongOperatorTopology public import QuantumSystem.ForMathlib.Analysis.LocallyConvex.WeakOperatorTopology public import QuantumSystem.ForMathlib.Analysis.Matrix.Basic public import QuantumSystem.ForMathlib.Analysis.Matrix.Hermitian public import QuantumSystem.ForMathlib.Analysis.Matrix.Order -public import QuantumSystem.ForMathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.Abs -public import QuantumSystem.ForMathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.PartialIsometry -public import QuantumSystem.ForMathlib.Analysis.SpecialFunctions.Pow.Real +public import QuantumSystem.ForMathlib.Analysis.Normed.Lp.Separable +public import QuantumSystem.ForMathlib.Analysis.VonNeumannAlgebra.Commutant public import QuantumSystem.ForMathlib.InformationTheory.KullbackLeibler.KLFun +public import QuantumSystem.ForMathlib.LinearAlgebra.Matrix.PartialTrace +public import QuantumSystem.ForMathlib.LinearAlgebra.Matrix.PosDef public import QuantumSystem.ForMathlib.LinearAlgebra.Matrix.StarAlgEquiv public import QuantumSystem.ForMathlib.LinearAlgebra.Span.Def +public import QuantumSystem.ForMathlib.LinearAlgebra.Trace public import QuantumSystem.ForMathlib.RingTheory.RootsOfUnity.Complex +public import QuantumSystem.ForMathlib.Topology.Algebra.CStarCompletion public import QuantumSystem.ForMathlib.Topology.DenseLinear public import QuantumSystem.ForMathlib.Topology.MetricSpace.Completion public import QuantumSystem.Notation diff --git a/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean b/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean index c5b4645..cae69f8 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/GNS/Construction.lean @@ -2,6 +2,7 @@ module public import Mathlib.Analysis.InnerProductSpace.Completion public import Mathlib.Analysis.Normed.Operator.Extend +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.HilbertSpace public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.Ideal public import QuantumSystem.Algebra.CStarAlgebra.State.Continuity @@ -14,6 +15,7 @@ namespace GNS namespace Construction open ComplexConjugate NNReal Topology Filter +open scoped InnerProductSpace Adjoint ComplexHilbertSpace variable {A : Type*} [NonUnitalCStarAlgebra A] variable (ω : State ℂ A) @@ -44,7 +46,8 @@ with respect to the positive sesquilinear form coming from the state `ω` (ultim consequence of the Cauchy–Schwarz inequality for states). -/ noncomputable def innerQuotient (xq yq : A ⧸ Nω) : ℂ := Quotient.liftOn₂' xq yq (fun x y => ω (star x * y)) - (fun x₁ y₁ x₂ y₂ (hx : CStarAlgebraIdeal.leftRel Nω x₁ x₂) (hy : CStarAlgebraIdeal.leftRel Nω y₁ y₂) => by + (fun x₁ y₁ x₂ y₂ (hx : CStarAlgebraIdeal.leftRel Nω x₁ x₂) + (hy : CStarAlgebraIdeal.leftRel Nω y₁ y₂) => by rw [CStarAlgebraIdeal.leftRel, QuotientAddGroup.leftRel_apply] at hx hy change ω (star x₁ * y₁) = ω (star x₂ * y₂) have hx' : ω (star (x₂ - x₁) * (x₂ - x₁)) = 0 := by simp only [sub_eq_neg_add]; exact hx @@ -104,7 +107,7 @@ noncomputable instance instInnerProductSpaceQuot : InnerProductSpace ℂ (A ⧸ `InnerProductSpace` instance on the quotient is built reducibly from the corresponding `InnerProductSpace.Core`. -/ private lemma inner_quotient_eq (x y : A ⧸ Nω) : - @inner ℂ (A ⧸ Nω) _ x y = innerQuotient ω x y := rfl + ⟪x, y⟫_ℂ = innerQuotient ω x y := rfl /-- The squared norm of the class `[x]` equals the real part `Re (ω (star x * x))`. -/ private lemma norm_sq_eq_inner (x : A) : @@ -115,11 +118,6 @@ private lemma norm_sq_eq_inner (x : A) : simp only [Quotient.liftOn₂'_mk''] rfl -/-- Inner products with elements of the kernel ideal vanish: if `s ∈ Nω` then -`ω (star x * s) = 0`. -/ -private lemma inner_kernel_elem_zero (x : A) (s : Nω) : ω (star x * s.val) = 0 := - State.kernel_degenerate_left (ω := ω) (x := s.val) (a := x) s.property - /-- The GNS Hilbert space `Hω`, defined as the completion of the quotient `A ⧸ Nω`. -/ abbrev Hω := UniformSpace.Completion (A ⧸ Nω) local notation "Hω" => (Hω (ω := ω)) @@ -185,7 +183,7 @@ lemma πω'_mul (a b : A) (c : A ⧸ Nω) : πω' ω (a * b) c = πω' ω a (π /-- Adjoint property of the algebraic action: `⟪πω'(a) b, c⟫ = ⟪b, πω'(a*) c⟫`. -/ lemma πω'_inner (a : A) (b c : A ⧸ Nω) : - @inner ℂ (A ⧸ Nω) _ (πω' ω a b) c = @inner ℂ (A ⧸ Nω) _ b (πω' ω (star a) c) := by + ⟪πω' ω a b, c⟫_ℂ = ⟪b, πω' ω (star a) c⟫_ℂ := by refine Quotient.inductionOn₂' b c fun b' c' => ?_ unfold πω' simp only [Quotient.liftOn'_mk''] @@ -218,10 +216,6 @@ lemma πω'_norm_le (a : A) (b : A ⧸ Nω) : ‖πω' ω a b‖ ≤ ‖a‖ * simpa [Real.sqrt_sq (norm_nonneg _), mul_nonneg (norm_nonneg a) (norm_nonneg b)] using Real.sqrt_le_sqrt h_sq -/-- Lipschitz continuity of `b ↦ πω'(a)b` with optimal constant `‖a‖`. -/ -lemma πω'_lipschitz (a : A) : ∃ C : ℝ≥0, ∀ b : A ⧸ Nω, ‖πω' ω a b‖ ≤ C * ‖b‖ := - ⟨⟨‖a‖, norm_nonneg a⟩, fun b => by simpa using πω'_norm_le (ω := ω) a b⟩ - /-- Continuous linear map version of the pre-representation: `πω'(a) : A ⧸ Nω →L[ℂ] A ⧸ Nω`. -/ noncomputable def πω'CLM (a : A) : (A ⧸ Nω) →L[ℂ] (A ⧸ Nω) := LinearMap.mkContinuous @@ -236,7 +230,7 @@ noncomputable def πω'CLM (a : A) : (A ⧸ Nω) →L[ℂ] (A ⧸ Nω) := dense quotient into its Hilbert space completion. -/ noncomputable def πω (a : A) : 𝓑(Hω) := ContinuousLinearMap.extend - (UniformSpace.Completion.toComplL.comp (πω'CLM ω a)) + (UniformSpace.Completion.toComplL ∘L (πω'CLM ω a)) (UniformSpace.Completion.toComplL (𝕜 := ℂ) (E := A ⧸ Nω)) /-- Agreement on the dense subspace: `πω(a) (↑x) = ↑(πω'(a) x)` for `x : A ⧸ Nω`. -/ @@ -260,27 +254,27 @@ lemma πω_mul (a b : A) : πω ω (a * b) = πω ω a ∘L πω ω b := by intro x simp [ContinuousLinearMap.comp_apply, πω_apply_coe, πω'_mul] -/-- *-preservation: `(πω(a)).adjoint = πω (star a)`. -/ -lemma πω_star (a : A) : (πω ω a).adjoint = πω ω (star a) := by +/-- *-preservation: `(πω a)† = πω (star a)`. -/ +lemma πω_star (a : A) : (πω ω a)† = πω ω (star a) := by ext x refine DenseRange.induction_on - (p := fun x => (πω ω a).adjoint x = πω ω (star a) x) + (p := fun x => (πω ω a)† x = πω ω (star a) x) (UniformSpace.Completion.denseRange_coe (α := A ⧸ Nω)) x - (isClosed_eq ((πω ω a).adjoint).continuous (πω ω (star a)).continuous) + (isClosed_eq ((πω ω a)†).continuous (πω ω (star a)).continuous) (fun c => by -- Reduce to checking equality of inner products with arbitrary y (Riesz representation) have : ∀ y, - @inner ℂ Hω _ ((πω ω a).adjoint (↑c)) y = @inner ℂ Hω _ (πω ω (star a) (↑c)) y := by + ⟪(πω ω a)† (↑c), y⟫_ℂ = ⟪πω ω (star a) (↑c), y⟫_ℂ := by intro y refine DenseRange.induction_on (p := fun y => - @inner ℂ Hω _ ((πω ω a).adjoint (↑c)) y = @inner ℂ Hω _ (πω ω (star a) (↑c)) y) + ⟪(πω ω a)† (↑c), y⟫_ℂ = ⟪πω ω (star a) (↑c), y⟫_ℂ) (UniformSpace.Completion.denseRange_coe (α := A ⧸ Nω)) y (isClosed_eq (Continuous.inner continuous_const continuous_id) (Continuous.inner continuous_const continuous_id)) (fun d => by -- Now both vectors are in the dense subspace; rewrite via the quotient-level identity - change @inner ℂ Hω _ ((πω ω a).adjoint (↑c)) (↑d) = @inner ℂ Hω _ (πω ω (star a) (↑c)) (↑d) + change ⟪(πω ω a)† (↑c), (↑d)⟫_ℂ = ⟪πω ω (star a) (↑c), (↑d)⟫_ℂ rw [ContinuousLinearMap.adjoint_inner_left] simp only [πω_apply_coe] rw [UniformSpace.Completion.inner_coe, UniformSpace.Completion.inner_coe] @@ -312,7 +306,8 @@ lemma πω_sub (a b : A) : πω ω (a - b) = πω ω a - πω ω b := by /-- Zero element maps to zero operator: `πω(0) = 0`. -/ lemma πω_zero : πω ω (0 : A) = 0 := - ext_on_completion (ω := ω) (πω ω 0) 0 fun x => by simp [πω_apply_coe, πω'_zero, UniformSpace.Completion.coe_zero] + ext_on_completion (ω := ω) (πω ω 0) 0 fun x => by + simp [πω_apply_coe, πω'_zero, UniformSpace.Completion.coe_zero] /-- The bundled non‑unital *-homomorphism `πω : A →⋆ₙₐ[ℂ] 𝓑(Hω)`. -/ noncomputable def πωStarHom : A →⋆ₙₐ[ℂ] 𝓑(Hω) where @@ -439,13 +434,13 @@ lemma stateOnHilbert_coe (x : A ⧸ Nω) : (h_e := (UniformSpace.Completion.isUniformEmbedding_coe (α := A ⧸ Nω)).isUniformInducing)] /-- Riesz identification: `⟪ξω, x⟫ = stateOnHilbert x`. -/ -lemma inner_ξω_eq (x : Hω) : @inner ℂ Hω _ (ξω ω) x = stateOnHilbert ω x := by +lemma inner_ξω_eq (x : Hω) : ⟪ξω ω, x⟫_ℂ = stateOnHilbert ω x := by unfold ξω rw [@InnerProductSpace.toDual_symm_apply ℂ Hω _ _ _ _] /-- Recovery of the original state on the quotient: `ω x = ⟪ξω, [x]⟫`. -/ lemma state_recovery_quot (x : A) : - ω x = @inner ℂ Hω _ (ξω ω) (↑(Quotient.mk'' x : A ⧸ Nω) : Hω) := by + ω x = ⟪ξω ω, (↑(Quotient.mk'' x : A ⧸ Nω) : Hω)⟫_ℂ := by rw [inner_ξω_eq] rw [stateOnHilbert_coe] rfl @@ -467,28 +462,24 @@ lemma πω_cyclic_identity (b : A) : intro yq obtain ⟨c, rfl⟩ := Quotient.exists_rep yq -- Typeclass instances are resolved using the renamed quotient instances - calc @inner ℂ Hω _ (πω ω b (ξω ω)) (↑(Quotient.mk'' c : A ⧸ Nω)) - = @inner ℂ Hω _ (ξω ω) (ContinuousLinearMap.adjoint (πω ω b) (↑(Quotient.mk'' c : A ⧸ Nω))) := by + calc ⟪πω ω b (ξω ω), (↑(Quotient.mk'' c : A ⧸ Nω) : Hω)⟫_ℂ + = ⟪ξω ω, (πω ω b)† (↑(Quotient.mk'' c : A ⧸ Nω) : Hω)⟫_ℂ := by rw [ContinuousLinearMap.adjoint_inner_right] - _ = @inner ℂ Hω _ (ξω ω) (πω ω (star b) (↑(Quotient.mk'' c : A ⧸ Nω))) := by + _ = ⟪ξω ω, πω ω (star b) (↑(Quotient.mk'' c : A ⧸ Nω) : Hω)⟫_ℂ := by rw [πω_star] - _ = @inner ℂ Hω _ (ξω ω) (↑(Quotient.mk'' (star b * c) : A ⧸ Nω)) := by rw [πω_apply_quotient_coe] + _ = ⟪ξω ω, (↑(Quotient.mk'' (star b * c) : A ⧸ Nω) : Hω)⟫_ℂ := by rw [πω_apply_quotient_coe] _ = stateOnHilbert ω (↑(Quotient.mk'' (star b * c) : A ⧸ Nω)) := by rw [inner_ξω_eq] _ = stateOnQuot ω (Quotient.mk'' (star b * c)) := by rw [stateOnHilbert_coe] _ = ω (star b * c) := rfl _ = innerQuotient ω (Quotient.mk'' b) (Quotient.mk'' c) := by unfold innerQuotient; simp + -- The bare quotient/completion coercions give the elaborator no type anchor for the + -- `⟪·,·⟫_ℂ` instance metavariable, so the inner product is spelled explicitly here. _ = @inner ℂ (A ⧸ Nω) _ (Quotient.mk'' b) (Quotient.mk'' c) := rfl _ = @inner ℂ Hω _ (↑(Quotient.mk'' b : A ⧸ Nω)) (↑(Quotient.mk'' c : A ⧸ Nω)) := by -- Lift inner product from dense subspace into completion simp [UniformSpace.Completion.inner_coe] -/-- The quotient image of any element lies in the closure of the cyclic orbit. -/ -lemma quotient_in_cyclic_closure (b : A) : - (↑(Quotient.mk'' b : A ⧸ Nω) : Hω) ∈ closure (⋃ (a : A), {πω ω a (ξω ω)}) := by - rw [← πω_cyclic_identity] - exact subset_closure (Set.mem_iUnion.mpr ⟨b, rfl⟩) - /-- Cyclicity of `ξω`: the span of `{πω a ξω | a : A}` is dense in `Hω`. -/ lemma ξω_is_cyclic : Dense (↑(Submodule.span ℂ {πω ω a (ξω ω) | a : A}) : Set Hω) := by rw [Metric.dense_iff] @@ -503,7 +494,7 @@ lemma ξω_is_cyclic : Dense (↑(Submodule.span ℂ {πω ω a (ξω ω) | a : /-- GNS identity for the constructed triplet: `ω a = ⟪ξω, πω a ξω⟫`. -/ lemma state_recovery (a : A) : - ω a = @inner ℂ Hω _ (ξω ω) (πω ω a (ξω ω)) := by + ω a = ⟪ξω ω, πω ω a (ξω ω)⟫_ℂ := by rw [πω_cyclic_identity] exact state_recovery_quot ω a @@ -536,7 +527,8 @@ lemma ξω_norm : ‖ξω ω‖ = 1 := by by_contra h_not; push Not at h_not -- Expand definition of norm via supremum over ratios ‖ω a‖/‖a‖ rw [State.norm_def] at h_not - obtain ⟨_, ⟨a, ha, rfl⟩, h_r_large⟩ : ∃ r ∈ {s : ℝ | ∃ a : A, a ≠ 0 ∧ s = ‖ω a‖ / ‖a‖}, ‖stateOnQuot ω‖ < r := by + obtain ⟨_, ⟨a, ha, rfl⟩, h_r_large⟩ : + ∃ r ∈ {s : ℝ | ∃ a : A, a ≠ 0 ∧ s = ‖ω a‖ / ‖a‖}, ‖stateOnQuot ω‖ < r := by by_contra h_no; push Not at h_no have : sSup {s : ℝ | ∃ a : A, a ≠ 0 ∧ s = ‖ω a‖ / ‖a‖} ≤ ‖stateOnQuot ω‖ := by refine csSup_le ?_ h_no @@ -753,15 +745,15 @@ lemma approxUnit_eval_tendsto_one : Tendsto (fun e : A => ω e) (CStarAlgebra.approximateUnit A) (nhds (1 : ℂ)) := by classical have h_vec := tendsto_on_vector (ω := ω) (x := ξω ω) - have h_inner : Continuous fun v : Hω => @inner ℂ Hω _ (ξω ω) v := + have h_inner : Continuous fun v : Hω => ⟪ξω ω, v⟫_ℂ := Continuous.inner continuous_const continuous_id have h_comp : - Tendsto (fun e : A => @inner ℂ Hω _ (ξω ω) (πω ω e (ξω ω))) + Tendsto (fun e : A => ⟪ξω ω, πω ω e (ξω ω)⟫_ℂ) (CStarAlgebra.approximateUnit A) - (nhds (@inner ℂ Hω _ (ξω ω) (ξω ω))) := + (nhds ⟪ξω ω, ξω ω⟫_ℂ) := (h_inner.tendsto (ξω ω)).comp h_vec have h_eq : - (fun e : A => @inner ℂ Hω _ (ξω ω) (πω ω e (ξω ω))) = + (fun e : A => ⟪ξω ω, πω ω e (ξω ω)⟫_ℂ) = fun e : A => ω e := by funext e simpa using (state_recovery (ω := ω) e).symm diff --git a/QuantumSystem/Algebra/CStarAlgebra/GNS/DirectSum.lean b/QuantumSystem/Algebra/CStarAlgebra/GNS/DirectSum.lean index 793e5e9..8e416f6 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/GNS/DirectSum.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/GNS/DirectSum.lean @@ -3,9 +3,13 @@ module public import Mathlib.Analysis.CStarAlgebra.Hom public import Mathlib.Analysis.InnerProductSpace.l2Space public import QuantumSystem.Algebra.CStarAlgebra.GNS.PureState +public import QuantumSystem.Algebra.CStarAlgebra.Representation +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation @[expose] public section +open scoped Adjoint ComplexHilbertSpace + namespace GNS namespace DirectSum @@ -69,7 +73,8 @@ lemma componentWiseMap_memℓp (a : A) (x : Hilbert A) : /-- The norm bound for the component-wise map. -/ lemma componentWiseMap_norm_bound (a : A) (x : Hilbert A) : - ‖(⟨fun ψ => componentWiseMap a ψ (x.val ψ), componentWiseMap_memℓp a x⟩ : Hilbert A)‖ ≤ ‖a‖ * ‖x‖ := by + ‖(⟨fun ψ => componentWiseMap a ψ (x.val ψ), componentWiseMap_memℓp a x⟩ : Hilbert A)‖ + ≤ ‖a‖ * ‖x‖ := by have h2pos : (0 : ℝ) < (2 : ℝ≥0∞).toReal := by norm_num have h2 : (2 : ℝ≥0∞).toReal = 2 := by norm_num rw [lp.norm_eq_tsum_rpow h2pos, lp.norm_eq_tsum_rpow h2pos] @@ -84,7 +89,9 @@ lemma componentWiseMap_norm_bound (a : A) (x : Hilbert A) : rw [memℓp_gen_iff zero_lt_two] at this simp only [h2] at this exact this - have sum_ineq : ∑' ψ, ‖componentWiseMap a ψ (x.val ψ)‖ ^ (2 : ℝ) ≤ ‖a‖ ^ 2 * ∑' ψ, ‖x.val ψ‖ ^ (2 : ℝ) := by + have sum_ineq : + ∑' ψ, ‖componentWiseMap a ψ (x.val ψ)‖ ^ (2 : ℝ) ≤ + ‖a‖ ^ 2 * ∑' ψ, ‖x.val ψ‖ ^ (2 : ℝ) := by rw [← tsum_mul_left] apply tsum_le_of_sum_le' (by positivity) intro s @@ -124,10 +131,10 @@ noncomputable def directSumCLM (a : A) : 𝓑(Hilbert A) := LinearMap.mkContinuous (directSumLinearMap a) ‖a‖ (componentWiseMap_norm_bound a) /-- The adjoint of the direct sum operator equals the direct sum operator of the adjoint: -`(directSumCLM a)* = directSumCLM (star a)`. This follows from the *-homomorphism property +`(directSumCLM a)† = directSumCLM (star a)`. This follows from the *-homomorphism property of each component GNS representation. -/ lemma directSumCLM_adjoint (a : A) : - ContinuousLinearMap.adjoint (directSumCLM a) = directSumCLM (star a) := by + (directSumCLM a)† = directSumCLM (star a) := by refine ContinuousLinearMap.ext fun x => ?_ apply ext_inner_right ℂ intro y @@ -147,8 +154,9 @@ lemma directSumCLM_adjoint (a : A) : rw [ContinuousLinearMap.star_eq_adjoint] /-- The direct sum representation as a non-unital *-algebra homomorphism from `A` -to bounded operators on the direct sum Hilbert space. This is the universal -representation used in the Gelfand-Naimark theorem. -/ +to bounded operators on the direct sum Hilbert space. This is the representation +used in the Gelfand-Naimark theorem; see `rep` for its bundled form, whose docstring +records why it is an atomic rather than a universal representation. -/ noncomputable def directSumAlgHom : A →⋆ₙₐ[ℂ] 𝓑(Hilbert A) where toFun a := directSumCLM a map_mul' a b := by @@ -306,6 +314,60 @@ theorem directSumAlgHom_isometry (a : A) : ‖directSumAlgHom a‖ = ‖a‖ := NonUnitalStarAlgHom.norm_map directSumAlgHom directSumAlgHom_injective a + +/-- The direct sum of the GNS representations of all pure states, bundled as a `CStarRep A`. + +This is the representation that witnesses the Gelfand-Naimark theorem +(`CStarRep.exists_isometric`). Summing over *pure* states — rather than over all states — +makes this a (non-reduced form of the) **atomic representation**: it is not the universal +representation, which is the direct sum over the whole state space. The index type is the +full type `PureState A`, not a set of unitary equivalence classes, so the same equivalence +class is repeated once per pure state realising it. -/ +noncomputable def rep (A : Type*) [NonUnitalCStarAlgebra A] : CStarRep A where + H := Hilbert A + π := directSumAlgHom + + +@[simp] +lemma rep_π : (rep A).π = directSumAlgHom (A := A) := rfl + + +/-- The direct sum representation is isometric, in the bundled form. -/ +theorem rep_isometry : Isometry (rep A).π := + AddMonoidHomClass.isometry_of_norm _ directSumAlgHom_isometry + + +/-- The direct sum representation is faithful, in the bundled form. -/ +theorem rep_injective : Function.Injective (rep A).π := + directSumAlgHom_injective + + +/-- The image of `A` under the direct sum representation is norm closed in `𝓑(H)`. + +Together with `rep_isometry` and `rep_injective` this is what makes the image a +C\*-subalgebra: `NonUnitalStarAlgHom.range` is already a `*`-subalgebra, and closedness +upgrades it to a C\*-subalgebra. It holds because `A` is complete and the representation +is isometric, so it is a closed embedding. -/ +theorem rep_isClosed_range : + IsClosed (NonUnitalStarAlgHom.range (rep A).π : Set 𝓑((rep A).H)) := by + rw [NonUnitalStarAlgHom.coe_range] + exact rep_isometry.isClosedEmbedding.isClosed_range + + +/-- The direct sum representation, corestricted to its image, is a `*`-isomorphism +of `A` onto the C\*-subalgebra `NonUnitalStarAlgHom.range (rep A).π` of `𝓑(H)`. -/ +noncomputable def repRangeEquiv (A : Type*) [NonUnitalCStarAlgebra A] : + A ≃⋆ₐ[ℂ] NonUnitalStarAlgHom.range (rep A).π := + StarAlgEquiv.ofBijective (NonUnitalStarAlgHom.rangeRestrict (rep A).π) + ⟨fun _ _ h => rep_injective (congrArg Subtype.val h), by rintro ⟨_, x, rfl⟩; exact ⟨x, rfl⟩⟩ + + +/-- The `*`-isomorphism of `A` onto the image of the direct sum representation is +isometric: it preserves the norm inherited from `𝓑(H)`. -/ +theorem norm_repRangeEquiv (a : A) : + ‖((repRangeEquiv A a : NonUnitalStarAlgHom.range (rep A).π) : 𝓑((rep A).H))‖ = ‖a‖ := + directSumAlgHom_isometry a + end DirectSum end GNS diff --git a/QuantumSystem/Algebra/CStarAlgebra/GNS/PureState.lean b/QuantumSystem/Algebra/CStarAlgebra/GNS/PureState.lean index 7ede837..ed86017 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/GNS/PureState.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/GNS/PureState.lean @@ -20,7 +20,7 @@ namespace GNS namespace Representation -open scoped ComplexConjugate CStarAlgebra InnerProduct NNReal +open scoped ComplexConjugate CStarAlgebra InnerProduct NNReal ComplexHilbertSpace local notation "⟪" x ", " y "⟫" => inner ℂ x y @@ -279,8 +279,11 @@ lemma normalized_vectorFunctional_mem_quasiStateSpace (T : GNS.Representation ω lemma trichotomy_from_purity {ψ : PureState A} - (W : Submodule ℂ (PureState.gnsRepresentation ψ).H) (hWinv : (PureState.gnsRepresentation ψ).IsInvariant W) (_hWclosed : IsClosed (W : Set (PureState.gnsRepresentation ψ).H)) - (v₁ v₂ : (PureState.gnsRepresentation ψ).H) (hv₁ : v₁ ∈ W) (hv₂ : v₂ ∈ Wᗮ) (hξ : (PureState.gnsRepresentation ψ).ξ = v₁ + v₂) (horth : ⟪v₁, v₂⟫ = 0) : + (W : Submodule ℂ (PureState.gnsRepresentation ψ).H) + (hWinv : (PureState.gnsRepresentation ψ).IsInvariant W) + (_hWclosed : IsClosed (W : Set (PureState.gnsRepresentation ψ).H)) + (v₁ v₂ : (PureState.gnsRepresentation ψ).H) (hv₁ : v₁ ∈ W) (hv₂ : v₂ ∈ Wᗮ) + (hξ : (PureState.gnsRepresentation ψ).ξ = v₁ + v₂) (horth : ⟪v₁, v₂⟫ = 0) : ‖v₁‖ ^ 2 = 0 ∨ ‖v₁‖ ^ 2 = 1 := by let T := PureState.gnsRepresentation ψ by_contra h_contra @@ -413,10 +416,12 @@ lemma trichotomy_from_purity {ψ : PureState A} rw [h_decomp] have h1 : (T.π a) v₁ - v₁ ∈ W := Submodule.sub_mem W (hWinv a ⟨v₁, hv₁, rfl⟩) hv₁ have h2 : (T.π a) v₂ ∈ Wᗮ := isInvariant_orthogonal T W hWinv a ⟨v₂, hv₂, rfl⟩ - have h_pythag := norm_add_sq_eq_norm_sq_add_norm_sq_of_inner_eq_zero ((T.π a) v₁ - v₁) ((T.π a) v₂) ((Submodule.mem_orthogonal W _).mp h2 _ h1) + have h_pythag := norm_add_sq_eq_norm_sq_add_norm_sq_of_inner_eq_zero + ((T.π a) v₁ - v₁) ((T.π a) v₂) ((Submodule.mem_orthogonal W _).mp h2 _ h1) rw [← sq, ← sq, ← sq] at h_pythag exact h_pythag - have h_sq_le : ‖(T.π a) v₁ - v₁‖ ^ 2 ≤ ‖(T.π a) T.ξ - v₁‖ ^ 2 := by rw [h_orth]; linarith [sq_nonneg ‖(T.π a) v₂‖] + have h_sq_le : ‖(T.π a) v₁ - v₁‖ ^ 2 ≤ ‖(T.π a) T.ξ - v₁‖ ^ 2 := by + rw [h_orth]; linarith [sq_nonneg ‖(T.π a) v₂‖] rw [sq_le_sq, abs_of_nonneg (norm_nonneg _), abs_of_nonneg (norm_nonneg _)] at h_sq_le exact lt_of_le_of_lt h_sq_le ha have h_norm_v2 : ‖(T.π a) v₂‖ < ε := by @@ -427,10 +432,12 @@ lemma trichotomy_from_purity {ψ : PureState A} rw [h_decomp] have h1 : (T.π a) v₁ - v₁ ∈ W := Submodule.sub_mem W (hWinv a ⟨v₁, hv₁, rfl⟩) hv₁ have h2 : (T.π a) v₂ ∈ Wᗮ := isInvariant_orthogonal T W hWinv a ⟨v₂, hv₂, rfl⟩ - have h_pythag := norm_add_sq_eq_norm_sq_add_norm_sq_of_inner_eq_zero ((T.π a) v₁ - v₁) ((T.π a) v₂) ((Submodule.mem_orthogonal W _).mp h2 _ h1) + have h_pythag := norm_add_sq_eq_norm_sq_add_norm_sq_of_inner_eq_zero + ((T.π a) v₁ - v₁) ((T.π a) v₂) ((Submodule.mem_orthogonal W _).mp h2 _ h1) rw [← sq, ← sq, ← sq] at h_pythag exact h_pythag - have h_sq_le : ‖(T.π a) v₂‖ ^ 2 ≤ ‖(T.π a) T.ξ - v₁‖ ^ 2 := by rw [h_orth]; linarith [sq_nonneg ‖(T.π a) v₁ - v₁‖] + have h_sq_le : ‖(T.π a) v₂‖ ^ 2 ≤ ‖(T.π a) T.ξ - v₁‖ ^ 2 := by + rw [h_orth]; linarith [sq_nonneg ‖(T.π a) v₁ - v₁‖] rw [sq_le_sq, abs_of_nonneg (norm_nonneg _), abs_of_nonneg (norm_nonneg _)] at h_sq_le exact lt_of_le_of_lt h_sq_le ha -- Contradiction @@ -463,7 +470,9 @@ lemma trichotomy_from_purity {ψ : PureState A} · apply mul_lt_mul_of_pos_left h_norm_diff hv₁_norm_pos · exact inv_pos.mpr h_pos _ = ε * (t⁻¹ * ‖v₁‖) := by ring - _ ≤ ε * K := by gcongr; apply le_add_of_nonneg_right; apply mul_nonneg (inv_nonneg.mpr (by linarith)) (norm_nonneg _) + _ ≤ ε * K := by + gcongr; apply le_add_of_nonneg_right + apply mul_nonneg (inv_nonneg.mpr (by linarith)) (norm_nonneg _) _ = 1/2 := by dsimp [ε] have hK_ne : K ≠ 0 := hK_pos.ne' @@ -479,7 +488,8 @@ lemma trichotomy_from_purity {ψ : PureState A} dsimp only [χ] change ‖(1 - (t : ℂ))⁻¹ • (T.vectorFunctional v₂) a‖ < 1/2 rw [vectorFunctional_apply, smul_eq_mul] - rw [norm_mul, ← Complex.ofReal_one, ← Complex.ofReal_sub, norm_inv, Complex.norm_real, Real.norm_eq_abs, abs_of_pos (by linarith : 0 < 1 - t)] + rw [norm_mul, ← Complex.ofReal_one, ← Complex.ofReal_sub, norm_inv, Complex.norm_real, + Real.norm_eq_abs, abs_of_pos (by linarith : 0 < 1 - t)] calc (1 - t)⁻¹ * ‖⟪v₂, (T.π a) v₂⟫‖ _ ≤ (1 - t)⁻¹ * (‖v₂‖ * ‖(T.π a) v₂‖) := by apply mul_le_mul_of_nonneg_left @@ -490,7 +500,9 @@ lemma trichotomy_from_purity {ψ : PureState A} · apply mul_lt_mul_of_pos_left h_norm_v2 hv₂_norm_pos · exact inv_pos.mpr (by linarith) _ = ε * ((1 - t)⁻¹ * ‖v₂‖) := by ring - _ ≤ ε * K := by gcongr; apply le_add_of_nonneg_left; apply mul_nonneg (inv_nonneg.mpr (by linarith)) (norm_nonneg _) + _ ≤ ε * K := by + gcongr; apply le_add_of_nonneg_left + apply mul_nonneg (inv_nonneg.mpr (by linarith)) (norm_nonneg _) _ = 1/2 := by dsimp [ε] have hK_ne : K ≠ 0 := hK_pos.ne' diff --git a/QuantumSystem/Algebra/CStarAlgebra/GNS/Representation.lean b/QuantumSystem/Algebra/CStarAlgebra/GNS/Representation.lean index 264a2a2..8a4df78 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/GNS/Representation.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/GNS/Representation.lean @@ -4,30 +4,37 @@ public import QuantumSystem.ForMathlib.LinearAlgebra.Span.Def public import QuantumSystem.ForMathlib.Topology.DenseLinear public import QuantumSystem.ForMathlib.Topology.MetricSpace.Completion public import QuantumSystem.Algebra.CStarAlgebra.GNS.Construction +public import QuantumSystem.Algebra.CStarAlgebra.Representation +public import QuantumSystem.Algebra.CStarAlgebra.Representation.Irreducible @[expose] public section +open scoped InnerProductSpace ComplexHilbertSpace + namespace GNS /-- A (non‑unital) GNS triplet `(π, H, ξ)` for a state `ω : A → ℂ` on a (possibly non‑unital) C*-algebra `A`. -Fields: +This structure extends the bundled C\*-algebra representation `CStarRep A` +(defined in `QuantumSystem.Algebra.CStarAlgebra.Representation`) by the additional data of a +cyclic unit vector `ξ` and the GNS identity, factoring the conceptual +decomposition "general C\*-representation" + "cyclic vector for a specified +state" at the type level. + +Inherited fields (from `CStarRep A`): * `H` : the underlying type of the Hilbert space. * `[hilbert]` : evidence that `H` is a complex Hilbert space. * `π : A →⋆ₙₐ[ℂ] 𝓑(H)` : a non‑unital *-representation of `A` on `H`. + +GNS-specific fields: * `ξ : H` : a cyclic unit vector. * `cyclic` : density of the linear span `Submodule.span ℂ { π a ξ | a : A }` in `H`. * `unit_norm` : normalisation `‖ξ‖ = 1`. * `gns_condition` : the GNS identity `ω a = ⟪ξ, π a ξ⟫` for every `a : A`. -/ -structure Representation {A} [NonUnitalCStarAlgebra A] (ω : State ℂ A) where - /-- The Hilbert space of the GNS representation -/ - H : Type* - /-- The complex Hilbert space structure on H -/ - [hilbert : ComplexHilbertSpace H] - /-- The representation π : A → 𝓑(H) -/ - π : A →⋆ₙₐ[ℂ] 𝓑(H) +structure Representation {A} [NonUnitalCStarAlgebra A] (ω : State ℂ A) + extends CStarRep A where /-- The cyclic vector ξ ∈ H -/ ξ : H /-- The cyclic property: the span of {π(a)ξ : a ∈ A} is dense in H -/ @@ -35,9 +42,7 @@ structure Representation {A} [NonUnitalCStarAlgebra A] (ω : State ℂ A) where /-- The unit norm property: ‖ξ‖ = 1 -/ unit_norm : ‖ξ‖ = 1 /-- The GNS condition: ω(a) = ⟪ξ, π(a)ξ⟫ for all a ∈ A -/ - gns_condition : ∀ a : A, ω a = @inner ℂ H hilbert.toInnerProductSpace.toCore.toInner ξ (π a ξ) - -attribute [instance] Representation.hilbert + gns_condition : ∀ a : A, ω a = ⟪ξ, π a ξ⟫_ℂ namespace Representation @@ -47,50 +52,52 @@ variable {A : Type*} [NonUnitalCStarAlgebra A] variable {ω : State ℂ A} /-- A submodule `W` of the Hilbert space of a GNS representation is invariant if it is -stable under the action of `π(a)` for every `a : A`. -/ +stable under the action of `π(a)` for every `a : A`. + +This is the generic `CStarRep.IsInvariant` of the underlying representation `T.toCStarRep`; +it is provided here as a thin wrapper so that the GNS-specific lemmas read `T.IsInvariant W`, +while the single source of truth for the notion is `CStarRep.IsInvariant`. -/ def IsInvariant (T : Representation ω) (W : Submodule ℂ T.H) : Prop := - ∀ a : A, W.map (T.π a).toLinearMap ≤ W + T.toCStarRep.IsInvariant W /-- A GNS representation is (topologically) irreducible if the only **closed** invariant -submodules are `⊥` and `⊤`. -/ +submodules are `⊥` and `⊤`. + +This is the generic `CStarRep.IsIrreducible` of the underlying representation `T.toCStarRep` +(definitionally, since `T.H`/`T.π` are the inherited fields); the GNS wrapper keeps the +`T.IsIrreducible` spelling while delegating the definition to `CStarRep.IsIrreducible`. -/ def IsIrreducible (T : Representation ω) : Prop := - ∀ W : Submodule ℂ T.H, - IsClosed (W : Set T.H) → - T.IsInvariant W → - (W = ⊥ ∨ W = ⊤) + T.toCStarRep.IsIrreducible @[simp] lemma isInvariant_bot (T : Representation ω) : T.IsInvariant (⊥ : Submodule ℂ T.H) := by - unfold IsInvariant intro a w hw rcases (show w = 0 from by simpa using hw) with rfl simp @[simp] lemma isInvariant_top (T : Representation ω) : T.IsInvariant (⊤ : Submodule ℂ T.H) := by - unfold IsInvariant intro a w hw simp -/-- A unitary equivalence between two GNS representations packages the underlying -unitary between the Hilbert spaces together with the expected compatibility data. -/ -structure UnitaryEquiv (T₁ T₂ : Representation ω) where - /-- The underlying unitary between the Hilbert spaces. -/ - unitary_map : UnitaryMap T₁.H T₂.H +/-- A unitary equivalence between two GNS representations for the **same** state `ω`. + +This **extends** the generic unitary equivalence of the underlying `CStarRep`s +(`CStarRep.UnitaryEquiv`, which supplies the intertwining unitary `unitary_map` and its +`intertwines` property) by the GNS-specific compatibility `map_cyclic_vector`, requiring the +unitary to identify the two cyclic vectors. This is exactly the extra data of the GNS +uniqueness statement, on top of the bare unitary intertwiner shared with sector theory. -/ +structure UnitaryEquiv (T₁ T₂ : Representation ω) extends + CStarRep.UnitaryEquiv T₁.toCStarRep T₂.toCStarRep where /-- The unitary sends the cyclic vector of the first triplet to that of the second. -/ map_cyclic_vector : - (unitary_map.toContinuousLinearMap) T₁.ξ = T₂.ξ - /-- The unitary intertwines the two representations. -/ - intertwines : - ∀ a : A, - (unitary_map.toContinuousLinearMap) ∘L T₁.π a = - T₂.π a ∘L (unitary_map.toContinuousLinearMap) + (toUnitaryEquiv.unitary_map.toContinuousLinearMap) T₁.ξ = T₂.ξ notation:50 T₁ " ≃ᵁ " T₂ => Representation.UnitaryEquiv (ω := _) T₁ T₂ /-- Auxiliary: computes `⟪π a ξ, π b ξ⟫ = ω (star a * b)` for a single GNS triplet. -/ private lemma inner_cyclic_aux (T : Representation ω) (a b : A) : - @inner ℂ T.H _ (T.π a T.ξ) (T.π b T.ξ) = ω (star a * b) := by - have h₁ : @inner ℂ T.H _ (T.π a T.ξ) (T.π b T.ξ) = - @inner ℂ T.H _ T.ξ ((T.π a).adjoint (T.π b T.ξ)) := by + ⟪T.π a T.ξ, T.π b T.ξ⟫_ℂ = ω (star a * b) := by + have h₁ : ⟪T.π a T.ξ, T.π b T.ξ⟫_ℂ = + ⟪T.ξ, (T.π a).adjoint (T.π b T.ξ)⟫_ℂ := by rw [ContinuousLinearMap.adjoint_inner_right] have hstar : (T.π a).adjoint = T.π (star a) := by have : T.π (star a) = star (T.π a) := T.π.map_star' a @@ -102,12 +109,12 @@ private lemma inner_cyclic_aux (T : Representation ω) (a b : A) : /-- Inner products on cyclic vectors agree across triplets: both realise `ω (star a * b)`. -/ private lemma inner_cyclic (T₁ T₂ : Representation ω) (a b : A) : - @inner ℂ T₁.H _ (T₁.π a T₁.ξ) (T₁.π b T₁.ξ) = - @inner ℂ T₂.H _ (T₂.π a T₂.ξ) (T₂.π b T₂.ξ) := by + ⟪T₁.π a T₁.ξ, T₁.π b T₁.ξ⟫_ℂ = + ⟪T₂.π a T₂.ξ, T₂.π b T₂.ξ⟫_ℂ := by calc - @inner ℂ T₁.H _ (T₁.π a T₁.ξ) (T₁.π b T₁.ξ) + ⟪T₁.π a T₁.ξ, T₁.π b T₁.ξ⟫_ℂ = ω (star a * b) := inner_cyclic_aux T₁ a b - _ = @inner ℂ T₂.H _ (T₂.π a T₂.ξ) (T₂.π b T₂.ξ) := (inner_cyclic_aux T₂ a b).symm + _ = ⟪T₂.π a T₂.ξ, T₂.π b T₂.ξ⟫_ℂ := (inner_cyclic_aux T₂ a b).symm /-- Equality of norms of corresponding cyclic orbit vectors between two triplets. -/ private lemma norm_cyclic (T₁ T₂ : Representation ω) (a : A) : @@ -133,7 +140,7 @@ private lemma cyclic_correspondence_well_defined (T₁ T₂ : Representation ω) _ = T₁.π a T₁.ξ - T₁.π b T₁.ξ := by simp [ContinuousLinearMap.sub_apply] _ = 0 := by rw [h]; simp -- Transfer vanishing inner product to T₂ using equality of inner forms on cyclic vectors - have h_inner_zero_T₂ : @inner ℂ T₂.H _ (T₂.π (a - b) T₂.ξ) (T₂.π (a - b) T₂.ξ) = 0 := by + have h_inner_zero_T₂ : ⟪T₂.π (a - b) T₂.ξ, T₂.π (a - b) T₂.ξ⟫_ℂ = 0 := by rw [← inner_cyclic T₁ T₂ (a - b) (a - b), h_map_sub]; simp -- Norm zero implies vector zero in T₂ have h_map_sub_T₂ : T₂.π (a - b) T₂.ξ = 0 := by @@ -209,13 +216,13 @@ private lemma dist_cyclic (T₁ T₂ : Representation ω) (a : A) : rw [← sq_eq_sq₀ (norm_nonneg _) (norm_nonneg _)] rw [@norm_sub_sq ℂ T₁.H, @norm_sub_sq ℂ T₂.H] have h_norm := norm_cyclic T₁ T₂ a - have h_inner₁ : @inner ℂ T₁.H _ (T₁.π a T₁.ξ) T₁.ξ = conj (ω a) := by - calc @inner ℂ T₁.H _ (T₁.π a T₁.ξ) T₁.ξ - = conj (@inner ℂ T₁.H _ T₁.ξ (T₁.π a T₁.ξ)) := by rw [@inner_conj_symm ℂ T₁.H] + have h_inner₁ : ⟪T₁.π a T₁.ξ, T₁.ξ⟫_ℂ = conj (ω a) := by + calc ⟪T₁.π a T₁.ξ, T₁.ξ⟫_ℂ + = conj ⟪T₁.ξ, T₁.π a T₁.ξ⟫_ℂ := by rw [@inner_conj_symm ℂ T₁.H] _ = conj (ω a) := by rw [T₁.gns_condition a] - have h_inner₂ : @inner ℂ T₂.H _ (T₂.π a T₂.ξ) T₂.ξ = conj (ω a) := by - calc @inner ℂ T₂.H _ (T₂.π a T₂.ξ) T₂.ξ - = conj (@inner ℂ T₂.H _ T₂.ξ (T₂.π a T₂.ξ)) := by rw [@inner_conj_symm ℂ T₂.H] + have h_inner₂ : ⟪T₂.π a T₂.ξ, T₂.ξ⟫_ℂ = conj (ω a) := by + calc ⟪T₂.π a T₂.ξ, T₂.ξ⟫_ℂ + = conj ⟪T₂.ξ, T₂.π a T₂.ξ⟫_ℂ := by rw [@inner_conj_symm ℂ T₂.H] _ = conj (ω a) := by rw [T₂.gns_condition a] rw [h_inner₁, h_inner₂, h_norm, T₁.unit_norm, T₂.unit_norm] @@ -244,8 +251,8 @@ private lemma cyclicMap_well_defined (T₁ T₂ : Representation ω) /-- The cyclic map preserves inner products. -/ private lemma cyclicMap_inner (T₁ T₂ : Representation ω) (x y : cyclicSet T₁) : - @inner ℂ T₂.H _ (cyclicMap T₁ T₂ x) (cyclicMap T₁ T₂ y) = - @inner ℂ T₁.H _ x.val y.val := by + ⟪cyclicMap T₁ T₂ x, cyclicMap T₁ T₂ y⟫_ℂ = + ⟪x.val, y.val⟫_ℂ := by obtain ⟨a, ha⟩ := Set.mem_iUnion.mp x.property obtain ⟨b, hb⟩ := Set.mem_iUnion.mp y.property simp only [Set.mem_singleton_iff] at ha hb @@ -293,7 +300,8 @@ private lemma linear_isometry_equiv_map_cyclic_vector (T₁ T₂ : Representatio U T₁.ξ = T₂.ξ := by suffices ‖(U : T₁.H →L[ℂ] T₂.H) T₁.ξ - T₂.ξ‖ = 0 by exact eq_of_sub_eq_zero (norm_eq_zero.mp this) refine le_antisymm (le_of_forall_pos_le_add fun ε hε => ?_) (norm_nonneg _) - obtain ⟨x₁, hx₁_close, hx₁_mem⟩ := Metric.dense_iff.mp (dense_cyclicSet T₁) T₁.ξ (ε / 2) (by linarith : 0 < ε / 2) + obtain ⟨x₁, hx₁_close, hx₁_mem⟩ := + Metric.dense_iff.mp (dense_cyclicSet T₁) T₁.ξ (ε / 2) (by linarith : 0 < ε / 2) obtain ⟨a, ha⟩ := Set.mem_iUnion.mp hx₁_mem simp only [Set.mem_singleton_iff] at ha subst ha; rw [Metric.mem_ball, dist_eq_norm] at hx₁_close @@ -301,8 +309,10 @@ private lemma linear_isometry_equiv_map_cyclic_vector (T₁ T₂ : Representatio calc ‖(U : T₁.H →L[ℂ] T₂.H) T₁.ξ - T₂.ξ‖ ≤ ‖(U : T₁.H →L[ℂ] T₂.H) T₁.ξ - (U : T₁.H →L[ℂ] T₂.H) (T₁.π a T₁.ξ)‖ + ‖(U : T₁.H →L[ℂ] T₂.H) (T₁.π a T₁.ξ) - T₂.ξ‖ := by - convert norm_add_le ((U : T₁.H →L[ℂ] T₂.H) T₁.ξ - (U : T₁.H →L[ℂ] T₂.H) (T₁.π a T₁.ξ)) - ((U : T₁.H →L[ℂ] T₂.H) (T₁.π a T₁.ξ) - T₂.ξ) using 2; abel + convert norm_add_le + ((U : T₁.H →L[ℂ] T₂.H) T₁.ξ - (U : T₁.H →L[ℂ] T₂.H) (T₁.π a T₁.ξ)) + ((U : T₁.H →L[ℂ] T₂.H) (T₁.π a T₁.ξ) - T₂.ξ) using 2 + abel _ = ‖(U : T₁.H →L[ℂ] T₂.H) (T₁.ξ - T₁.π a T₁.ξ)‖ + ‖T₂.π a T₂.ξ - T₂.ξ‖ := by rw [map_sub, h_cyclic] _ = ‖T₁.ξ - T₁.π a T₁.ξ‖ + ‖T₂.π a T₂.ξ - T₂.ξ‖ := by @@ -341,7 +351,8 @@ private lemma linear_isometry_equiv_intertwines (T₁ T₂ : Representation ω) simp only [ContinuousLinearMap.coe_comp', Function.comp_apply] have h_dense := dense_cyclicSet T₁ unfold cyclicSet at h_dense - have h_on_cyclic : ∀ b : A, (U : T₁.H →L[ℂ] T₂.H) (T₁.π a (T₁.π b T₁.ξ)) = T₂.π a ((U : T₁.H →L[ℂ] T₂.H) (T₁.π b T₁.ξ)) := + have h_on_cyclic : ∀ b : A, (U : T₁.H →L[ℂ] T₂.H) (T₁.π a (T₁.π b T₁.ξ)) = + T₂.π a ((U : T₁.H →L[ℂ] T₂.H) (T₁.π b T₁.ξ)) := fun b => linear_isometry_equiv_intertwines_on_cyclic T₁ T₂ U h_cyclic a b let f : T₁.H → T₂.H := fun y => (U : T₁.H →L[ℂ] T₂.H) (T₁.π a y) let g : T₁.H → T₂.H := fun y => T₂.π a ((U : T₁.H →L[ℂ] T₂.H) y) @@ -381,7 +392,8 @@ private lemma extendCyclicMap_eq (T₁ T₂ : Representation ω) (x : cyclicSet (cyclicMap T₁ T₂) (cyclicMap_isometry T₁ T₂) x private lemma extend_cyclic_map_left_inv (T₁ T₂ : Representation ω) : - ∀ x : T₁.H, (extendCyclicMap (T₁ := T₂) (T₂ := T₁)) ((extendCyclicMap (T₁ := T₁) (T₂ := T₂)) x) = x := by + ∀ x : T₁.H, + (extendCyclicMap (T₁ := T₂) (T₂ := T₁)) ((extendCyclicMap (T₁ := T₁) (T₂ := T₂)) x) = x := by intro x set U_fun := extendCyclicMap (T₁ := T₁) (T₂ := T₂) set V_fun := extendCyclicMap (T₁ := T₂) (T₂ := T₁) @@ -407,7 +419,8 @@ private lemma extend_cyclic_map_left_inv (T₁ T₂ : Representation ω) : _ = T₁.π a T₁.ξ := hV private lemma extend_cyclic_map_right_inv (T₁ T₂ : Representation ω) : - ∀ y : T₂.H, (extendCyclicMap (T₁ := T₁) (T₂ := T₂)) ((extendCyclicMap (T₁ := T₂) (T₂ := T₁)) y) = y := by + ∀ y : T₂.H, + (extendCyclicMap (T₁ := T₁) (T₂ := T₂)) ((extendCyclicMap (T₁ := T₂) (T₂ := T₁)) y) = y := by intro y set U_fun := extendCyclicMap (T₁ := T₁) (T₂ := T₂) set V_fun := extendCyclicMap (T₁ := T₂) (T₂ := T₁) @@ -453,9 +466,11 @@ private lemma extend_cyclic_map_add (T₁ T₂ : Representation ω) : (cyclicMap_well_defined T₁ T₂ ⟨_, mem_cyclicSet (T := T₁) b⟩ b rfl) calc U_fun (T₁.π a T₁.ξ + T₁.π b T₁.ξ) = U_fun ((T₁.π a + T₁.π b) T₁.ξ) := by rw [ContinuousLinearMap.add_apply] - _ = U_fun (T₁.π (a + b) T₁.ξ) := by rw [show T₁.π (a + b) = T₁.π a + T₁.π b from T₁.π.map_add' a b] + _ = U_fun (T₁.π (a + b) T₁.ξ) := by + rw [show T₁.π (a + b) = T₁.π a + T₁.π b from T₁.π.map_add' a b] _ = T₂.π (a + b) T₂.ξ := hUab - _ = (T₂.π a + T₂.π b) T₂.ξ := by rw [show T₂.π (a + b) = T₂.π a + T₂.π b from T₂.π.map_add' a b] + _ = (T₂.π a + T₂.π b) T₂.ξ := by + rw [show T₂.π (a + b) = T₂.π a + T₂.π b from T₂.π.map_add' a b] _ = T₂.π a T₂.ξ + T₂.π b T₂.ξ := by rw [ContinuousLinearMap.add_apply] _ = U_fun (T₁.π a T₁.ξ) + U_fun (T₁.π b T₁.ξ) := by rw [← hUa, ← hUb] @@ -477,7 +492,8 @@ private lemma extend_cyclic_map_smul (T₁ T₂ : Representation ω) : change U_fun (c • T₁.π a T₁.ξ) = c • U_fun (T₁.π a T₁.ξ) calc U_fun (c • T₁.π a T₁.ξ) = U_fun ((c • T₁.π a) T₁.ξ) := rfl - _ = U_fun ((T₁.π (c • a)) T₁.ξ) := by rw [show T₁.π (c • a) = c • T₁.π a from T₁.π.map_smul' c a] + _ = U_fun ((T₁.π (c • a)) T₁.ξ) := by + rw [show T₁.π (c • a) = c • T₁.π a from T₁.π.map_smul' c a] _ = T₂.π (c • a) T₂.ξ := hU1 _ = (c • T₂.π a) T₂.ξ := by rw [show T₂.π (c • a) = c • T₂.π a from T₂.π.map_smul' c a] _ = c • T₂.π a T₂.ξ := rfl diff --git a/QuantumSystem/Algebra/CStarAlgebra/GNS/Separable.lean b/QuantumSystem/Algebra/CStarAlgebra/GNS/Separable.lean new file mode 100644 index 0000000..87e878e --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/GNS/Separable.lean @@ -0,0 +1,244 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.Hom +public import QuantumSystem.Algebra.CStarAlgebra.GNS.PureState +public import QuantumSystem.Algebra.CStarAlgebra.Representation.DirectSum +public import QuantumSystem.ForMathlib.Analysis.Normed.Lp.Separable + +/-! +# A countable norming family of pure states, for a separable C\*-algebra + +The Gelfand-Naimark representation built in `CStarAlgebra/GNS/DirectSum.lean` is indexed by +the *whole* pure state space, so its Hilbert space is typically nonseparable even when `A` +is separable. This file builds the countable replacement. + +The idea is to index not by states but by a dense sequence of the algebra: for each nonzero +member `aₙ` of a countable dense sequence, pick a pure state that **norms** `aₙ` +(`IsPureState.exists_norm_sq_of_ne_zero`), and take the ℓ²-direct sum of the corresponding +GNS representations. Norming — rather than merely detecting — is what makes a countable +family enough: for `a ≠ 0` and `aₙ` within `‖a‖ / 2` of `a`, the `n`-th representation +cannot annihilate `a`, because it does not shrink `aₙ`. + +## Main results + +* `GNS.Representation.norm_sq_apply_cyclic` — `‖T.π x T.ξ‖ ^ 2 = (ω (star x * x)).re` for any + GNS triplet. This is what turns a norming *state* into a non-vanishing *operator*. +* `GNS.Representation.separableSpace_H` — the Hilbert space of a GNS triplet over a + separable algebra is separable. +* `GNS.normingFamily` — the countable family of GNS representations described above, and + `GNS.normingFamily_separatesPoints`, `GNS.separableSpace_normingFamily_directSumHilbert`. + +The theorem these serve is `CStarRep.exists_isometric_separable`, in +`CStarAlgebra/GelfandNaimark.lean`. +-/ + +@[expose] public section + +open TopologicalSpace + +open scoped InnerProductSpace Adjoint ComplexHilbertSpace + +universe u + +namespace GNS + +variable {A : Type u} [NonUnitalCStarAlgebra A] + +namespace Representation + +/-- For a GNS triplet, the squared length of the orbit vector `T.π x T.ξ` is the value of +the state at `star x * x`. + +This is the bridge between a *norming state* and a *non-vanishing operator*: a state whose +value at `star x * x` is `‖x‖ ^ 2` yields a representation with `‖T.π x T.ξ‖ = ‖x‖`. -/ +lemma norm_sq_apply_cyclic {ω : State ℂ A} (T : Representation ω) (x : A) : + ‖T.π x T.ξ‖ ^ 2 = (ω (star x * x)).re := by + have hstar : T.π (star x) = (T.π x)† := by + rw [map_star, ContinuousLinearMap.star_eq_adjoint] + have hval : T.π (star x * x) T.ξ = (T.π x)† (T.π x T.ξ) := by + rw [map_mul, hstar] + rfl + rw [T.gns_condition (star x * x), hval, ContinuousLinearMap.adjoint_inner_right, + inner_self_eq_norm_sq_to_K] + simp [← Complex.ofReal_pow] + +/-- A norming state gives an orbit vector of full length: if `ω (star x * x) = ‖x‖ ^ 2` +then `‖T.π x T.ξ‖ = ‖x‖`. -/ +lemma norm_apply_cyclic_of_norming {ω : State ℂ A} (T : Representation ω) {x : A} + (hx : ω (star x * x) = ((‖x‖ ^ 2 : ℝ) : ℂ)) : + ‖T.π x T.ξ‖ = ‖x‖ := by + have h := T.norm_sq_apply_cyclic x + rw [hx] at h + have h' : ‖T.π x T.ξ‖ ^ 2 = ‖x‖ ^ 2 := by rw [h, Complex.ofReal_re] + nlinarith [norm_nonneg (T.π x T.ξ), norm_nonneg x, h'] + +/-- The orbit map `a ↦ T.π a T.ξ` is `1`-Lipschitz. + +It is the composition of the contraction `a ↦ T.π a` with evaluation at a unit vector. -/ +lemma lipschitzWith_apply_cyclic {ω : State ℂ A} (T : Representation ω) : + LipschitzWith 1 (fun a : A => T.π a T.ξ) := by + refine LipschitzWith.of_dist_le_mul fun a b => ?_ + have hsub : T.π a T.ξ - T.π b T.ξ = T.π (a - b) T.ξ := by + rw [map_sub] + rfl + have hbound : dist (T.π a T.ξ) (T.π b T.ξ) ≤ ‖a - b‖ := by + calc dist (T.π a T.ξ) (T.π b T.ξ) = ‖T.π (a - b) T.ξ‖ := by rw [dist_eq_norm, hsub] + _ ≤ ‖T.π (a - b)‖ * ‖T.ξ‖ := ContinuousLinearMap.le_opNorm _ _ + _ ≤ ‖a - b‖ * 1 := by + gcongr + · exact NonUnitalStarAlgHom.norm_apply_le _ _ + · exact le_of_eq T.unit_norm + _ = ‖a - b‖ := mul_one _ + simpa [dist_eq_norm] using hbound + +/-- The Hilbert space of a GNS triplet over a **separable** C\*-algebra is separable. + +The orbit of the cyclic vector is a continuous image of `A`, hence separable; its span is +separable, and the span is dense by cyclicity. -/ +theorem separableSpace_H [SeparableSpace A] {ω : State ℂ A} (T : Representation ω) : + SeparableSpace T.H := by + have hrange : IsSeparable (Set.range fun a : A => T.π a T.ξ) := + isSeparable_range T.lipschitzWith_apply_cyclic.continuous + have hspan : IsSeparable + ((Submodule.span ℂ {x | ∃ a : A, T.π a T.ξ = x} : Submodule ℂ T.H) : Set T.H) := + hrange.span + rw [← isSeparable_univ_iff, ← T.cyclic.closure_eq] + exact hspan.closure + +end Representation + +section Norming + +variable (A) in +/-- The index type of the norming family: the positions of the **nonzero** members of a +fixed dense sequence of `A`. + +Dropping the zero members is what lets every index carry a pure state; the discarded +positions carry no information, since `0` is annihilated by every representation. When +`A = 0` the index type is empty, which is the correct answer there. -/ +noncomputable def NormingIndex [SeparableSpace A] : Type := {n : ℕ // denseSeq A n ≠ 0} + +instance [SeparableSpace A] : Countable (NormingIndex A) := + inferInstanceAs (Countable {n : ℕ // denseSeq A n ≠ 0}) + +/-- The element of the dense sequence sitting at a norming index. -/ +noncomputable def NormingIndex.elem [SeparableSpace A] (i : NormingIndex A) : A := + denseSeq A i.1 + +lemma NormingIndex.elem_ne_zero [SeparableSpace A] (i : NormingIndex A) : i.elem ≠ 0 := i.2 + +/-- A pure state norming the element at a norming index. -/ +noncomputable def normingState [SeparableSpace A] (i : NormingIndex A) : PureState A := + ⟨(IsPureState.exists_norm_sq_of_ne_zero i.elem i.elem_ne_zero).choose, + (IsPureState.exists_norm_sq_of_ne_zero i.elem i.elem_ne_zero).choose_spec.1⟩ + +lemma normingState_spec [SeparableSpace A] (i : NormingIndex A) : + (normingState i : State ℂ A) (star i.elem * i.elem) = ((‖i.elem‖ ^ 2 : ℝ) : ℂ) := + (IsPureState.exists_norm_sq_of_ne_zero i.elem i.elem_ne_zero).choose_spec.2 + +variable (A) in +/-- The **countable norming family**: one GNS representation for each nonzero member of a +dense sequence of `A`, at a pure state norming that member. -/ +noncomputable def normingFamily [SeparableSpace A] : SectorFamily.{u, u, 0} A where + Index := NormingIndex A + rep i := (PureState.gnsRepresentation (normingState i)).toCStarRep + +/-- Each summand of the norming family is separable. -/ +instance [SeparableSpace A] (i : NormingIndex A) : + SeparableSpace ((normingFamily A).rep i).H := + Representation.separableSpace_H _ + +/-- The direct-sum Hilbert space of the norming family is separable: it is an ℓ²-sum of +countably many separable spaces. -/ +instance separableSpace_normingFamily_directSumHilbert [SeparableSpace A] : + SeparableSpace (normingFamily A).directSumHilbert := + inferInstanceAs (SeparableSpace (lp (fun i : NormingIndex A => + ((normingFamily A).rep i).H) 2)) + +variable (A) in +/-- **The norming family separates points.** + +If every member annihilates `a ≠ 0`, take `aₙ` from the dense sequence within `‖a‖ / 2` of +`a`. Then `aₙ ≠ 0`, so it carries an index `i`, and its norming state gives +`‖π_i(aₙ) ξ_i‖ = ‖aₙ‖`. But `π_i(a) = 0` forces +`‖aₙ‖ = ‖π_i(aₙ - a) ξ_i‖ ≤ ‖aₙ - a‖ < ‖a‖ / 2`, contradicting `‖aₙ‖ > ‖a‖ / 2`. -/ +theorem normingFamily_separatesPoints [SeparableSpace A] : + (normingFamily A).SeparatesPoints := by + intro a ha + by_contra hne + have hapos : (0 : ℝ) < ‖a‖ := norm_pos_iff.mpr hne + -- A member of the dense sequence within `‖a‖ / 2` of `a`. + obtain ⟨n, hn⟩ := (denseRange_denseSeq A).exists_dist_lt a (by positivity : (0:ℝ) < ‖a‖ / 2) + have hdist : ‖denseSeq A n - a‖ < ‖a‖ / 2 := by + rw [dist_eq_norm] at hn + rwa [norm_sub_rev] at hn + have hb_lower : ‖a‖ / 2 < ‖denseSeq A n‖ := by + have := norm_sub_norm_le a (denseSeq A n) + rw [← norm_neg (a - denseSeq A n), neg_sub] at this + linarith + have hb_ne : denseSeq A n ≠ 0 := by + intro h + rw [h, norm_zero] at hb_lower + linarith + set i : NormingIndex A := ⟨n, hb_ne⟩ with hi + have helem : i.elem = denseSeq A n := rfl + set T := PureState.gnsRepresentation (normingState i) with hT + -- The representation at `i` norms `i.elem`. + have hnorm : ‖T.π i.elem T.ξ‖ = ‖i.elem‖ := + T.norm_apply_cyclic_of_norming (normingState_spec i) + -- but it kills `a`, so it can only see the difference. + have hzero : T.π a = 0 := ha i + have hsplit : T.π i.elem T.ξ = T.π (i.elem - a) T.ξ := by + rw [map_sub] + simp [hzero] + have hle : ‖T.π i.elem T.ξ‖ ≤ ‖i.elem - a‖ := by + rw [hsplit] + calc ‖T.π (i.elem - a) T.ξ‖ ≤ ‖T.π (i.elem - a)‖ * ‖T.ξ‖ := + ContinuousLinearMap.le_opNorm _ _ + _ ≤ ‖i.elem - a‖ * 1 := by + gcongr + · exact NonUnitalStarAlgHom.norm_apply_le _ _ + · exact le_of_eq T.unit_norm + _ = ‖i.elem - a‖ := mul_one _ + rw [hnorm, helem] at hle + linarith + +variable (A) in +/-- The ℓ²-direct sum of the norming family, bundled as a `CStarRep`. + +This is the separable counterpart of `GNS.DirectSum.rep`: same construction, but indexed by +a dense sequence of the algebra instead of by the whole pure state space. -/ +noncomputable def normingRep [SeparableSpace A] : CStarRep.{u, u} A where + H := (normingFamily A).directSumHilbert + π := (normingFamily A).directSumRep + +@[simp] +lemma normingRep_π [SeparableSpace A] : + (normingRep A).π = (normingFamily A).directSumRep := rfl + +instance [SeparableSpace A] : SeparableSpace (normingRep A).H := + separableSpace_normingFamily_directSumHilbert + +variable (A) in +/-- The norming representation is faithful. -/ +theorem normingRep_injective [SeparableSpace A] : + Function.Injective (normingRep A).π := + (normingFamily A).directSumRep_injective_of (normingFamily_separatesPoints A) + +variable (A) in +/-- The norming representation is isometric, by faithfulness. -/ +theorem normingRep_isometry [SeparableSpace A] : Isometry (normingRep A).π := + AddMonoidHomClass.isometry_of_norm _ fun a => + NonUnitalStarAlgHom.norm_map _ (normingRep_injective A) a + +variable (A) in +/-- The image of the norming representation is norm closed, so it is a C\*-subalgebra of +the bounded operators on a separable Hilbert space. -/ +theorem normingRep_isClosed_range [SeparableSpace A] : + IsClosed (NonUnitalStarAlgHom.range (normingRep A).π : Set 𝓑((normingRep A).H)) := by + rw [NonUnitalStarAlgHom.coe_range] + exact (normingRep_isometry A).isClosedEmbedding.isClosed_range + +end Norming + +end GNS diff --git a/QuantumSystem/Algebra/CStarAlgebra/GelfandNaimark.lean b/QuantumSystem/Algebra/CStarAlgebra/GelfandNaimark.lean index 669fae7..4ffa029 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/GelfandNaimark.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/GelfandNaimark.lean @@ -1,34 +1,120 @@ module public import QuantumSystem.Algebra.CStarAlgebra.GNS.DirectSum +public import QuantumSystem.Algebra.CStarAlgebra.GNS.Separable + +/-! +# The Gelfand-Naimark theorem + +Every C\*-algebra, not necessarily unital and not necessarily commutative, is isometrically +`*`-isomorphic onto a norm closed `*`-subalgebra of the bounded operators on some complex +Hilbert space. This is the noncommutative Gelfand-Naimark theorem (Gelfand-Naimark 1943; +Murphy, *C\*-algebras and Operator Theory*, Ch. 3 "Ideals and Positive Functionals"; Pedersen, +*C\*-Algebras and Their Automorphism Groups*, Ch. 3 "Functionals and Representations"; +Blackadar, *Operator Algebras*, Ch. II "C\*-Algebras"; Takesaki, *Theory of Operator Algebras +I*, Ch. I "Fundamentals of Banach Algebras and C\*-Algebras" — chapter-level locators only, +since none of these four books was independently opened for this file; see the `math-review` +ledger entry for `GelfandNaimark.lean`), and it is not to be confused with the commutative +Gelfand-Naimark theorem — Gelfand duality — which Mathlib carries as +`gelfandTransform_isometry`. + +## Main results + +* `CStarRep.exists_isometric` — there is a representation `R : CStarRep A` whose `R.π` is + isometric, injective, and has norm closed range. The three conjuncts together say that + `R.π` identifies `A` with a C\*-subalgebra of `𝓑(R.H)`. +* `CStarRep.exists_starAlgEquiv_range` — the same statement in the form the classical + formulation uses: an explicit `*`-isomorphism of `A` onto a norm closed `*`-subalgebra + of `𝓑(R.H)` that preserves the norm. +* `CStarRep.exists_isometric_separable` — the **separable refinement**: when `A` is + separable the Hilbert space may be taken separable as well. + +The witness for the first two is `GNS.DirectSum.rep`, the ℓ²-direct sum of the GNS +representations of all pure states of `A`; faithfulness comes from there being enough pure +states (`IsPureState.exists_pos_re_of_ne_zero`), and isometry from faithfulness by +`NonUnitalStarAlgHom.norm_map`. The separable refinement uses a different witness, +`GNS.normingRep`, indexed by a dense sequence of the algebra instead of by the whole pure +state space. + +## Conventions and scope + +* The Hilbert space is produced in the *same* universe as `A`, which is stronger than the + usual textbook statement. +* Closedness of the range uses completeness of `A`; `NonUnitalStarAlgHom.range` supplies the + `*`-subalgebra structure, so "closed `*`-subalgebra" is exactly "C\*-subalgebra" here. +* Nondegeneracy of the representation is not asserted. It would not strengthen the + statement: corestricting any isometric `*`-representation to the closure of the span of + its image is again isometric and is nondegenerate, so the two existentials are equivalent. + (The witness `GNS.DirectSum.rep` is in fact nondegenerate, being a direct sum of cyclic + representations.) +* The witness of `CStarRep.exists_isometric` is itself never separable beyond the trivial + cases: it is indexed by the whole pure state space, and for `A = C₀(ℝ)` the point + evaluations already form a continuum. The separable refinement therefore does not + strengthen that theorem's witness but replaces it — see + `CStarRep.exists_isometric_separable` and `GNS.normingRep`. +* `CStarRep.exists_isometric_separable` does **not** claim that `H` may be taken to be + `ℓ²(ℕ)`. That is true, by the unitary classification of Hilbert spaces by the cardinality + of an orthonormal basis, but it is extra content and is not stated here. +* There is no unital corollary: nothing here states `R.π 1 = 1` for unital `A`. +-/ @[expose] public section +open scoped ComplexHilbertSpace + universe u -/-- -The **Gelfand-Naimark Theorem** (non-unital version): -Every non-unital C*-algebra `A` is isometrically *-isomorphic to a C*-subalgebra of -bounded operators on some Hilbert space `H`. +/-- **Gelfand-Naimark theorem** (noncommutative form, `A` not necessarily unital): +every C\*-algebra admits a faithful isometric `*`-representation whose image is norm closed, +that is, `A` is carried onto a C\*-subalgebra of `𝓑(H)` for some complex Hilbert space `H`. -We prove this by exhibiting: -1. A Hilbert space `H` (the ℓ²-direct sum of GNS Hilbert spaces over all pure states). -2. A non-unital *-homomorphism `φ : A →⋆ₙₐ[ℂ] 𝓑(H)` (the direct sum of GNS representations). -3. A proof that `φ` is injective and isometric (hence an isometric *-isomorphism onto its image). --/ -theorem gelfand_naimark_theorem : - ∀ (A: Type u) [NonUnitalCStarAlgebra A], - ∃ (H : Type u) (_ : ComplexHilbertSpace H), - ∃ (φ : A →⋆ₙₐ[ℂ] 𝓑(H)), Isometry φ := by - intro A _ - -- The Hilbert space is the ℓ²-direct sum of GNS Hilbert spaces over all pure states - let H := GNS.DirectSum.Hilbert A - -- The representation is the direct sum of all GNS representations - let φ := GNS.DirectSum.directSumAlgHom (A := A) - refine ⟨H, inferInstance, φ, ?_⟩ - -- To show φ is an isometry, it suffices to show it preserves norms - -- (since it's already additive as a *-homomorphism) - apply AddMonoidHomClass.isometry_of_norm - intro a - exact GNS.DirectSum.directSumAlgHom_isometry a +The three conjuncts are what make the conclusion an identification rather than a mere bound: +`Isometry R.π` gives `‖R.π a‖ = ‖a‖`, `Function.Injective R.π` makes `R.π` a bijection onto +its image, and `IsClosed` upgrades the `*`-subalgebra `NonUnitalStarAlgHom.range R.π` to a C\*-subalgebra. For +the same statement packaged as an explicit `*`-isomorphism, see +`CStarRep.exists_starAlgEquiv_range`. + +The Hilbert space is obtained in the same universe as `A`. The witness is +`GNS.DirectSum.rep A`, the ℓ²-direct sum of the GNS representations of all pure states. -/ +theorem CStarRep.exists_isometric (A : Type u) [NonUnitalCStarAlgebra A] : + ∃ R : CStarRep.{u, u} A, + Isometry R.π ∧ Function.Injective R.π ∧ IsClosed (NonUnitalStarAlgHom.range R.π : Set 𝓑(R.H)) := + ⟨GNS.DirectSum.rep A, GNS.DirectSum.rep_isometry, GNS.DirectSum.rep_injective, + GNS.DirectSum.rep_isClosed_range⟩ + + +/-- **Gelfand-Naimark theorem**, in the form the classical statement uses: every +C\*-algebra `A`, not necessarily unital, is isometrically `*`-isomorphic onto a norm closed +`*`-subalgebra `S` of `𝓑(H)` for some complex Hilbert space `H`. + +Isometry is stated as `‖(e a : 𝓑(R.H))‖ = ‖a‖` — the norm `S` inherits from `𝓑(R.H)` — +rather than through a norm structure on `S` itself. See `CStarRep.exists_isometric` for the +unbundled form. -/ +theorem CStarRep.exists_starAlgEquiv_range (A : Type u) [NonUnitalCStarAlgebra A] : + ∃ (R : CStarRep.{u, u} A) (S : NonUnitalStarSubalgebra ℂ 𝓑(R.H)) (e : A ≃⋆ₐ[ℂ] S), + IsClosed (S : Set 𝓑(R.H)) ∧ ∀ a : A, ‖((e a : S) : 𝓑(R.H))‖ = ‖a‖ := + ⟨GNS.DirectSum.rep A, NonUnitalStarAlgHom.range (GNS.DirectSum.rep A).π, GNS.DirectSum.repRangeEquiv A, + GNS.DirectSum.rep_isClosed_range, GNS.DirectSum.norm_repRangeEquiv⟩ + + +/-- **Gelfand-Naimark theorem, separable refinement**: a *separable* C\*-algebra, not +necessarily unital, admits a faithful isometric `*`-representation with norm closed image on +a **separable** complex Hilbert space. + +The refinement is genuine extra content over `CStarRep.exists_isometric`, whose witness is +indexed by the whole pure state space and is nonseparable for, say, `A = C₀(ℝ)`. The +witness here is `GNS.normingRep A`: the ℓ²-direct sum of the GNS representations of a +*countable* family of pure states, one norming each nonzero member of a dense sequence of +`A`. Norming rather than merely detecting is what makes a countable family separate the +points of `A` — see `GNS.normingFamily_separatesPoints`. + +Separability of `A` is sufficient and never necessary: `𝓑(ℓ²)` is not norm separable, yet +its identity representation on the separable space `ℓ²` is faithful. -/ +theorem CStarRep.exists_isometric_separable (A : Type u) [NonUnitalCStarAlgebra A] + [TopologicalSpace.SeparableSpace A] : + ∃ R : CStarRep.{u, u} A, TopologicalSpace.SeparableSpace R.H ∧ + Isometry R.π ∧ Function.Injective R.π ∧ + IsClosed (NonUnitalStarAlgHom.range R.π : Set 𝓑(R.H)) := + ⟨GNS.normingRep A, inferInstance, GNS.normingRep_isometry A, GNS.normingRep_injective A, + GNS.normingRep_isClosed_range A⟩ diff --git a/QuantumSystem/Algebra/CStarAlgebra/PureState.lean b/QuantumSystem/Algebra/CStarAlgebra/PureState.lean index cbbd410..e3e0f60 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/PureState.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/PureState.lean @@ -131,13 +131,16 @@ lemma isPositive_of_norm_eq_one_map_one /-- For any nonzero positive element `b`, there exists a state on the unitization -whose value on `b` is a strictly positive real number (viewed in `ℂ`). - -We state this by exhibiting `r : ℝ` with `0 < r` and `ψ (Unitization.inr b) = (r : ℂ)`. -This avoids using an order on `ℂ` (which does not exist in Lean). -/ -private lemma exists_unitization_state_pos_re (b : A) (hb : 0 ≤ b) (hb_ne : b ≠ 0) : +whose value on `b` is exactly `‖b‖`, viewed in `ℂ`. + +The value is the norm rather than merely a positive real: the character supplied by Gelfand +duality is evaluated at the spectral radius, and `‖b‖` lies in the spectrum because `b` is +positive. Norming — not just detecting — is what the separable refinement of the +Gelfand-Naimark theorem needs, so the value is carried in the statement instead of being +existentially discarded. Positivity of the value follows from `hb_ne`, since `0 < ‖b‖`. -/ +private lemma exists_unitization_state_norm (b : A) (hb : 0 ≤ b) (hb_ne : b ≠ 0) : ∃ ψ : Unitization ℂ A →L[ℂ] ℂ, ‖ψ‖ = 1 ∧ ψ 1 = 1 ∧ - ∃ r : ℝ, 0 < r ∧ ψ (Unitization.inr b) = (r : ℂ) := by + ψ (Unitization.inr b) = (‖b‖ : ℂ) := by let b' : Unitization ℂ A := Unitization.inr b have hb' : 0 ≤ b' := Unitization.inr_nonneg_iff.mpr hb have hb'_ne : b' ≠ 0 := Unitization.inr_injective.ne hb_ne @@ -162,7 +165,8 @@ private lemma exists_unitization_state_pos_re (b : A) (hb : 0 ≤ b) (hb_ne : b haveI : IsStarNormal b' := IsSelfAdjoint.isStarNormal (IsSelfAdjoint.of_nonneg hb') haveI : IsClosed (StarAlgebra.elemental ℂ b' : Set (Unitization ℂ A)) := StarAlgebra.elemental.isClosed ℂ b' - have h_spec_S : (‖b'‖ : ℂ) ∈ spectrum ℂ (⟨b', StarAlgebra.elemental.self_mem ℂ b'⟩ : StarAlgebra.elemental ℂ b') := by + have h_spec_S : (‖b'‖ : ℂ) ∈ + spectrum ℂ (⟨b', StarAlgebra.elemental.self_mem ℂ b'⟩ : StarAlgebra.elemental ℂ b') := by rwa [StarSubalgebra.spectrum_eq] obtain ⟨φ, hφ⟩ := WeakDual.CharacterSpace.mem_spectrum_iff_exists.mp h_spec_S -- Extend φ to a state ψ on Unitization ℂ A @@ -176,20 +180,25 @@ private lemma exists_unitization_state_pos_re (b : A) (hb : 0 ≤ b) (hb_ne : b rw [WeakDual.toStrongDual_apply] change φ 1 = 1 rw [map_one φ] - · refine ⟨‖b'‖, norm_pos_iff.mpr hb'_ne, ?_⟩ - have hψb' : ψ b' = (‖b'‖ : ℂ) := by + · have hψb' : ψ b' = (‖b'‖ : ℂ) := by rw [hψ_ext ⟨b', StarAlgebra.elemental.self_mem ℂ b'⟩] change (WeakDual.toStrongDual φ.val) ⟨b', _⟩ = (‖b'‖ : ℂ) rw [WeakDual.toStrongDual_apply] simpa using hφ + have hnorm : ‖b'‖ = ‖b‖ := Unitization.norm_inr b + rw [← hnorm] simpa [b'] using hψb' -private lemma exists_quasiState_pos_re (b : A) (hb : 0 ≤ b) (hb_ne : b ≠ 0) : - ∃ φ ∈ QuasiStateSpace A, ∃ r : ℝ, 0 < r ∧ φ b = (r : ℂ) := by +/-- For any nonzero positive element `b`, some quasi-state takes the value `‖b‖` at `b`. + +This is `exists_unitization_state_norm` pulled back along the isometric embedding of `A` +into its unitization. -/ +private lemma exists_quasiState_norm (b : A) (hb : 0 ≤ b) (hb_ne : b ≠ 0) : + ∃ φ ∈ QuasiStateSpace A, φ b = (‖b‖ : ℂ) := by haveI : Nontrivial A := nontrivial_of_ne b 0 hb_ne - obtain ⟨ψ, hψ_norm_eq, hψ_one, r, hrpos, hψb_eq⟩ := - exists_unitization_state_pos_re b hb hb_ne + obtain ⟨ψ, hψ_norm_eq, hψ_one, hψb_eq⟩ := + exists_unitization_state_norm b hb hb_ne have hψ_pos : IsPositive (Unitization ℂ A) ψ := isPositive_of_norm_eq_one_map_one ψ hψ_norm_eq hψ_one let inrLM : A →ₗ[ℂ] Unitization ℂ A := @@ -221,8 +230,7 @@ private lemma exists_quasiState_pos_re (b : A) (hb : 0 ≤ b) (hb_ne : b ≠ 0) · refine ⟨hφ_pos, ?_⟩ simp only [Set.mem_preimage, Metric.mem_closedBall, dist_zero_right] exact hφ_norm - · refine ⟨r, hrpos, ?_⟩ - simpa [φ, inrCLM, inrIso, LinearIsometry.coe_toContinuousLinearMap, + · simpa [φ, inrCLM, inrIso, LinearIsometry.coe_toContinuousLinearMap, LinearIsometry.coe_mk, inrLM, LinearMap.coe_mk, AddHom.coe_mk] using hψb_eq @@ -338,7 +346,8 @@ noncomputable def toState {φ : WeakDual ℂ A} (h : IsPureState φ) : State ℂ simp [h_all_zero a] exact h_ne_zero this.symm obtain ⟨a₀, ha₀⟩ := h_exists - have h_nonempty : { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ }.Nonempty := + have h_nonempty : + { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ }.Nonempty := ⟨‖(WeakDual.toStrongDual φ) a₀‖ / ‖a₀‖, a₀, ha₀, rfl⟩ have h_bdd : BddAbove { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ } := by use ‖WeakDual.toStrongDual φ‖ @@ -349,7 +358,8 @@ noncomputable def toState {φ : WeakDual ℂ A} (h : IsPureState φ) : State ℂ have h_opNorm_eq : ‖WeakDual.toStrongDual φ‖ = sSup { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ } := by apply le_antisymm - · have h_nonneg : 0 ≤ sSup { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ } := by + · have h_nonneg : + 0 ≤ sSup { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ } := by apply Real.sSup_nonneg rintro _ ⟨a, _, rfl⟩ exact div_nonneg (norm_nonneg _) (norm_nonneg _) @@ -360,7 +370,8 @@ noncomputable def toState {φ : WeakDual ℂ A} (h : IsPureState φ) : State ℂ · rw [mul_comm] calc ‖(WeakDual.toStrongDual φ) a‖ = ‖a‖ * (‖(WeakDual.toStrongDual φ) a‖ / ‖a‖) := by field_simp [norm_pos_iff.mpr ha] - _ ≤ ‖a‖ * sSup { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ } := by + _ ≤ ‖a‖ * + sSup { r : ℝ | ∃ a : A, a ≠ 0 ∧ r = ‖(WeakDual.toStrongDual φ) a‖ / ‖a‖ } := by apply mul_le_mul_of_nonneg_left _ (norm_nonneg _) apply le_csSup h_bdd exact ⟨a, ha, rfl⟩ @@ -372,14 +383,19 @@ noncomputable def toState {φ : WeakDual ℂ A} (h : IsPureState φ) : State ℂ simp [h_opNorm_eq] -/-- For any non-zero element `a`, there exists a pure state `φ` such that -`φ (star a * a)` is a strictly positive real number (viewed in `ℂ`). +/-- For any non-zero element `a`, there exists a pure state `φ` **norming** `a`: +`φ (star a * a) = ‖a‖ ^ 2`, viewed in `ℂ`. -Note: `ℂ` itself is not ordered in Lean, so the correct way to express -“`φ(star a * a) > 0`” is to exhibit a real `r > 0` with -`φ (star a * a) = (r : ℂ)`. -/ -lemma exists_pos_re_of_ne_zero (a : A) (ha : a ≠ 0) : - ∃ φ : WeakDual ℂ A, IsPureState φ ∧ ∃ r : ℝ, 0 < r ∧ φ (star a * a) = (r : ℂ) := by +This is the quantitative form of the existence of pure states. It says that the pure +states do not merely detect `a` but recover its norm, which is what lets a *countable* +family of pure states — one for each member of a dense sequence — separate the points of a +separable C\*-algebra. For the weaker detection statement see +`exists_pos_re_of_ne_zero`, which is a corollary. + +Note: `ℂ` is not ordered in Lean, so the value is stated as an equality with the real +number `‖a‖ ^ 2` coerced into `ℂ`. -/ +lemma exists_norm_sq_of_ne_zero (a : A) (ha : a ≠ 0) : + ∃ φ : WeakDual ℂ A, IsPureState φ ∧ φ (star a * a) = ((‖a‖ ^ 2 : ℝ) : ℂ) := by let b := star a * a have hb_ne_zero : b ≠ 0 := by rw [ne_eq, CStarRing.star_mul_self_eq_zero_iff] @@ -388,7 +404,10 @@ lemma exists_pos_re_of_ne_zero (a : A) (ha : a ≠ 0) : apply StarOrderedRing.nonneg_iff.mpr apply AddSubmonoid.subset_closure use a - obtain ⟨ω, hω_mem, r, hrpos, hω_eq⟩ := exists_quasiState_pos_re b hb_pos hb_ne_zero + have hb_norm : ‖b‖ = ‖a‖ ^ 2 := by + simpa [b, sq] using CStarRing.norm_star_mul_self (x := a) + have hb_norm_pos : (0 : ℝ) < ‖b‖ := norm_pos_iff.mpr hb_ne_zero + obtain ⟨ω, hω_mem, hω_eq⟩ := exists_quasiState_norm b hb_pos hb_ne_zero let l : WeakDual ℂ A →L[ℝ] ℝ := { toFun := fun φ => (φ b).re map_add' := fun φ ψ => by @@ -409,15 +428,28 @@ lemma exists_pos_re_of_ne_zero (a : A) (ha : a ≠ 0) : let f : WeakDual ℂ A → ℝ := l let M := sSup (f '' QuasiStateSpace A) have hf : ContinuousOn f (QuasiStateSpace A) := Continuous.continuousOn l.continuous |>.mono (Set.subset_univ _) - have h_M_pos : M > 0 := by - have hω_re : (ω b).re = r := by - have := congrArg Complex.re hω_eq - simpa using this - apply lt_of_lt_of_le hrpos + have hω_re : (ω b).re = ‖b‖ := by + have := congrArg Complex.re hω_eq + simpa using this + -- `M` is bounded below by `‖b‖`, because `ω` attains it. + have h_M_ge : ‖b‖ ≤ M := by rw [← hω_re] apply le_csSup · exact ((QuasiStateSpace.compact A).image_of_continuousOn hf).bddAbove · exact Set.mem_image_of_mem f hω_mem + -- and above by `‖b‖`, because every quasi-state has norm at most one. + have h_M_le : M ≤ ‖b‖ := by + apply csSup_le + · exact ⟨f ω, Set.mem_image_of_mem f hω_mem⟩ + rintro x ⟨φ, hφ_mem, rfl⟩ + have hφ_norm : ‖WeakDual.toStrongDual φ‖ ≤ 1 := by + simpa [QuasiStateSpace] using hφ_mem.2 + calc f φ ≤ ‖φ b‖ := Complex.re_le_norm _ + _ ≤ ‖WeakDual.toStrongDual φ‖ * ‖b‖ := (WeakDual.toStrongDual φ).le_opNorm b + _ ≤ 1 * ‖b‖ := by gcongr + _ = ‖b‖ := one_mul _ + have h_M_eq : M = ‖b‖ := le_antisymm h_M_le h_M_ge + have h_M_pos : M > 0 := by rw [h_M_eq]; exact hb_norm_pos -- Find a maximizer of f on the QuasiStateSpace obtain ⟨φ, hφ_mem, hφ_max⟩ := IsCompact.exists_isMaxOn (QuasiStateSpace.compact A) ⟨ω, hω_mem⟩ hf have hφ_val : f φ = M := by @@ -486,31 +518,33 @@ lemma exists_pos_re_of_ne_zero (a : A) (ha : a ≠ 0) : · intro r ⟨z, hz, hr⟩ rw [← hr] exact hψ_mem_F.2 z hz - have hψ_re_pos : (ψ b).re > 0 := by - -- `l ψ` is definitionally `(ψ b).re`. - have : l ψ > 0 := by - rw [hψ_l_eq_M] - exact h_M_pos - -- Avoid `simp at` (flagged by linter.flexible) - have : (ψ b).re > 0 := by - -- unfold the linear functional `l` on this specific argument - have h' := this - dsimp [l, ContinuousLinearMap.comp_apply, Complex.reCLM] at h' - exact h' - exact this - -- Then use positivity (membership in `QuasiStateSpace`) to see the value is real. + -- `l ψ` is definitionally `(ψ b).re`, and the maximum is `‖b‖`. + have hψ_re_eq : (ψ b).re = ‖b‖ := by + have h' : l ψ = ‖b‖ := by rw [hψ_l_eq_M, h_M_eq] + dsimp [l, ContinuousLinearMap.comp_apply, Complex.reCLM] at h' + exact h' + -- Membership in `QuasiStateSpace` makes the value real, so its real part determines it. have hψ_mem_S : ψ ∈ QuasiStateSpace A := hψ_mem_F.1 obtain ⟨r₀, hr₀⟩ := hψ_mem_S.1 a - -- `hr₀` is exactly the real-valuedness statement for `ψ (star a * a)`. - have hr₀_re : (ψ b).re = (r₀ : ℝ) := by - have h : ψ b = (r₀ : ℂ) := by - simpa [b] using hr₀ - have := congrArg Complex.re h - simpa using this - have hrpos : (0 : ℝ) < (r₀ : ℝ) := by - simpa [hr₀_re] using hψ_re_pos - refine ⟨(r₀ : ℝ), hrpos, ?_⟩ - simpa [b] using hr₀ + have hr₀' : ψ b = ((r₀ : ℝ) : ℂ) := by simpa [b] using hr₀ + change ψ b = ((‖a‖ ^ 2 : ℝ) : ℂ) + rw [← hb_norm, ← hψ_re_eq, hr₀'] + simp + + +/-- For any non-zero element `a`, there exists a pure state `φ` such that +`φ (star a * a)` is a strictly positive real number (viewed in `ℂ`). + +Note: `ℂ` itself is not ordered in Lean, so the correct way to express +“`φ(star a * a) > 0`” is to exhibit a real `r > 0` with +`φ (star a * a) = (r : ℂ)`. + +This is the detection form of `exists_norm_sq_of_ne_zero`; the witness it discards is the +norm itself. -/ +lemma exists_pos_re_of_ne_zero (a : A) (ha : a ≠ 0) : + ∃ φ : WeakDual ℂ A, IsPureState φ ∧ ∃ r : ℝ, 0 < r ∧ φ (star a * a) = (r : ℂ) := by + obtain ⟨φ, hφ_pure, hφ_eq⟩ := exists_norm_sq_of_ne_zero a ha + exact ⟨φ, hφ_pure, ‖a‖ ^ 2, pow_pos (norm_pos_iff.mpr ha) 2, hφ_eq⟩ end IsPureState diff --git a/QuantumSystem/Algebra/CStarAlgebra/Representation.lean b/QuantumSystem/Algebra/CStarAlgebra/Representation.lean new file mode 100644 index 0000000..95b1aa7 --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/Representation.lean @@ -0,0 +1,85 @@ +module + +public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.HilbertSpace + +/-! +# Bundled `*`-representations of a C\*-algebra on a complex Hilbert space + +This file introduces the type `CStarRep A` of (non-unital) `*`-representations +of a non-unital C\*-algebra `A` on a complex Hilbert space, packaged as the +pair `(H, π)` of a carrier and a non-unital star-algebra homomorphism into +the C\*-algebra of bounded linear operators `𝓑(H)`. + +`CStarRep A` is a foundational, sector-agnostic notion: it is the generic +data of a C\*-algebra representation, with no choice of cyclic vector or +attachment to a state. Both the GNS construction and the abstract +representation-theoretic layer are built on top of it: + +* `CStarAlgebra/GNS/Representation.lean` adds a cyclic vector and a state to + obtain a GNS triplet (`GNS.Representation` extends `CStarRep`); +* `CStarAlgebra/Representation/UnitaryEquiv.lean` defines unitary equivalence + between two `CStarRep`s as the existence of an intertwining unitary map (no + cyclic vector compatibility, contrary to `GNS.Representation.UnitaryEquiv` + which is the same-state GNS uniqueness statement); +* `CStarAlgebra/Representation/Irreducible.lean` lifts the irreducibility + predicate to the general `CStarRep` setting; +* `CStarAlgebra/Representation/Family.lean` packages indexed families of + representatives, on which the superselection sector theory of + `QuantumSystem/Algebra/Sector/*` imposes its DHR / topological / KMS criteria. + +## Relation to Mathlib + +Mathlib's `Mathlib.RepresentationTheory.Basic.Representation` is the +group/monoid representation type `G →* (V →ₗ[k] V)` and does not match +the C\*-algebra / Hilbert-space setting. The GNS construction in +`Mathlib.Analysis.CStarAlgebra.GelfandNaimarkSegal` exposes the Hilbert +space (`f.GNS`) and the homomorphism (`f.gnsStarAlgHom`) as separate +artifacts; there is no bundled `(H, π)` structure. This file introduces +the bundle for the first time. + +## Relation to `GNS.Representation` + +`GNS.Representation ω` (defined in +`QuantumSystem/Algebra/CStarAlgebra/GNS/Representation.lean`) is the GNS +triplet `(H, π, ξ)` for a specific state `ω : State ℂ A`, adding a +cyclic unit vector `ξ` and the GNS identity +`ω a = ⟪ξ, π a ξ⟫` on top of the data of a `CStarRep A`. +The forgetful projection sending a GNS triplet to its underlying +`CStarRep` is the canonical bridge between the two layers. + +## Main definitions + +* `CStarRep A` — a bundled non-unital `*`-representation + `π : A →⋆ₙₐ[ℂ] 𝓑(H)` together with the carrier `H` and its + `ComplexHilbertSpace` instance. +-/ + +@[expose] public section + +open scoped ComplexHilbertSpace + +universe u v + +variable {A : Type u} [NonUnitalCStarAlgebra A] + +/-- A bundled non-unital `*`-representation of a non-unital C\*-algebra +`A` on a complex Hilbert space. + +Fields: + +* `H` — the underlying type of the Hilbert space. +* `[hilbert]` — evidence that `H` is a complex Hilbert space. +* `π` — a non-unital `*`-representation `A →⋆ₙₐ[ℂ] 𝓑(H)`. + +This is the underlying data of a representation without any choice of a +cyclic vector or attachment to a particular state. For a GNS triplet +attached to a fixed state, see `GNS.Representation`. -/ +structure CStarRep (A : Type u) [NonUnitalCStarAlgebra A] where + /-- The Hilbert space on which the representation acts. -/ + H : Type v + /-- The complex Hilbert space structure on `H`. -/ + [hilbert : ComplexHilbertSpace H] + /-- The non-unital `*`-representation `A →⋆ₙₐ[ℂ] 𝓑(H)`. -/ + π : A →⋆ₙₐ[ℂ] 𝓑(H) + +attribute [instance] CStarRep.hilbert diff --git a/QuantumSystem/Algebra/CStarAlgebra/Representation/Conjugation.lean b/QuantumSystem/Algebra/CStarAlgebra/Representation/Conjugation.lean new file mode 100644 index 0000000..8eb6db1 --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/Representation/Conjugation.lean @@ -0,0 +1,152 @@ +module + +public import QuantumSystem.Algebra.CStarAlgebra.Representation.UnitaryEquiv + +/-! +# Conjugation of a `CStarRep` by a unitary + +Given a representation `R : CStarRep A` and a unitary +`U : UnitaryMap R.H K` between Hilbert spaces, the **conjugated +representation** is the `CStarRep A` on `K` whose action sends +`a : A` to the bounded operator + +``` +U ∘L R.π a ∘L U† : K →L[ℂ] K. +``` + +The unitary `U` itself intertwines `R` with `R.conjBy U`, so the two +representations are canonically unitarily equivalent. + +This is the operator-algebraic building block of the DHR structure +theorem: given a DHR-intertwined `R` with witness unitary +`U : R.H → globalHilbert L Ω`, the conjugated representation +`R.conjBy U` lives on the same Hilbert space as the vacuum +representation, and the intertwining condition forces it to agree +with the vacuum action on operators localised outside the +DHR-localisation region. + +## Main definitions + +* `CStarRep.conjBy R U` — the conjugated representation. +* `CStarRep.conjByUnitaryEquiv R U` — the canonical unitary + equivalence `R ≃ R.conjBy U`. + +## Main results + +* `CStarRep.conjBy_π_apply` — `(R.conjBy U).π a = U R.π a U†`. +* `CStarRep.conjBy_π_eq_of_intertwined` — if the intertwining identity + `U ∘L R.π a = T ∘L U` holds at a particular operator `T`, then + `(R.conjBy U).π a = T`. This is the bridge to the DHR structure + theorem: the conjugated DHR representation acts as the vacuum + representation on operators localised outside the DHR region. + +## References + +* Naaijkens, *Anyons in Infinite Quantum Systems*, 2012, §3.2. +* Bratteli, Robinson, *Operator Algebras and Quantum Statistical + Mechanics II*, §5.3. +-/ + +@[expose] public section + +open scoped Adjoint + +namespace CStarRep + +variable {A : Type*} [NonUnitalCStarAlgebra A] + +/-- The **conjugated representation** of `R : CStarRep A` by a unitary +`U : UnitaryMap R.H K`: the carrier is `K` and the action is +`a ↦ U ∘L R.π a ∘L U†`. + +Internally the underlying non-unital star-algebra homomorphism is the +composition of `R.π` with the conjugation-by-`U` star-algebra +equivalence `(R.H →L[ℂ] R.H) ≃⋆ₐ[ℂ] (K →L[ℂ] K)` provided by +`LinearIsometryEquiv.conjStarAlgEquiv`. -/ +noncomputable def conjBy (R : CStarRep A) {K : Type*} [ComplexHilbertSpace K] + (U : UnitaryMap R.H K) : CStarRep A where + H := K + π := + (((U.toLinearIsometryEquiv.conjStarAlgEquiv : + (R.H →L[ℂ] R.H) →⋆ₙₐ[ℂ] (K →L[ℂ] K))).comp R.π) + +@[simp] lemma conjBy_H (R : CStarRep A) {K : Type*} [ComplexHilbertSpace K] + (U : UnitaryMap R.H K) : (R.conjBy U).H = K := rfl + +/-- The action of the conjugated representation: `(R.conjBy U).π a` is +the operator `U ∘L R.π a ∘L U†` on `K`. -/ +@[simp] lemma conjBy_π_apply (R : CStarRep A) {K : Type*} + [ComplexHilbertSpace K] (U : UnitaryMap R.H K) (a : A) : + (R.conjBy U).π a = + U.toContinuousLinearMap ∘L R.π a ∘L U.toContinuousLinearMap† := by + -- `conjStarAlgEquiv` is defined so that `e.conjStarAlgEquiv x = e ∘L x ∘L e.symm` + -- and `UnitaryMap.toLinearIsometryEquiv` is built with `toFun = U.toCLM` and + -- `invFun = U.toCLM.adjoint`, so both sides reduce to the same CLM. + apply ContinuousLinearMap.ext + intro y + rfl + +/-- The **canonical unitary equivalence** `R ≃ R.conjBy U`: the +intertwining unitary is `U` itself, and the intertwining identity is +`U ∘L R.π a = (U R.π a U†) ∘L U`, which follows from `U† U = id`. -/ +noncomputable def conjByUnitaryEquiv (R : CStarRep A) {K : Type*} + [ComplexHilbertSpace K] (U : UnitaryMap R.H K) : + CStarRep.UnitaryEquiv R (R.conjBy U) where + unitary_map := U + intertwines a := by + -- LHS: `U ∘L R.π a`. + -- RHS: `(R.conjBy U).π a ∘L U = U ∘L R.π a ∘L U† ∘L U = U ∘L R.π a` + -- using `U† U = id` from `U.adjoint_comp`. + rw [conjBy_π_apply] + apply ContinuousLinearMap.ext + intro x + have h : U.toContinuousLinearMap† + (U.toContinuousLinearMap x) = x := by + have := congrArg + (fun (f : R.H →L[ℂ] R.H) => f x) U.adjoint_comp + simpa using this + -- Goal: `U (R.π a x) = U (R.π a (U† (U x)))`. Unfold nested + -- `.comp` applications and then rewrite with `h`. + change U.toContinuousLinearMap ((R.π a) x) = + U.toContinuousLinearMap ((R.π a) (U.toContinuousLinearMap† + (U.toContinuousLinearMap x))) + rw [h] + +/-- **Intertwining-from-the-left transports to operator equality** for +the conjugated representation. + +If a unitary `U : UnitaryMap R.H K` satisfies `U ∘L R.π a = T ∘L U` +for some `T : K →L[ℂ] K` at a particular `a : A`, then the conjugated +action `(R.conjBy U).π a` equals `T`. + +This is the bridge to the DHR structure theorem: when `U` is the +witness of a DHR intertwining condition and `T = incl a` is the action +of a reference inclusion (for instance the vacuum representation +`Subtype.val`), the conjugated representation agrees with that +inclusion on every operator where the intertwining condition holds — +i.e. on operators localised outside the DHR region. -/ +lemma conjBy_π_eq_of_intertwined {R : CStarRep A} + {K : Type*} [ComplexHilbertSpace K] + (U : UnitaryMap R.H K) + {a : A} {T : K →L[ℂ] K} + (hUa : U.toContinuousLinearMap ∘L R.π a = T ∘L U.toContinuousLinearMap) : + (R.conjBy U).π a = T := by + -- From `U ∘L R.π a = T ∘L U`, post-compose with `U†`: + -- `U ∘L R.π a ∘L U† = T ∘L U ∘L U† = T` using `U U† = id`. + rw [conjBy_π_apply] + apply ContinuousLinearMap.ext + intro y + -- Apply `hUa` at the point `U† y`. + have hy := congrArg + (fun (f : R.H →L[ℂ] K) => f (U.toContinuousLinearMap† y)) hUa + simp only [ContinuousLinearMap.coe_comp', Function.comp_apply] at hy ⊢ + rw [hy] + -- Remaining: `T (U (U† y)) = T y`. + have hUU : U.toContinuousLinearMap + (U.toContinuousLinearMap† y) = y := by + have := congrArg + (fun (f : K →L[ℂ] K) => f y) U.comp_adjoint + simpa using this + rw [hUU] + +end CStarRep diff --git a/QuantumSystem/Algebra/CStarAlgebra/Representation/DirectSum.lean b/QuantumSystem/Algebra/CStarAlgebra/Representation/DirectSum.lean new file mode 100644 index 0000000..cae55ae --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/Representation/DirectSum.lean @@ -0,0 +1,365 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.Spectrum +public import Mathlib.Analysis.InnerProductSpace.l2Space +public import Mathlib.Data.Real.StarOrdered +public import QuantumSystem.Algebra.CStarAlgebra.Representation.Family +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation + +/-! +# Direct sum of a sector family + +For a sector family `F : SectorFamily A` on a non-unital C\*-algebra +`A`, this file constructs: + +* `F.directSumHilbert` — the `ℓ²`-direct sum Hilbert space + `⨁_{α : F.Index} (F.rep α).H`. +* `F.directSumRep` — the block-diagonal universal representation + `A →⋆ₙₐ[ℂ] 𝓑(F.directSumHilbert)`. +* `F.directSumRep_norm_le` — the operator-norm bound + `‖F.directSumRep a‖ ≤ ‖a‖`. + +The construction is criterion-agnostic: it depends only on the +indexed family of representations, not on any selection predicate +`P : CStarRep A → Prop`. Faithfulness of `directSumRep` requires that +the family separates points (`F.SeparatesPoints`); see the +`directSumRep_injective_of` theorem. + +## Comparison with `GNS.DirectSum` + +`QuantumSystem.Algebra.CStarAlgebra.GNS.DirectSum.Hilbert A` is the +analogous `ℓ²`-direct sum indexed by *all* `PureState A`, with each +state contributing its GNS Hilbert space. Two pure states whose GNS +representations are unitarily equivalent contribute the "same sector" +twice (once per state), so this "fat" direct sum is not a true sector +decomposition. The skeleton version +(`PureState.sectorFamily` filtered to a skeleton in +`Sector/PureState/Family.lean`) avoids the over-counting. + +## Main definitions + +* `SectorFamily.directSumHilbert` — the direct-sum Hilbert space. +* `SectorFamily.sectorComponent` / `.sectorEmbed` — coordinate + projection / embedding. +* `SectorFamily.directSumRep` — the block-diagonal universal + `*`-representation. +-/ + +@[expose] public section + +open ENNReal +open scoped Adjoint ComplexHilbertSpace + +namespace SectorFamily + +universe u v w + +variable {A : Type u} [NonUnitalCStarAlgebra A] + +/-- The `ℓ²` direct-sum Hilbert space of the family. -/ +noncomputable abbrev directSumHilbert (F : SectorFamily.{u, v, w} A) := + ↥(lp (fun α : F.Index => (F.rep α).H) 2) + +noncomputable instance (F : SectorFamily.{u, v, w} A) : + ComplexHilbertSpace F.directSumHilbert where + toNormedAddCommGroup := inferInstance + toInnerProductSpace := inferInstance + toCompleteSpace := inferInstance + +/-! ### Coordinate API + +For each `α : F.Index`, the `α`-th coordinate of the direct-sum +Hilbert space is `(F.rep α).H`. We provide: + +* `sectorComponent F α` — coordinate projection + `F.directSumHilbert →L[ℂ] (F.rep α).H`. +* `sectorEmbed F α` — coordinate embedding + `(F.rep α).H →ₗᵢ[ℂ] F.directSumHilbert` (Mathlib `lp.single` + packaged as a `LinearIsometry`). +-/ + +/-- The coordinate projection onto the `α`-th sector component, as a +continuous linear map. -/ +noncomputable def sectorComponent (F : SectorFamily.{u, v, w} A) + (α : F.Index) : F.directSumHilbert →L[ℂ] (F.rep α).H := + lp.evalCLM (𝕜 := ℂ) (fun α' : F.Index => (F.rep α').H) 2 α + +/-- The coordinate embedding of `(F.rep α).H` into the direct-sum +Hilbert space at the `α`-th component, as a linear isometry. -/ +noncomputable def sectorEmbed (F : SectorFamily.{u, v, w} A) + (α : F.Index) : (F.rep α).H →ₗᵢ[ℂ] F.directSumHilbert := + letI : DecidableEq F.Index := Classical.decEq _ + { toLinearMap := + (lp.singleContinuousLinearMap (𝕜 := ℂ) + (E := fun α' : F.Index => (F.rep α').H) 2 α).toLinearMap + norm_map' := fun x => + lp.norm_single (E := fun α' : F.Index => (F.rep α').H) + (by norm_num : (0 : ℝ≥0∞) < 2) α x } + +@[simp] lemma sectorEmbed_apply_coord (F : SectorFamily.{u, v, w} A) + (α : F.Index) (v : (F.rep α).H) : + (sectorEmbed F α v).val α = v := by + letI : DecidableEq F.Index := Classical.decEq _ + change (lp.single (E := fun α' : F.Index => (F.rep α').H) 2 α v) α = v + exact lp.single_apply_self + (E := fun α' : F.Index => (F.rep α').H) 2 α v + +lemma sectorEmbed_apply_coord_ne (F : SectorFamily.{u, v, w} A) + (α : F.Index) (v : (F.rep α).H) + {α' : F.Index} (h : α' ≠ α) : + (sectorEmbed F α v).val α' = 0 := by + letI : DecidableEq F.Index := Classical.decEq _ + change (lp.single (E := fun α'' : F.Index => (F.rep α'').H) 2 α v) α' = 0 + exact lp.single_apply_ne + (E := fun α'' : F.Index => (F.rep α'').H) 2 α v h + +@[simp] lemma sectorComponent_sectorEmbed (F : SectorFamily.{u, v, w} A) + (α : F.Index) (v : (F.rep α).H) : + sectorComponent F α (sectorEmbed F α v) = v := by + change (sectorEmbed F α v).val α = v + exact sectorEmbed_apply_coord F α v + +/-- Generic helper: from a `LinearIsometryEquiv` between a Hilbert space +`H` and the `α`-th fiber of the family, produce a `LinearIsometry` +embedding of `H` into the direct-sum Hilbert space. + +Defined generically (the family `F` stays a variable so that the +costly `directSumHilbert`-family typeclass synthesis only fires here, +not at downstream specialized call sites). -/ +noncomputable def sectorEmbedOfEquiv (F : SectorFamily.{u, v, w} A) + (α : F.Index) {H : Type v} [ComplexHilbertSpace H] + (eq : H ≃ₗᵢ[ℂ] (F.rep α).H) : + H →ₗᵢ[ℂ] F.directSumHilbert := + (sectorEmbed F α).comp eq.toLinearIsometry + +/-- The component-wise action of `a : A` on a fiber. -/ +noncomputable def componentWiseMap (F : SectorFamily.{u, v, w} A) (a : A) : + ∀ α : F.Index, 𝓑((F.rep α).H) := + fun α => (F.rep α).π a + +/-- Norm bound on the component-wise action: each fibre is bounded by `‖a‖`. -/ +lemma componentWiseMap_norm_le (F : SectorFamily.{u, v, w} A) + (a : A) (α : F.Index) (v : (F.rep α).H) : + ‖componentWiseMap F a α v‖ ≤ ‖a‖ * ‖v‖ := by + -- `‖π_α(a) v‖ ≤ ‖π_α(a)‖ * ‖v‖ ≤ ‖a‖ * ‖v‖` since `‖π_α(a)‖ ≤ ‖a‖` + -- for any C\*-algebra `*`-homomorphism into `𝓑(H)`. + calc ‖componentWiseMap F a α v‖ + _ ≤ ‖componentWiseMap F a α‖ * ‖v‖ := + ContinuousLinearMap.le_opNorm _ _ + _ ≤ ‖a‖ * ‖v‖ := + mul_le_mul_of_nonneg_right + (NonUnitalStarAlgHom.norm_apply_le (φ := (F.rep α).π) a) + (norm_nonneg _) + +/-- The component-wise image of an `ℓ²` family stays in `ℓ²`. -/ +lemma componentWiseMap_memℓp (F : SectorFamily.{u, v, w} A) (a : A) + (x : F.directSumHilbert) : + Memℓp (fun α => componentWiseMap F a α (x.val α)) 2 := by + have hx : Memℓp x.val 2 := x.property + rw [memℓp_gen_iff zero_lt_two] at hx ⊢ + have h2 : (2 : ℝ≥0∞).toReal = 2 := by norm_num + simp only [h2] at hx ⊢ + refine Summable.of_nonneg_of_le (fun α => by positivity) (fun α => ?_) + (Summable.mul_left (‖a‖ ^ 2) hx) + have h := componentWiseMap_norm_le F a α (x.val α) + change ‖componentWiseMap F a α (x.val α)‖ ^ (2 : ℝ) ≤ ‖a‖ ^ 2 * ‖x.val α‖ ^ 2 + trans (‖a‖ * ‖x.val α‖) ^ (2 : ℝ) + · gcongr + · rw [Real.mul_rpow (norm_nonneg _) (norm_nonneg _)] + norm_cast + +/-- The component-wise operator norm bound, summed in `ℓ²`. -/ +lemma componentWiseMap_norm_bound (F : SectorFamily.{u, v, w} A) (a : A) + (x : F.directSumHilbert) : + ‖(⟨fun α => componentWiseMap F a α (x.val α), + componentWiseMap_memℓp F a x⟩ : F.directSumHilbert)‖ ≤ ‖a‖ * ‖x‖ := by + have h2pos : (0 : ℝ) < (2 : ℝ≥0∞).toReal := by norm_num + have h2 : (2 : ℝ≥0∞).toReal = 2 := by norm_num + rw [lp.norm_eq_tsum_rpow h2pos, lp.norm_eq_tsum_rpow h2pos] + simp only [h2] + have hsum1 : Summable fun α => ‖componentWiseMap F a α (x.val α)‖ ^ (2 : ℝ) := by + have := componentWiseMap_memℓp F a x + rw [memℓp_gen_iff zero_lt_two] at this + simp only [h2] at this + exact this + have hsum2 : Summable fun α => ‖x.val α‖ ^ (2 : ℝ) := by + have : Memℓp x.val 2 := x.property + rw [memℓp_gen_iff zero_lt_two] at this + simp only [h2] at this + exact this + have sum_ineq : + ∑' α, ‖componentWiseMap F a α (x.val α)‖ ^ (2 : ℝ) ≤ + ‖a‖ ^ 2 * ∑' α, ‖x.val α‖ ^ (2 : ℝ) := by + rw [← tsum_mul_left] + apply tsum_le_of_sum_le' (by positivity) + intro s + calc ∑ α ∈ s, ‖componentWiseMap F a α (x.val α)‖ ^ (2 : ℝ) + _ ≤ ∑ α ∈ s, ‖a‖ ^ 2 * ‖x.val α‖ ^ (2 : ℝ) := by + gcongr with α _ + have h := componentWiseMap_norm_le F a α (x.val α) + trans (‖a‖ * ‖x.val α‖) ^ (2 : ℝ) + · gcongr + · rw [Real.mul_rpow (norm_nonneg _) (norm_nonneg _)] + norm_cast + _ ≤ ∑' α, ‖a‖ ^ 2 * ‖x.val α‖ ^ (2 : ℝ) := by + refine sum_le_hasSum _ (fun α _ => by positivity) + (Summable.hasSum (Summable.mul_left _ hsum2)) + trans ((‖a‖ ^ 2 * ∑' α, ‖x.val α‖ ^ (2 : ℝ)) ^ ((1 : ℝ) / 2)) + · gcongr + rw [Real.mul_rpow (sq_nonneg _) (tsum_nonneg fun α => by positivity)] + gcongr + rw [← Real.rpow_natCast ‖a‖ 2, ← Real.rpow_mul (norm_nonneg _)] + norm_num + +/-- The linear-map version of the block-diagonal action of `a` on the +direct-sum Hilbert space. -/ +noncomputable def directSumLinearMap (F : SectorFamily.{u, v, w} A) (a : A) : + F.directSumHilbert →ₗ[ℂ] F.directSumHilbert where + toFun x := ⟨fun α => componentWiseMap F a α (x.val α), + componentWiseMap_memℓp F a x⟩ + map_add' x y := by + apply Subtype.ext + funext α + simp only [lp.coeFn_add, Pi.add_apply, map_add] + map_smul' c x := by + apply Subtype.ext + funext α + simp only [lp.coeFn_smul, Pi.smul_apply, map_smul, RingHom.id_apply] + +/-- The bounded-operator version of the block-diagonal action of `a`. -/ +noncomputable def directSumCLM (F : SectorFamily.{u, v, w} A) (a : A) : + 𝓑(F.directSumHilbert) := + LinearMap.mkContinuous (F.directSumLinearMap a) ‖a‖ + (componentWiseMap_norm_bound F a) + +/-- Block-diagonality is compatible with the `*`-structure. -/ +lemma directSumCLM_adjoint (F : SectorFamily.{u, v, w} A) (a : A) : + (F.directSumCLM a)† = F.directSumCLM (star a) := by + refine ContinuousLinearMap.ext fun x => ?_ + apply ext_inner_right ℂ + intro y + rw [ContinuousLinearMap.adjoint_inner_left] + rw [lp.inner_eq_tsum, lp.inner_eq_tsum] + congr with α + simp only [directSumCLM, LinearMap.mkContinuous_apply, directSumLinearMap, + LinearMap.coe_mk, AddHom.coe_mk, componentWiseMap] + rw [← ContinuousLinearMap.adjoint_inner_left] + rw [map_star] + rw [ContinuousLinearMap.star_eq_adjoint] + +/-- The block-diagonal universal `*`-representation associated with a +sector family. -/ +noncomputable def directSumRep (F : SectorFamily.{u, v, w} A) : + A →⋆ₙₐ[ℂ] 𝓑(F.directSumHilbert) where + toFun a := F.directSumCLM a + map_mul' a b := by + ext x : 1 + apply Subtype.ext + funext α + simp only [directSumCLM, LinearMap.mkContinuous_apply, directSumLinearMap, + LinearMap.coe_mk, AddHom.coe_mk, ContinuousLinearMap.mul_apply] + rw [componentWiseMap, componentWiseMap, componentWiseMap] + conv_lhs => rw [map_mul] + rfl + map_zero' := by + ext x : 1 + apply Subtype.ext + funext α + simp only [directSumCLM, LinearMap.mkContinuous_apply, directSumLinearMap, + LinearMap.coe_mk, AddHom.coe_mk, ContinuousLinearMap.zero_apply] + rw [componentWiseMap] + rw [map_zero] + rfl + map_add' a b := by + ext x : 1 + apply Subtype.ext + funext α + simp only [directSumCLM, LinearMap.mkContinuous_apply, directSumLinearMap, + LinearMap.coe_mk, AddHom.coe_mk, ContinuousLinearMap.add_apply, + lp.coeFn_add, Pi.add_apply] + rw [componentWiseMap, componentWiseMap, componentWiseMap] + conv_lhs => rw [map_add] + rfl + map_smul' c a := by + ext x : 1 + apply Subtype.ext + funext α + simp only [directSumCLM, LinearMap.mkContinuous_apply, directSumLinearMap, + LinearMap.coe_mk, AddHom.coe_mk, ContinuousLinearMap.smul_apply, + lp.coeFn_smul, Pi.smul_apply] + rw [componentWiseMap, componentWiseMap] + conv_lhs => rw [map_smul] + rfl + map_star' a := by + rw [← directSumCLM_adjoint] + rfl + +/-- The operator-norm bound `‖F.directSumRep a‖ ≤ ‖a‖`. -/ +lemma directSumRep_norm_le (F : SectorFamily.{u, v, w} A) (a : A) : + ‖F.directSumRep a‖ ≤ ‖a‖ := by + change ‖F.directSumCLM a‖ ≤ ‖a‖ + exact LinearMap.mkContinuous_norm_le _ (norm_nonneg _) _ + +/-- A sector family *separates points* if the family of representations +separates `A`: whenever every member annihilates `a`, we have `a = 0`. -/ +def SeparatesPoints (F : SectorFamily.{u, v, w} A) : Prop := + ∀ a : A, (∀ α : F.Index, (F.rep α).π a = 0) → a = 0 + +/-- **Faithfulness of the direct-sum representation** under a +points-separation condition on the family: if the family separates `A`, +then `F.directSumRep` is injective. -/ +theorem directSumRep_injective_of (F : SectorFamily.{u, v, w} A) + (h_sep : F.SeparatesPoints) : + Function.Injective F.directSumRep := by + intro a b hab + -- It suffices to show `a - b = 0`. + rw [← sub_eq_zero] + -- `F.directSumRep (a - b) = 0` from `hab` by linearity. + have h_diff_zero : F.directSumRep (a - b) = 0 := by + rw [map_sub, hab, sub_self] + -- `F.directSumCLM (a - b) = 0` (the underlying bounded operator). + have h_clm_zero : F.directSumCLM (a - b) = 0 := h_diff_zero + -- Apply separation: every family member annihilates `a - b`. + apply h_sep + intro α + ext v + -- Goal: `(F.rep α).π (a - b) v = 0`. + classical + -- Construct the `δ_α`-vector in `directSumHilbert` carrying `v` in the + -- `α`-th component and `0` elsewhere. + let f : ∀ α' : F.Index, (F.rep α').H := + fun α' => if h : α' = α then h ▸ v else 0 + have hf_mem : Memℓp f 2 := by + rw [memℓp_gen_iff zero_lt_two] + have h2 : (2 : ℝ≥0∞).toReal = 2 := by norm_num + simp only [h2] + have h_eq : (fun α' => ‖f α'‖ ^ (2 : ℝ)) = + fun α' => if α' = α then ‖v‖ ^ 2 else 0 := by + ext α' + simp only [f] + by_cases h : α' = α + · subst h; simp + · simp only [dif_neg h, if_neg h]; simp + rw [h_eq] + apply summable_of_hasFiniteSupport + have : + Function.support (fun α' => if α' = α then ‖v‖ ^ 2 else 0) ⊆ {α} := by + intro α' hα' + simp only [Function.mem_support, ne_eq, ite_eq_right_iff, + Set.mem_singleton_iff] at hα' ⊢ + by_contra h + simp [h] at hα' + exact Set.Finite.subset (Set.finite_singleton α) this + let x : F.directSumHilbert := ⟨f, hf_mem⟩ + have hx_α : x.val α = v := by + simp only [x, f]; simp + -- Apply `h_clm_zero` to `x`. + have h0 : F.directSumCLM (a - b) x = 0 := by simp [h_clm_zero] + have hα0 : (F.directSumCLM (a - b) x).val α = 0 := by + simpa using congrArg (fun y : F.directSumHilbert => y.val α) h0 + -- Unfold the `α`-coordinate. + have hcomp : componentWiseMap F (a - b) α (x.val α) = 0 := by + simpa [directSumCLM, directSumLinearMap] using hα0 + simpa [componentWiseMap, hx_α] using hcomp + +end SectorFamily diff --git a/QuantumSystem/Algebra/CStarAlgebra/Representation/Family.lean b/QuantumSystem/Algebra/CStarAlgebra/Representation/Family.lean new file mode 100644 index 0000000..d41cfa9 --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/Representation/Family.lean @@ -0,0 +1,82 @@ +module + +public import QuantumSystem.Algebra.CStarAlgebra.Representation.UnitaryEquiv + +/-! +# Sector families + +A *sector family* `F : SectorFamily A` for a non-unital C\*-algebra `A` +is an indexed family of `CStarRep`s. In sector theory the typical +intent is that `F` enumerates a *complete system of representatives* +for the unitary-equivalence classes of representations satisfying some +physical selection condition `P` (DHR, KMS, cone-localised, ...): for +every physical representation `R` there is some index `α` with +`R ≃ F.rep α`, and indices give pairwise inequivalent representations. + +The selection condition `P` and the completeness/skeleton conditions are +*separate* structures (`SectorFamily.IsComplete`, `IsSkeleton`), so the +basic `SectorFamily` data carries no condition — it is just an indexed +family of representations. The direct sum +`SectorFamily.directSumHilbert` (in `CStarAlgebra/Representation/DirectSum.lean`) and its +universal `*`-representation `directSumRep` need only the family data. + +## Design rationale + +Compared to a quotient-first API such as +`Sector 𝒞 := Quotient 𝒞.equiv`, the family-first API avoids +`Classical.choice` / `Quotient.out` at the representative-selection level. +Sector representatives are explicit values of `F.rep α`, not output of +`Quotient.out`. This is the natural form used in the +Doplicher–Haag–Roberts and Naaijkens literature, where DHR sectors are +first constructed from localized endomorphisms and only *afterwards* +identified up to unitary equivalence. + +## Main definitions + +* `SectorFamily A` — an indexed family of `CStarRep`s. +* `SectorFamily.IsPhysical` — every member satisfies `P`. +* `SectorFamily.IsComplete` — every `P`-representation is unitarily + equivalent to some `F.rep α`. +* `SectorFamily.IsSkeleton` — `IsComplete` and pairwise non-equivalent. +-/ + +@[expose] public section + +universe u v w + +/-- An indexed family of `CStarRep`s of a non-unital C\*-algebra `A`, +parametrised by an arbitrary index type. -/ +structure SectorFamily (A : Type u) [NonUnitalCStarAlgebra A] where + /-- The index type. -/ + Index : Type w + /-- The representation at each index. -/ + rep : Index → CStarRep.{u, v} A + +namespace SectorFamily + +variable {A : Type u} [NonUnitalCStarAlgebra A] + +/-- Every member of the family satisfies the predicate `P`. -/ +structure IsPhysical (F : SectorFamily.{u, v, w} A) + (P : CStarRep.{u, v} A → Prop) : Prop where + /-- Each `F.rep α` is physical. -/ + isPhysical : ∀ α, P (F.rep α) + +/-- The family `F` is a *complete system of physical representatives*: +every representation satisfying `P` is unitarily equivalent to some +`F.rep α`. -/ +structure IsComplete (F : SectorFamily.{u, v, w} A) + (P : CStarRep.{u, v} A → Prop) : Prop extends F.IsPhysical P where + /-- Every `P`-representation is unitarily equivalent to some `F.rep α`. -/ + complete : ∀ R : CStarRep.{u, v} A, P R → ∃ α, + Nonempty (CStarRep.UnitaryEquiv R (F.rep α)) + +/-- The family `F` is a *skeleton* for the `P`-representations: it is +complete and any two indices give non-equivalent representations. -/ +structure IsSkeleton (F : SectorFamily.{u, v, w} A) + (P : CStarRep.{u, v} A → Prop) : Prop extends F.IsComplete P where + /-- Distinct indices give non-equivalent representations. -/ + pairwise : ∀ α β, + Nonempty (CStarRep.UnitaryEquiv (F.rep α) (F.rep β)) → α = β + +end SectorFamily diff --git a/QuantumSystem/Algebra/CStarAlgebra/Representation/Hom.lean b/QuantumSystem/Algebra/CStarAlgebra/Representation/Hom.lean new file mode 100644 index 0000000..8c61b87 --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/Representation/Hom.lean @@ -0,0 +1,121 @@ +module + +public import QuantumSystem.Algebra.CStarAlgebra.Representation.UnitaryEquiv +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation + +/-! +# Intertwiners between `CStarRep`s + +A *morphism* `T : CStarRep.Hom R₁ R₂` in the representation category of a +non-unital C\*-algebra `A` is a bounded linear map +`T : R₁.H →L[ℂ] R₂.H` that intertwines the two `*`-representations: + +``` +T ∘L R₁.π a = R₂.π a ∘L T for every a : A. +``` + +This is the standard notion of intertwiner used throughout sector +theory: subrepresentations, irreducibility, direct sums, Schur's lemma +and ultimately fusion of DHR sectors are all phrased in this language. +Unitary equivalence (`CStarAlgebra/Representation/UnitaryEquiv.lean`) is the subgroupoid of +isomorphisms in this category. + +## Main definitions + +* `CStarRep.Hom R₁ R₂` — bundled intertwiner. +* `CStarRep.Hom.id` — identity intertwiner. +* `CStarRep.Hom.comp` — composition of intertwiners. +* `CStarRep.UnitaryEquiv.toHom` — forget a unitary equivalence to an + intertwiner. +-/ + +@[expose] public section + +open scoped Adjoint + +namespace CStarRep + +variable {A : Type*} [NonUnitalCStarAlgebra A] + +/-- A morphism between two `CStarRep`s of the same C\*-algebra: a +bounded linear map intertwining the two `*`-representations. -/ +structure Hom (R₁ R₂ : CStarRep A) where + /-- The underlying bounded linear map. -/ + toContinuousLinearMap : R₁.H →L[ℂ] R₂.H + /-- The intertwining property. -/ + intertwines : + ∀ a : A, + toContinuousLinearMap ∘L R₁.π a = R₂.π a ∘L toContinuousLinearMap + +namespace Hom + +variable {R₁ R₂ R₃ : CStarRep A} + +/-- Two intertwiners are equal when their underlying continuous linear +maps agree. -/ +@[ext] lemma ext {T S : Hom R₁ R₂} + (h : T.toContinuousLinearMap = S.toContinuousLinearMap) : T = S := by + cases T; cases S; congr + +/-- The identity intertwiner. -/ +noncomputable def id (R : CStarRep A) : Hom R R where + toContinuousLinearMap := ContinuousLinearMap.id ℂ R.H + intertwines a := by ext x; simp + +/-- Composition of intertwiners. -/ +noncomputable def comp (S : Hom R₂ R₃) (T : Hom R₁ R₂) : Hom R₁ R₃ where + toContinuousLinearMap := + S.toContinuousLinearMap ∘L T.toContinuousLinearMap + intertwines a := by + -- `S ∘L T ∘L π₁ a = S ∘L (π₂ a ∘L T) = (S ∘L π₂ a) ∘L T = (π₃ a ∘L S) ∘L T`. + ext x + have hT := congrArg (fun (f : R₁.H →L[ℂ] R₂.H) => f x) (T.intertwines a) + have hS := congrArg (fun (f : R₂.H →L[ℂ] R₃.H) => f (T.toContinuousLinearMap x)) + (S.intertwines a) + simp only [ContinuousLinearMap.coe_comp', Function.comp_apply] at hT hS ⊢ + rw [← hT] at hS + exact hS + +@[simp] lemma id_toContinuousLinearMap (R : CStarRep A) : + (Hom.id R).toContinuousLinearMap = ContinuousLinearMap.id ℂ R.H := rfl + +@[simp] lemma comp_toContinuousLinearMap (S : Hom R₂ R₃) (T : Hom R₁ R₂) : + (S.comp T).toContinuousLinearMap = + S.toContinuousLinearMap ∘L T.toContinuousLinearMap := rfl + +@[simp] lemma id_comp (T : Hom R₁ R₂) : (Hom.id R₂).comp T = T := by + ext; simp + +@[simp] lemma comp_id (T : Hom R₁ R₂) : T.comp (Hom.id R₁) = T := by + ext; simp + +lemma comp_assoc {R₄ : CStarRep A} + (U : Hom R₃ R₄) (S : Hom R₂ R₃) (T : Hom R₁ R₂) : + (U.comp S).comp T = U.comp (S.comp T) := by + ext; simp [ContinuousLinearMap.comp_assoc] + +end Hom + +namespace UnitaryEquiv + +variable {R₁ R₂ : CStarRep A} + +/-- A unitary equivalence is in particular an intertwiner. -/ +noncomputable def toHom (U : UnitaryEquiv R₁ R₂) : Hom R₁ R₂ where + toContinuousLinearMap := U.unitary_map.toContinuousLinearMap + intertwines := U.intertwines + +@[simp] lemma toHom_toContinuousLinearMap (U : UnitaryEquiv R₁ R₂) : + U.toHom.toContinuousLinearMap = U.unitary_map.toContinuousLinearMap := rfl + +@[simp] lemma symm_toHom (U : UnitaryEquiv R₁ R₂) : + U.symm.toHom.toContinuousLinearMap = + U.unitary_map.toContinuousLinearMap† := rfl + +@[simp] lemma refl_toHom (R : CStarRep A) : + (UnitaryEquiv.refl R).toHom = Hom.id R := by + ext; rfl + +end UnitaryEquiv + +end CStarRep diff --git a/QuantumSystem/Algebra/CStarAlgebra/Representation/Irreducible.lean b/QuantumSystem/Algebra/CStarAlgebra/Representation/Irreducible.lean new file mode 100644 index 0000000..1c27020 --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/Representation/Irreducible.lean @@ -0,0 +1,115 @@ +module + +public import QuantumSystem.Algebra.CStarAlgebra.Representation.UnitaryEquiv +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation + +/-! +# Irreducibility for `CStarRep` + +A `*`-representation `R : CStarRep A` is *(topologically) irreducible* +when its only closed invariant subspaces are `⊥` and `⊤`. This file +lifts the irreducibility predicate from `GNS.Representation` (where it +is parameterised by a state `ω`) to the more general setting of +`CStarRep A` (no cyclic vector / no state attachment), and proves that +irreducibility is preserved under unitary equivalence — a fact +required by sector well-definedness (a sector is an equivalence class +of irreducible representations, so the irreducibility predicate must +descend to the quotient). + +## Main definitions + +* `CStarRep.IsInvariant R W` — `W` is stable under every `R.π a`. +* `CStarRep.IsIrreducible R` — the only closed `R`-invariant + submodules are `⊥` and `⊤`. + +## Main results + +* `CStarRep.UnitaryEquiv.isIrreducible_iff` — irreducibility transfers + along a unitary equivalence. +-/ + +@[expose] public section + +open scoped Adjoint + +namespace CStarRep + +variable {A : Type*} [NonUnitalCStarAlgebra A] + +/-- A submodule `W` of the carrier of a representation is *invariant* if +it is stable under the action of every `R.π a`. -/ +def IsInvariant (R : CStarRep A) (W : Submodule ℂ R.H) : Prop := + ∀ a : A, W.map (R.π a).toLinearMap ≤ W + +/-- A representation is (topologically) irreducible if the only closed +invariant submodules are `⊥` and `⊤`. -/ +def IsIrreducible (R : CStarRep A) : Prop := + ∀ W : Submodule ℂ R.H, + IsClosed (W : Set R.H) → R.IsInvariant W → (W = ⊥ ∨ W = ⊤) + +namespace UnitaryEquiv + +/-- The forward direction of `isIrreducible_iff`: a unitary equivalence +transports irreducibility. -/ +private lemma isIrreducible_of {R₁ R₂ : CStarRep A} + (U : UnitaryEquiv R₁ R₂) (h₁ : R₁.IsIrreducible) : R₂.IsIrreducible := by + intro W hW_closed hW_inv + -- Pull `W` back along `U` to obtain a closed invariant submodule `W'` of `R₁.H`. + let f : R₁.H →L[ℂ] R₂.H := U.unitary_map.toContinuousLinearMap + let W' : Submodule ℂ R₁.H := W.comap f.toLinearMap + have hW'_closed : IsClosed (W' : Set R₁.H) := + hW_closed.preimage f.continuous + have hW'_inv : R₁.IsInvariant W' := by + intro a y hy + obtain ⟨x, hx_mem, rfl⟩ := hy + -- `hx_mem : x ∈ W'` means `f x ∈ W`. + -- Goal: `(R₁.π a) x ∈ W'`, i.e. `f ((R₁.π a) x) ∈ W`. + change f ((R₁.π a) x) ∈ W + -- Use the intertwining property `f ∘L R₁.π a = R₂.π a ∘L f`. + have hI := congrArg (fun (g : R₁.H →L[ℂ] R₂.H) => g x) (U.intertwines a) + simp only [ContinuousLinearMap.coe_comp', Function.comp_apply] at hI + rw [show f ((R₁.π a) x) = (R₂.π a) (f x) from hI] + -- `f x ∈ W` and `W` is invariant under `R₂.π a`. + have hfx : f x ∈ W := hx_mem + have := hW_inv a (Submodule.mem_map.mpr ⟨f x, hfx, rfl⟩) + exact this + rcases h₁ W' hW'_closed hW'_inv with hbot | htop + · -- `W' = ⊥`: then every `w ∈ W` has `w = f x` with `x ∈ W' = ⊥`, hence `x = 0`, so `w = 0`. + refine Or.inl ?_ + refine le_antisymm ?_ bot_le + intro w hw + -- Surjectivity of `f`: `w = f (f† w)` since `f ∘L f† = 1`. + have hsurj : f (f† w) = w := by + have := congrArg (fun (g : R₂.H →L[ℂ] R₂.H) => g w) U.unitary_map.comp_adjoint + simpa using this + set x := f† w with hx_def + have hx_mem : x ∈ W' := by + change f x ∈ W + rw [hx_def]; rw [hsurj] + exact hw + have hx_zero : x = 0 := by + have : x ∈ (⊥ : Submodule ℂ R₁.H) := hbot ▸ hx_mem + simpa using this + have : w = 0 := by rw [← hsurj, hx_zero]; simp + exact this ▸ Submodule.zero_mem _ + · -- `W' = ⊤`: then every `x ∈ R₁.H` has `f x ∈ W`; by surjectivity, every `w ∈ R₂.H` is in `W`. + refine Or.inr ?_ + refine le_antisymm le_top ?_ + intro w _ + have hsurj : f (f† w) = w := by + have := congrArg (fun (g : R₂.H →L[ℂ] R₂.H) => g w) U.unitary_map.comp_adjoint + simpa using this + have hx_mem : f† w ∈ W' := + htop ▸ Submodule.mem_top + have hfx : f (f† w) ∈ W := hx_mem + rw [hsurj] at hfx + exact hfx + +/-- Irreducibility transfers along unitary equivalence. -/ +lemma isIrreducible_iff {R₁ R₂ : CStarRep A} (U : UnitaryEquiv R₁ R₂) : + R₁.IsIrreducible ↔ R₂.IsIrreducible := + ⟨isIrreducible_of U, isIrreducible_of U.symm⟩ + +end UnitaryEquiv + +end CStarRep diff --git a/QuantumSystem/Algebra/CStarAlgebra/Representation/UnitaryEquiv.lean b/QuantumSystem/Algebra/CStarAlgebra/Representation/UnitaryEquiv.lean new file mode 100644 index 0000000..350e72a --- /dev/null +++ b/QuantumSystem/Algebra/CStarAlgebra/Representation/UnitaryEquiv.lean @@ -0,0 +1,190 @@ +module + +public import QuantumSystem.Algebra.CStarAlgebra.Representation +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation + +/-! +# Unitary equivalence of `CStarRep`s + +Two non-unital `*`-representations `R₁ R₂ : CStarRep A` are *unitarily +equivalent* if there exists a unitary map `U : R₁.H → R₂.H` intertwining +the two representations: + +``` +U ∘L R₁.π a = R₂.π a ∘L U for every a : A +``` + +This is the standard notion used in sector theory: representations in +the same unitary-equivalence class are physically indistinguishable. +Compared with `GNS.Representation.UnitaryEquiv` (which additionally +requires the unitary to identify the GNS cyclic vectors of two triplets +for the *same* state `ω`), this notion drops the cyclic-vector +compatibility and applies to two general representations of possibly +unrelated origin. + +## Main definitions + +* `CStarRep.UnitaryEquiv R₁ R₂` — the data of a unitary intertwiner. +* `CStarRep.UnitaryEquiv.refl` / `.symm` / `.trans` — equivalence + closure. +* `CStarRep.unitarySetoid` — the corresponding `Setoid` on `CStarRep A`, + whose underlying relation is `Nonempty ∘ UnitaryEquiv`. +-/ + +@[expose] public section + +open scoped Adjoint + +namespace CStarRep + +variable {A : Type*} [NonUnitalCStarAlgebra A] + +/-- A unitary equivalence between two `CStarRep`s of the same C\*-algebra +`A`: a unitary map between the underlying Hilbert spaces that intertwines +the two `*`-representations. -/ +structure UnitaryEquiv (R₁ R₂ : CStarRep A) where + /-- The underlying unitary between the Hilbert spaces. -/ + unitary_map : UnitaryMap R₁.H R₂.H + /-- The intertwining property. -/ + intertwines : + ∀ a : A, + unitary_map.toContinuousLinearMap ∘L R₁.π a = + R₂.π a ∘L unitary_map.toContinuousLinearMap + +namespace UnitaryEquiv + +/-- Identity unitary equivalence. -/ +noncomputable def refl (R : CStarRep A) : UnitaryEquiv R R where + unitary_map := + { toContinuousLinearMap := ContinuousLinearMap.id ℂ R.H + adjoint_comp := by + rw [ContinuousLinearMap.adjoint_id] + ext x; simp + comp_adjoint := by + rw [ContinuousLinearMap.adjoint_id] + ext x; simp } + intertwines a := by ext x; simp + +/-- Inverse of a unitary equivalence: take the adjoint of the unitary map. -/ +noncomputable def symm {R₁ R₂ : CStarRep A} (U : UnitaryEquiv R₁ R₂) : + UnitaryEquiv R₂ R₁ where + unitary_map := + { toContinuousLinearMap := U.unitary_map.toContinuousLinearMap† + adjoint_comp := by + rw [ContinuousLinearMap.adjoint_adjoint] + exact U.unitary_map.comp_adjoint + comp_adjoint := by + rw [ContinuousLinearMap.adjoint_adjoint] + exact U.unitary_map.adjoint_comp } + intertwines a := by + -- From `U ∘L π₁ a = π₂ a ∘L U`, taking adjoints both sides yields + -- `(π₁ a)† ∘L U† = U† ∘L (π₂ a)†`; rewriting (πᵢ a)† = πᵢ (star a) + -- gives `π₁ (star a) ∘L U† = U† ∘L π₂ (star a)`. Substituting + -- `a ↦ star a` (and using `star (star a) = a`) yields the goal. + have h := U.intertwines (star a) + have h' : + (U.unitary_map.toContinuousLinearMap ∘L R₁.π (star a))† = + (R₂.π (star a) ∘L U.unitary_map.toContinuousLinearMap)† := by + rw [h] + rw [ContinuousLinearMap.adjoint_comp, ContinuousLinearMap.adjoint_comp] at h' + -- Now `h' : (π₁ (star a))† ∘L U† = U† ∘L (π₂ (star a))†`. + -- It remains to rewrite `(πᵢ (star a))† = πᵢ a` via `(π a)† = π (star a)` and `star_star`. + have hπ₁ : (R₁.π (star a))† = R₁.π a := by + have := (R₁.π).map_star' (star a) + rw [ContinuousLinearMap.star_eq_adjoint, star_star] at this + exact this.symm + have hπ₂ : (R₂.π (star a))† = R₂.π a := by + have := (R₂.π).map_star' (star a) + rw [ContinuousLinearMap.star_eq_adjoint, star_star] at this + exact this.symm + -- After rewriting: + rw [hπ₁, hπ₂] at h' + exact h'.symm + +/-- Composition of unitary equivalences. -/ +noncomputable def trans {R₁ R₂ R₃ : CStarRep A} + (U : UnitaryEquiv R₁ R₂) (V : UnitaryEquiv R₂ R₃) : + UnitaryEquiv R₁ R₃ where + unitary_map := + { toContinuousLinearMap := + V.unitary_map.toContinuousLinearMap ∘L U.unitary_map.toContinuousLinearMap + adjoint_comp := by + ext x + -- ((VU)† ∘L (VU)) x = U†(V†(V(Ux))) = U†(Ux) = x + simp only [ContinuousLinearMap.adjoint_comp, ContinuousLinearMap.coe_comp', + Function.comp_apply, ContinuousLinearMap.one_apply] + have hV : + V.unitary_map.toContinuousLinearMap† + (V.unitary_map.toContinuousLinearMap (U.unitary_map.toContinuousLinearMap x)) = + U.unitary_map.toContinuousLinearMap x := by + have := congrArg + (fun (f : R₂.H →L[ℂ] R₂.H) => f (U.unitary_map.toContinuousLinearMap x)) + V.unitary_map.adjoint_comp + simpa using this + rw [hV] + have hU : + U.unitary_map.toContinuousLinearMap† + (U.unitary_map.toContinuousLinearMap x) = x := by + have := congrArg + (fun (f : R₁.H →L[ℂ] R₁.H) => f x) U.unitary_map.adjoint_comp + simpa using this + exact hU + comp_adjoint := by + ext y + simp only [ContinuousLinearMap.adjoint_comp, ContinuousLinearMap.coe_comp', + Function.comp_apply, ContinuousLinearMap.one_apply] + have hU : + U.unitary_map.toContinuousLinearMap + (U.unitary_map.toContinuousLinearMap† + (V.unitary_map.toContinuousLinearMap† y)) = + V.unitary_map.toContinuousLinearMap† y := by + have := congrArg + (fun (f : R₂.H →L[ℂ] R₂.H) => f (V.unitary_map.toContinuousLinearMap† y)) + U.unitary_map.comp_adjoint + simpa using this + rw [hU] + have hV : + V.unitary_map.toContinuousLinearMap + (V.unitary_map.toContinuousLinearMap† y) = y := by + have := congrArg + (fun (f : R₃.H →L[ℂ] R₃.H) => f y) V.unitary_map.comp_adjoint + simpa using this + exact hV } + intertwines a := by + -- (VU) ∘L π₁ a = V ∘L (U ∘L π₁ a) = V ∘L (π₂ a ∘L U) = (V ∘L π₂ a) ∘L U + -- = (π₃ a ∘L V) ∘L U = π₃ a ∘L (V ∘L U) + ext x + have hU := congrArg (fun (f : R₁.H →L[ℂ] R₂.H) => f x) (U.intertwines a) + have hV := congrArg (fun (f : R₂.H →L[ℂ] R₃.H) => f (U.unitary_map.toContinuousLinearMap x)) + (V.intertwines a) + simp only [ContinuousLinearMap.coe_comp', Function.comp_apply] at hU hV ⊢ + rw [← hU] at hV + exact hV + +end UnitaryEquiv + +/-- The relation "there exists a unitary equivalence between `R₁` and `R₂`". -/ +def unitarilyEquivalent (R₁ R₂ : CStarRep A) : Prop := + Nonempty (UnitaryEquiv R₁ R₂) + +lemma unitarilyEquivalent.refl (R : CStarRep A) : unitarilyEquivalent R R := + ⟨UnitaryEquiv.refl R⟩ + +lemma unitarilyEquivalent.symm {R₁ R₂ : CStarRep A} + (h : unitarilyEquivalent R₁ R₂) : unitarilyEquivalent R₂ R₁ := + h.elim fun U => ⟨U.symm⟩ + +lemma unitarilyEquivalent.trans {R₁ R₂ R₃ : CStarRep A} + (h₁ : unitarilyEquivalent R₁ R₂) (h₂ : unitarilyEquivalent R₂ R₃) : + unitarilyEquivalent R₁ R₃ := + h₁.elim fun U => h₂.elim fun V => ⟨U.trans V⟩ + +/-- The `Setoid` of `CStarRep`s up to unitary equivalence. -/ +def unitarySetoid : Setoid (CStarRep A) where + r := unitarilyEquivalent + iseqv := + { refl := unitarilyEquivalent.refl + symm := unitarilyEquivalent.symm + trans := unitarilyEquivalent.trans } + +end CStarRep diff --git a/QuantumSystem/Algebra/CStarAlgebra/State.lean b/QuantumSystem/Algebra/CStarAlgebra/State.lean index 791b3e0..b9a9c76 100644 --- a/QuantumSystem/Algebra/CStarAlgebra/State.lean +++ b/QuantumSystem/Algebra/CStarAlgebra/State.lean @@ -97,9 +97,6 @@ variable (z c : ℂ) /-- The star of a complex number equals its conjugate. -/ lemma star_as_conj : star z = conj z := rfl -/-- The product of a complex number with its conjugate equals the squared norm. -/ -lemma star_mul_self_eq_normSq : star z * z = ‖z‖^2 := Complex.conj_mul' z - /-- Quadratic expansion of `ω(star (z • x + y) * (z • x + y))`. -/ lemma quadratic_expansion : ω (star (z • x + y) * (z • x + y)) = ‖z‖^2 * ω (star x * x) + conj z * ω (star x * y) + z * ω (star y * x) + ω (star y * y) := by @@ -175,18 +172,21 @@ lemma cauchy_schwarz_ineq : ‖ω (star y * x)‖^2 ≤ (ω (star x * x)) * (ω intro t obtain ⟨γ, hγ_norm, hγ_phase⟩ := Complex.phase_alignment (ω (star y * x)) let s : A := ((t : ℂ) * γ) • x + y - have hexp2 : (ω (star s * s)).re = (positiveReal ω x) * (t * t) + (2 * ‖ω (star y * x)‖ ) * t + (positiveReal ω y) := by + have hexp2 : (ω (star s * s)).re = + (positiveReal ω x) * (t * t) + (2 * ‖ω (star y * x)‖ ) * t + (positiveReal ω y) := by have hquad := quadratic_expansion (ω := ω) (x := x) (y := y) (z := ((t : ℂ) * γ)) have h1 : (‖(t : ℂ) * γ‖^2 : ℂ) = (t^2 : ℂ) := by rw [Complex.norm_mul, hγ_norm, mul_one, Complex.norm_real, pow_two, pow_two] norm_cast exact abs_mul_abs_self t - have h2 : conj ((t : ℂ) * γ) * ω (star x * y) + ((t : ℂ) * γ) * ω (star y * x) = 2 * t * ‖ω (star y * x)‖ := by + have h2 : conj ((t : ℂ) * γ) * ω (star x * y) + ((t : ℂ) * γ) * ω (star y * x) = + 2 * t * ‖ω (star y * x)‖ := by have : ((t : ℂ) * γ) * ω (star y * x) = t * (γ * ω (star y * x)) := by ring rw [conj_linear_combination_real, this, hγ_phase] norm_cast ring_nf - have hequiv : ω (star s * s) = (t^2 : ℂ) * ω (star x * x) + 2 * t * ‖ω (star y * x)‖ + ω (star y * y) := by + have hequiv : + ω (star s * s) = (t^2 : ℂ) * ω (star x * x) + 2 * t * ‖ω (star y * x)‖ + ω (star y * y) := by unfold s rw [hquad, h1, ← h2] ring_nf @@ -213,13 +213,9 @@ lemma kernel_degenerate_left (a : A) (hx : ω (star x * x) = 0) : ω (star a * x rw [hx, zero_mul] at this exact norm_eq_zero.mp (sq_eq_zero_iff.mp (le_antisymm (by exact_mod_cast this) (sq_nonneg _))) -/-- If `ω(star x * x) = 0`, then `ω(star x * a) = 0` for any `a`. -/ -lemma kernel_degenerate_right (a : A) (hx : ω (star x * x) = 0) : ω (star x * a) = 0 := by - rw [conj_sym (ω := ω) (x := a) (y := x), kernel_degenerate_left (ω := ω) (x := x) (a := a) hx] - simp - /-- If `ω(star x * x) = 0` and `ω(star y * y) = 0`, then `ω(star (x + y) * (x + y)) = 0`. -/ -lemma kernel_closed_under_add (hx : ω (star x * x) = 0) (hy : ω (star y * y) = 0) : ω (star (x + y) * (x + y)) = 0 := by +lemma kernel_closed_under_add (hx : ω (star x * x) = 0) (hy : ω (star y * y) = 0) : + ω (star (x + y) * (x + y)) = 0 := by calc ω (star (x + y) * (x + y)) = ω (star x * x + star x * y + star y * x + star y * y) := by rw [star_add, add_mul, mul_add, mul_add] @@ -227,7 +223,8 @@ lemma kernel_closed_under_add (hx : ω (star x * x) = 0) (hy : ω (star y * y) = _ = ω (star x * x) + ω (star x * y) + ω (star y * x) + ω (star y * y) := by rw [map_add, map_add, map_add] _ = 0 := by - rw [hx, hy, kernel_degenerate_left (ω := ω) (x := y) (a := x) hy, kernel_degenerate_left (ω := ω) (x := x) (a := y) hx] + rw [hx, hy, kernel_degenerate_left (ω := ω) (x := y) (a := x) hy, + kernel_degenerate_left (ω := ω) (x := x) (a := y) hx] ring /-- If `ω(star x * x) = 0`, then `ω(star (c • x) * (c • x)) = 0` for any scalar `c`. -/ @@ -320,23 +317,6 @@ lemma star_mul_bound (a b : A) : _ = ω (‖a‖ ^ 2 • (star b * b)) := by norm_cast _ = ω (star b * star a * a * b) + RCLike.ofReal (r : ℝ) := hr -/-- Real parts of a state are nonnegative on positive elements. -/ -lemma real_eval_nonneg_of_nonneg {a : A} (ha : 0 ≤ a) : 0 ≤ (ω a).re := by - obtain ⟨r, hr⟩ := nonneg_of_nonneg (ω := ω) ha - have hr_nonneg : 0 ≤ (r : ℝ) := by exact_mod_cast r.property - have hr_rewrite : (ω a).re = (r : ℝ) := by rw [hr]; simp - exact hr_rewrite ▸ hr_nonneg - -/-- States are monotone on real parts: `a ≤ b` implies `(ω a).re ≤ (ω b).re`. -/ -lemma real_eval_le_of_le {a b : A} (hab : a ≤ b) : (ω a).re ≤ (ω b).re := by - obtain ⟨r, hr⟩ := monotone (ω := ω) hab - have h_re : (ω b).re = (ω a).re + (r : ℝ) := by - have := congrArg Complex.re hr - simpa [Complex.add_re, RCLike.ofReal_re] using this - have hr_nonneg : 0 ≤ (r : ℝ) := by exact_mod_cast r.property - have h_le : (ω a).re ≤ (ω a).re + (r : ℝ) := le_add_of_nonneg_right hr_nonneg - exact h_re ▸ h_le - /-- If `ω(star x * x) = 0`, then `ω(x) = 0`. This uses the approximate unit. -/ lemma kernel_vanish_on_elem (hx : ω (star x * x) = 0) : ω x = 0 := by -- Prove by showing ‖ω(x)‖ < ε for all ε > 0 @@ -413,19 +393,4 @@ lemma approx_unit_cauchy_schwarz_bound (a e : A) (he_star : star e = e) (he_norm · simpa [h_ee_real, RCLike.ofReal_re] using h_ee_bound _ = (ω (star a * a)).re := mul_one _ - -/-- For nonzero `a`, the element `star a * a` is also nonzero. -/ -lemma star_mul_self_ne_zero_of_ne_zero {a : A} (ha : a ≠ 0) : star a * a ≠ 0 := by - rw [← norm_ne_zero_iff] at ha ⊢ - rw [CStarRing.norm_star_mul_self] - intro h - have : ‖a‖ = 0 := by nlinarith [sq_nonneg ‖a‖] - exact ha this - - -/-- The norm of `star a * a` equals the square of the norm of `a`. -/ -lemma norm_star_mul_self_eq_sq (a : A) : ‖star a * a‖ = ‖a‖ ^ 2 := by - rw [pow_two] - exact CStarRing.norm_star_mul_self - end State diff --git a/QuantumSystem/Algebra/LocalNet.lean b/QuantumSystem/Algebra/LocalNet.lean deleted file mode 100644 index c2594ae..0000000 --- a/QuantumSystem/Algebra/LocalNet.lean +++ /dev/null @@ -1,718 +0,0 @@ -module - -public import QuantumSystem.Channel - -/-! -# Local Net of Matrix Algebras (finite-dim) - -This file defines the data of a **local net of matrix algebras** on a finite lattice. -An AQFT system assigns to each spacetime / lattice region `Λ` an -algebra `𝔄(Λ)` of observables, with **isotony** (`Λ₁ ⊆ Λ₂ ⟹ 𝔄(Λ₁) ⊆ 𝔄(Λ₂)`), **locality** -(disjoint regions commute), and—in the spacetime version—**covariance**. - -For finite-dimensional quantum spin systems, the construction specialises to: - -- a finite set of **sites** `L`, -- a local index type `ℂ^{n_x}` at each site `x ∈ L`, -- regions `Λ ∈ 𝒫(L)` (`Finset L.sites`), -- local algebra `𝔄(Λ) = ⊗_{x ∈ Λ} M_{n_x}(ℂ)` realised concretely as - `Matrix (Π s ∈ Λ, idx s) (Π s ∈ Λ, idx s) ℂ`. - -This file provides: - -1. the structure carrying the lattice + per-site Hilbert-space data, the derived region index - types, and the index-combiner equivalence relating `regionIdx Λ_total` to the product - `regionIdx Λ × regionIdx (Λ_total \ Λ)`; -2. the **restriction** (Schrödinger-picture partial trace): given regions `Λ ⊆ Λ_total`, - the restriction of a state on `𝔄(Λ_total)` to `𝔄(Λ)`. In density-matrix language this is - exactly the partial trace over the complementary region `Λ_total \ Λ`. - The restriction is the Schrödinger-picture dual of the algebra - inclusion `𝔄(Λ) ↪ 𝔄(Λ_total)`. There is no positional ("left/right") concept — the - operation is parameterised by the region itself. - -## Main definitions - -* `LocalNet` — data carrier: sites + per-site finite index types -* `LocalNet.regionIdx` — index type of a region (dependent product) -* `LocalNet.localAlgebra` — the matrix algebra at a region -* `LocalNet.densityMatrix` — density matrices at a region -* `LocalNet.combineIdx` — `regionIdx Λ × regionIdx (Λ_total \ Λ) ≃ regionIdx Λ_total` -* `LocalNet.includeAlgebra` — isotony embedding `𝔄(Λ) ↪ 𝔄(Λ_total)` -* `LocalNet.regionIdxInsertEquiv` — recursive split: `regionIdx (insert s Λ) ≃ localIdx s × regionIdx Λ` -* `LocalNet.regionIdxPairEquiv` / `regionIdxTripleEquiv` / `regionIdxTripleEquiv'` — - factorisation of `n`-element regions into per-site product types -* `LocalNet.regionIdxComplLeftSite` / `regionIdxComplRightSite` — - `regionIdx ({a, b} \ {a}) ≃ localIdx b` and its right-site dual - -The generic primitives above subsume any partite count; the bipartite / tripartite -factorisation specialisations are exposed below as `regionIdxPairEquiv`, -`regionIdxTripleEquiv`, and `regionIdxTripleEquiv'`. - -The partial-trace / restriction operations (`Matrix.restrict`, `Matrix.restrictKraus`, -`Matrix.QuantumChannel.restrict`, `DensityMatrix.restrict`, and the paper notation -`ρ ↾ Λ`) are defined in `QuantumSystem/Analysis/Matrix/PartialTrace.lean`. - -## References - -* Verch 2025 (`https://arxiv.org/abs/2507.00900`) -* Naaijkens 2012 (`https://repository.ubn.ru.nl/handle/2066/92737`) --/ - -@[expose] public section - -/-- Data for a finite-dimensional **local net of matrix algebras** on a finite lattice. - Each site `s : sites` carries a finite index type `localIdx s` whose cardinality is the - local Hilbert-space dimension. The local algebra at a region `Λ ⊆ sites` is then the - matrix algebra on the dependent product `Π s ∈ Λ, localIdx s`. -/ -structure LocalNet where - /-- Lattice of sites — `Fintype` for the finite-dim project scope. -/ - sites : Type* - [sitesFintype : Fintype sites] - [sitesDecEq : DecidableEq sites] - /-- Local Hilbert-space index type at each site. -/ - localIdx : sites → Type* - [localFintype : ∀ s, Fintype (localIdx s)] - [localDecEq : ∀ s, DecidableEq (localIdx s)] - -namespace LocalNet - -attribute [instance] sitesFintype sitesDecEq localFintype localDecEq - -variable (L : LocalNet) - -/-- Index type of a region: dependent product of local indices over the sites in `Λ`. -/ -abbrev regionIdx (Λ : Finset L.sites) : Type _ := ∀ s : Λ, L.localIdx s.val - -/-- A region's index type stays nonempty when restricting to a sub-region: any element of - `regionIdx Λ_total` restricts to an element of `regionIdx Λ` along `h : Λ ⊆ Λ_total`. -/ -lemma regionIdx_nonempty_of_subset {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - [hne : Nonempty (L.regionIdx Λ_total)] : Nonempty (L.regionIdx Λ) := - hne.elim fun f => ⟨fun s => f ⟨s.val, h s.property⟩⟩ - -/-- Local algebra `𝔄(Λ)` at a region — concrete matrix algebra over `ℂ`. -/ -abbrev localAlgebra (Λ : Finset L.sites) : Type _ := - Matrix (L.regionIdx Λ) (L.regionIdx Λ) ℂ - -/-- Density matrices on a region (positive semi-definite, trace 1). -/ -abbrev densityMatrix (Λ : Finset L.sites) : Type _ := - DensityMatrix (L.regionIdx Λ) - -/-! ### Combining region indices via disjoint union -/ - -/-- For `Λ ⊆ Λ_total`, the index type of the larger region splits as a product: - `regionIdx Λ × regionIdx (Λ_total \ Λ) ≃ regionIdx Λ_total`. This realises the tensor - factorisation `ℋ_Λ_total = ℋ_Λ ⊗ ℋ_{Λ_total \ Λ}` underlying isotony and partial trace. -/ -def combineIdx {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - (L.regionIdx Λ × L.regionIdx (Λ_total \ Λ)) ≃ L.regionIdx Λ_total where - toFun ab s := - if hs : s.val ∈ Λ then ab.1 ⟨s.val, hs⟩ - else ab.2 ⟨s.val, Finset.mem_sdiff.mpr ⟨s.property, hs⟩⟩ - invFun f := - (fun s => f ⟨s.val, h s.property⟩, - fun s => f ⟨s.val, (Finset.mem_sdiff.mp s.property).1⟩) - left_inv := by - rintro ⟨a, b⟩ - ext1 - · funext s - have hs : s.val ∈ Λ := s.property - simp [hs] - · funext s - have hns : s.val ∉ Λ := (Finset.mem_sdiff.mp s.property).2 - simp [hns] - right_inv := by - intro f - funext s - by_cases hs : s.val ∈ Λ <;> simp [hs] - -/-! ### Pointwise behaviour of `combineIdx` -/ - -variable {L} in -@[simp] lemma combineIdx_apply_mem - {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (a : L.regionIdx Λ) (b : L.regionIdx (Λ_total \ Λ)) - (s : ↥Λ_total) (hs : s.val ∈ Λ) : - (L.combineIdx h (a, b)) s = a ⟨s.val, hs⟩ := by - simp only [combineIdx, Equiv.coe_fn_mk] - rw [dif_pos hs] - -variable {L} in -@[simp] lemma combineIdx_apply_not_mem - {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (a : L.regionIdx Λ) (b : L.regionIdx (Λ_total \ Λ)) - (s : ↥Λ_total) (hs : s.val ∉ Λ) : - (L.combineIdx h (a, b)) s - = b ⟨s.val, Finset.mem_sdiff.mpr ⟨s.property, hs⟩⟩ := by - simp only [combineIdx, Equiv.coe_fn_mk] - rw [dif_neg hs] - -/-- Cardinality factorisation for region indices induced by `combineIdx`. -/ -theorem card_regionIdx_total {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - Fintype.card (L.regionIdx Λ_total) = - Fintype.card (L.regionIdx Λ) * Fintype.card (L.regionIdx (Λ_total \ Λ)) := by - rw [← Fintype.card_prod] - exact Fintype.card_congr (L.combineIdx h).symm - -/-! ### Isotony embedding (algebra inclusion) - -The isotony embedding `𝔄(Λ) ↪ 𝔄(Λ_total)` is realised concretely as the tensor with -identity on the complement, `A ↦ A ⊗ I_{Λ_total \ Λ}`. We bundle it as a unital -`*`-algebra homomorphism (`StarAlgHom`) so that the AQFT axioms (Naaijkens 2012 §1.3 -line 211, Verch 2025 §1.2 axiom (i), Bratteli–Robinson Vol.2 §6.2) — preservation of -unit, product, and adjoint — are guaranteed at the type level. - -Pipeline: entry-wise underlying function `includeAlgebraFun` → algebraic identities -`includeAlgebraFun_{one,mul,star,...}` → bundled `includeAlgebra : _ →⋆ₐ[ℂ] _`. -/ - -/-- Entry-wise underlying function for `includeAlgebra`, defined separately so the - structural simp lemmas (`includeAlgebraFun_apply`, `..._apply_combineIdx`) reduce - by `rfl`/`simp` without going through the `StarAlgHom` coercion. -/ -noncomputable def includeAlgebraFun {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (X : L.localAlgebra Λ) : L.localAlgebra Λ_total := - Matrix.of fun s s' => - if ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 then - X ((L.combineIdx h).symm s).1 ((L.combineIdx h).symm s').1 - else 0 - -@[simp] lemma includeAlgebraFun_apply {Λ Λ_total : Finset L.sites} - (h : Λ ⊆ Λ_total) (X : L.localAlgebra Λ) (s s' : L.regionIdx Λ_total) : - L.includeAlgebraFun h X s s' = - if ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 then - X ((L.combineIdx h).symm s).1 ((L.combineIdx h).symm s').1 - else 0 := rfl - -/-- Entry-wise behaviour of `includeAlgebraFun` at combined indices: the off-diagonal - components in the complementary region vanish, leaving `X a a'` on the diagonal. -/ -@[simp] lemma includeAlgebraFun_apply_combineIdx - {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) (X : L.localAlgebra Λ) - (a a' : L.regionIdx Λ) (b b' : L.regionIdx (Λ_total \ Λ)) : - L.includeAlgebraFun h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b')) = - if b = b' then X a a' else 0 := by - simp [includeAlgebraFun, Equiv.symm_apply_apply] - -lemma includeAlgebraFun_zero {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - L.includeAlgebraFun h 0 = 0 := by - ext s s' - simp only [includeAlgebraFun_apply, Matrix.zero_apply] - split_ifs <;> rfl - -lemma includeAlgebraFun_add {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (X Y : L.localAlgebra Λ) : - L.includeAlgebraFun h (X + Y) = - L.includeAlgebraFun h X + L.includeAlgebraFun h Y := by - ext s s' - simp only [includeAlgebraFun_apply, Matrix.add_apply] - split_ifs with hbb - · rfl - · rw [add_zero] - -lemma includeAlgebraFun_smul {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (c : ℂ) (X : L.localAlgebra Λ) : - L.includeAlgebraFun h (c • X) = c • L.includeAlgebraFun h X := by - ext s s' - simp only [includeAlgebraFun_apply, Matrix.smul_apply, smul_eq_mul] - split_ifs with hbb - · rfl - · rw [mul_zero] - -lemma includeAlgebraFun_one {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - L.includeAlgebraFun h 1 = 1 := by - ext s s' - by_cases hss : s = s' - · subst hss - rw [includeAlgebraFun_apply, if_pos rfl, Matrix.one_apply_eq, Matrix.one_apply_eq] - · rw [includeAlgebraFun_apply, Matrix.one_apply_ne hss] - -- Translate `s ≠ s'` to a disjunction on the two coordinates of `(combineIdx h).symm`. - have hne : (L.combineIdx h).symm s ≠ (L.combineIdx h).symm s' := fun heq => - hss ((L.combineIdx h).symm.injective heq) - rw [Ne, Prod.ext_iff, not_and_or] at hne - by_cases h2 : ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 - · rw [if_pos h2] - rcases hne with h1 | h2' - · rw [Matrix.one_apply_ne h1] - · exact absurd h2 h2' - · rw [if_neg h2] - -lemma includeAlgebraFun_star {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (X : L.localAlgebra Λ) : - L.includeAlgebraFun h (star X) = star (L.includeAlgebraFun h X) := by - ext s s' - simp only [includeAlgebraFun_apply, Matrix.star_apply] - by_cases h2 : ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 - · rw [if_pos h2, if_pos h2.symm] - · rw [if_neg h2, if_neg (fun hh => h2 hh.symm), star_zero] - -lemma includeAlgebraFun_mul {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (X Y : L.localAlgebra Λ) : - L.includeAlgebraFun h (X * Y) = - L.includeAlgebraFun h X * L.includeAlgebraFun h Y := by - ext s s' - -- Express both rows/columns through `combineIdx` so the `_apply_combineIdx` simp lemma fires. - set sa := ((L.combineIdx h).symm s).1 with hsa - set sb := ((L.combineIdx h).symm s).2 with hsb - set s'a := ((L.combineIdx h).symm s').1 with hs'a - set s'b := ((L.combineIdx h).symm s').2 with hs'b - have hs : s = L.combineIdx h (sa, sb) := by - simp [sa, sb, Equiv.apply_symm_apply] - have hs' : s' = L.combineIdx h (s'a, s'b) := by - simp [s'a, s'b, Equiv.apply_symm_apply] - rw [hs, hs', includeAlgebraFun_apply_combineIdx, Matrix.mul_apply] - -- Reindex the RHS sum (over `regionIdx Λ_total`) via `combineIdx`. - rw [show ((L.includeAlgebraFun h X * L.includeAlgebraFun h Y) - (L.combineIdx h (sa, sb)) (L.combineIdx h (s'a, s'b))) = - ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), - L.includeAlgebraFun h X (L.combineIdx h (sa, sb)) (L.combineIdx h p) * - L.includeAlgebraFun h Y (L.combineIdx h p) (L.combineIdx h (s'a, s'b)) from by - rw [Matrix.mul_apply] - exact ((L.combineIdx h).sum_comp _).symm] - rw [Fintype.sum_prod_type] - simp_rw [includeAlgebraFun_apply_combineIdx] - -- Goal: - -- (if sb = s'b then ∑ a'', X sa a'' * Y a'' s'a else 0) - -- = ∑ a'', ∑ b'', (if sb = b'' then X sa a'' else 0) * (if b'' = s'b then Y a'' s'a else 0) - by_cases hbb : sb = s'b - · rw [if_pos hbb] - refine Finset.sum_congr rfl fun a'' _ => ?_ - rw [Finset.sum_eq_single sb - (fun b'' _ hb'' => by rw [if_neg fun heq => hb'' heq.symm, zero_mul]) - (fun h_not_mem => absurd (Finset.mem_univ sb) h_not_mem)] - rw [if_pos rfl, ← hbb, if_pos rfl] - · rw [if_neg hbb] - refine (Finset.sum_eq_zero fun a'' _ => ?_).symm - refine Finset.sum_eq_zero fun b'' _ => ?_ - by_cases hb_sb : sb = b'' - · subst hb_sb - rw [if_neg hbb, mul_zero] - · rw [if_neg hb_sb, zero_mul] - -lemma includeAlgebraFun_algebraMap {Λ Λ_total : Finset L.sites} - (h : Λ ⊆ Λ_total) (c : ℂ) : - L.includeAlgebraFun h ((algebraMap ℂ (L.localAlgebra Λ)) c) = - (algebraMap ℂ (L.localAlgebra Λ_total)) c := by - rw [Algebra.algebraMap_eq_smul_one, Algebra.algebraMap_eq_smul_one, - includeAlgebraFun_smul, includeAlgebraFun_one] - -/-- **Isotony embedding** `𝔄(Λ) ↪ 𝔄(Λ_total)`: tensor a local matrix with the identity on - the complementary region. Realises the inclusion `A ↦ A ⊗ I_{Λ_total \ Λ}` from - Naaijkens 2012 §1.3 line 211, Verch 2025 §1.2 axiom (i), Bratteli–Robinson Vol.2 §6.2. - Bundled as a unital `*`-algebra homomorphism so that `map_one`, `map_mul`, `map_star` - are available via the `StarAlgHom` API. Entry-wise: - `(includeAlgebra h X) s s' = X (combineIdx⁻¹ s).1 (combineIdx⁻¹ s').1` when the - complementary indices match, else `0`. -/ -noncomputable def includeAlgebra {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - L.localAlgebra Λ →⋆ₐ[ℂ] L.localAlgebra Λ_total where - toFun := L.includeAlgebraFun h - map_zero' := L.includeAlgebraFun_zero h - map_add' := L.includeAlgebraFun_add h - map_one' := L.includeAlgebraFun_one h - map_mul' := L.includeAlgebraFun_mul h - commutes' := L.includeAlgebraFun_algebraMap h - map_star' := L.includeAlgebraFun_star h - -/-- Entry-wise unfolding of `includeAlgebra h X`: at indices `(s, s')` of the larger - region, the embedded matrix equals `X` on the diagonal (in the complementary index) - and zero off-diagonal. -/ -@[simp] lemma includeAlgebra_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (X : L.localAlgebra Λ) (s s' : L.regionIdx Λ_total) : - L.includeAlgebra h X s s' = - if ((L.combineIdx h).symm s).2 = ((L.combineIdx h).symm s').2 then - X ((L.combineIdx h).symm s).1 ((L.combineIdx h).symm s').1 - else 0 := rfl - -/-- **Injectivity of the isotony embedding** (the `↪` of `𝔄(Λ) ↪ 𝔄(Λ_total)`): under - the standing AQFT non-degeneracy assumption that the complementary region has a - non-empty index type, `includeAlgebra h` is injective as a map of `*`-algebras. -/ -theorem includeAlgebra_injective {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - [hne : Nonempty (L.regionIdx (Λ_total \ Λ))] : - Function.Injective (L.includeAlgebra h) := by - rw [injective_iff_map_eq_zero] - intro X hX - ext a a' - obtain ⟨b⟩ := hne - have heq : L.includeAlgebra h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) = - 0 := by rw [hX]; rfl - have key : L.includeAlgebra h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) = - X a a' := by - change L.includeAlgebraFun h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) = X a a' - rw [includeAlgebraFun_apply_combineIdx, if_pos rfl] - rw [key] at heq - simpa using heq - -/-! ### Region equivalences - -Generic equivalences over arbitrary `Fintype` site sets — used both directly (for any -finite site set) and as building blocks for the bipartite (`regionIdxPairEquiv`) and -tripartite (`regionIdxTripleEquiv`, `regionIdxTripleEquiv'`) factorisations below. -/ - -variable (L : LocalNet) - -/-- Transport `regionIdx` along a Finset equality. -/ -def regionIdxCongr {Λ Λ' : Finset L.sites} (h : Λ = Λ') : - L.regionIdx Λ ≃ L.regionIdx Λ' := - h ▸ Equiv.refl _ - -@[simp] lemma regionIdxCongr_apply - {Λ Λ' : Finset L.sites} (h : Λ = Λ') (x : L.regionIdx Λ) - {s : L.sites} (hs : s ∈ Λ) (hs' : s ∈ Λ') : - (L.regionIdxCongr h x) ⟨s, hs'⟩ = x ⟨s, hs⟩ := by - subst h - rfl - -/-- Singleton region: `regionIdx {s} ≃ localIdx s`. -/ -def singletonRegionIdxEquiv (s : L.sites) : - L.regionIdx ({s} : Finset L.sites) ≃ L.localIdx s where - toFun f := f ⟨s, Finset.mem_singleton.mpr rfl⟩ - invFun x := fun ⟨v, hv⟩ => - (Finset.mem_singleton.mp hv).symm ▸ x - left_inv f := by - funext ⟨v, hv⟩ - have hvs : v = s := Finset.mem_singleton.mp hv - subst hvs - rfl - right_inv x := rfl - -@[simp] private lemma singletonRegionIdxEquiv_apply (s : L.sites) - (f : L.regionIdx ({s} : Finset L.sites)) : - L.singletonRegionIdxEquiv s f = f ⟨s, Finset.mem_singleton.mpr rfl⟩ := rfl - -/-! ### Generic n-partite primitives - -Building blocks for any finite site set: `regionIdx ∅ ≃ PUnit`, an `insert`-based -recursive split, and the universal product form `regionIdx Finset.univ ≃ Π s, localIdx s`. -Two- and three-element factor equivs are derived from the recursive split — adding more -partite counts (4, 5, ...) is now a one-liner with no new boilerplate. -/ - -/-- The empty region: `regionIdx ∅ ≃ PUnit`. The dependent product over the empty - subtype has a unique element. -/ -def regionIdxEmptyEquiv : L.regionIdx (∅ : Finset L.sites) ≃ PUnit where - toFun _ := PUnit.unit - invFun _ := fun s => absurd s.property (Finset.notMem_empty _) - left_inv f := by - funext s - exact absurd s.property (Finset.notMem_empty _) - right_inv _ := rfl - -/-- **Recursive split (region composition rule)**: for `s ∉ Λ`, - `regionIdx (insert s Λ) ≃ localIdx s × regionIdx Λ`. - - This is the core composition primitive — repeated application gives factorisation - of any finitely-enumerated region into per-site factors. Built from `combineIdx` - applied to the singleton `{s} ⊆ insert s Λ`, with the complementary region - `insert s Λ \ {s}` reducing to `Λ`. -/ -def regionIdxInsertEquiv {s : L.sites} {Λ : Finset L.sites} (hs : s ∉ Λ) : - L.regionIdx (insert s Λ) ≃ L.localIdx s × L.regionIdx Λ := - have h_sub : ({s} : Finset L.sites) ⊆ insert s Λ := - Finset.singleton_subset_iff.mpr (Finset.mem_insert_self s Λ) - have h_compl_eq : insert s Λ \ {s} = Λ := by - rw [Finset.insert_sdiff_of_mem _ (Finset.mem_singleton_self s), - Finset.sdiff_eq_self_iff_disjoint.mpr (Finset.disjoint_singleton_right.mpr hs)] - (L.combineIdx h_sub).symm.trans - (Equiv.prodCongr (L.singletonRegionIdxEquiv s) (L.regionIdxCongr h_compl_eq)) - -/-- The universal region: `regionIdx Finset.univ ≃ Π s : sites, localIdx s`. - Collapses the `Finset.univ`-subtype back to the underlying type. -/ -def regionIdxUnivEquiv : L.regionIdx (Finset.univ : Finset L.sites) ≃ - ∀ s : L.sites, L.localIdx s where - toFun f s := f ⟨s, Finset.mem_univ s⟩ - invFun g := fun ⟨s, _⟩ => g s - left_inv f := by funext ⟨s, _⟩; rfl - right_inv _ := rfl - -/-- **2-element factorisation**: `regionIdx {a, b} ≃ localIdx a × localIdx b` when - `a ≠ b`. Direct definition with concrete `toFun` so both projections evaluate by `rfl`. -/ -def regionIdxPairEquiv {a b : L.sites} (hab : a ≠ b) : - L.regionIdx ({a, b} : Finset L.sites) ≃ L.localIdx a × L.localIdx b where - toFun f := - (f ⟨a, Finset.mem_insert_self a {b}⟩, - f ⟨b, Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)⟩) - invFun ab := fun ⟨s, hs⟩ => - if h : s = a then h ▸ ab.1 - else - have hsb : s = b := by - rcases Finset.mem_insert.mp hs with h' | h' - · exact absurd h' h - · exact Finset.mem_singleton.mp h' - hsb ▸ ab.2 - left_inv f := by - funext ⟨s, hs⟩ - by_cases hsa : s = a - · subst hsa - simp - · have hsb : s = b := by - rcases Finset.mem_insert.mp hs with h' | h' - · exact absurd h' hsa - · exact Finset.mem_singleton.mp h' - subst hsb - simp [hsa] - right_inv ab := by - have hba : b ≠ a := fun h_eq => hab h_eq.symm - ext1 - · simp - · simp [hba] - -/-- Closed-form unfolding of `regionIdxPairEquiv` as a pair. -/ -@[simp] lemma regionIdxPairEquiv_apply {a b : L.sites} (hab : a ≠ b) - (f : L.regionIdx ({a, b} : Finset L.sites)) : - L.regionIdxPairEquiv hab f = - (f ⟨a, Finset.mem_insert_self a {b}⟩, - f ⟨b, Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)⟩) := rfl - -/-- Closed-form first projection of `regionIdxPairEquiv` — picks out the value at site `a`. -/ -@[simp] private lemma regionIdxPairEquiv_apply_fst {a b : L.sites} (hab : a ≠ b) - (f : L.regionIdx ({a, b} : Finset L.sites)) : - (L.regionIdxPairEquiv hab f).1 = f ⟨a, Finset.mem_insert_self a {b}⟩ := rfl - -/-- Closed-form second projection of `regionIdxPairEquiv` — picks out the value at site `b`. -/ -@[simp] private lemma regionIdxPairEquiv_apply_snd {a b : L.sites} (hab : a ≠ b) - (f : L.regionIdx ({a, b} : Finset L.sites)) : - (L.regionIdxPairEquiv hab f).2 = - f ⟨b, Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)⟩ := rfl - -/-- **3-element factorisation (right-associated)**: - `regionIdx {a, b, c} ≃ localIdx a × localIdx b × localIdx c` when the sites are - pairwise distinct. Direct definition with concrete `toFun` so all three projections - evaluate by `rfl`. -/ -def regionIdxTripleEquiv {a b c : L.sites} (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) : - L.regionIdx ({a, b, c} : Finset L.sites) ≃ - L.localIdx a × L.localIdx b × L.localIdx c where - toFun f := - (f ⟨a, Finset.mem_insert_self a {b, c}⟩, - f ⟨b, Finset.mem_insert_of_mem (Finset.mem_insert_self b {c})⟩, - f ⟨c, Finset.mem_insert_of_mem - (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl))⟩) - invFun abc := fun ⟨s, hs⟩ => - if h : s = a then h ▸ abc.1 - else if h' : s = b then h' ▸ abc.2.1 - else - have hsc : s = c := by - rcases Finset.mem_insert.mp hs with hh | hh - · exact absurd hh h - · rcases Finset.mem_insert.mp hh with hh | hh - · exact absurd hh h' - · exact Finset.mem_singleton.mp hh - hsc ▸ abc.2.2 - left_inv f := by - funext ⟨s, hs⟩ - by_cases hsa : s = a - · subst hsa; simp - by_cases hsb : s = b - · subst hsb; simp [hsa] - have hsc : s = c := by - rcases Finset.mem_insert.mp hs with hh | hh - · exact absurd hh hsa - · rcases Finset.mem_insert.mp hh with hh | hh - · exact absurd hh hsb - · exact Finset.mem_singleton.mp hh - subst hsc; simp [hsa, hsb] - right_inv abc := by - have hba : b ≠ a := fun h => hab h.symm - have hca : c ≠ a := fun h => hac h.symm - have hcb : c ≠ b := fun h => hbc h.symm - ext1 - · simp - · ext1 - · simp [hba] - · simp [hca, hcb] - -/-- Closed-form first projection of `regionIdxTripleEquiv` — value at site `a`. -/ -@[simp] private lemma regionIdxTripleEquiv_apply_fst {a b c : L.sites} - (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) - (f : L.regionIdx ({a, b, c} : Finset L.sites)) : - (L.regionIdxTripleEquiv hab hbc hac f).1 = f ⟨a, Finset.mem_insert_self a {b, c}⟩ := rfl - -/-- Second projection of `regionIdxTripleEquiv` — value at site `b`. -/ -@[simp] private lemma regionIdxTripleEquiv_apply_snd_fst {a b c : L.sites} - (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) - (f : L.regionIdx ({a, b, c} : Finset L.sites)) : - (L.regionIdxTripleEquiv hab hbc hac f).2.1 = - f ⟨b, Finset.mem_insert_of_mem (Finset.mem_insert_self b {c})⟩ := rfl - -/-- Third projection of `regionIdxTripleEquiv` — value at site `c`. -/ -@[simp] private lemma regionIdxTripleEquiv_apply_snd_snd {a b c : L.sites} - (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) - (f : L.regionIdx ({a, b, c} : Finset L.sites)) : - (L.regionIdxTripleEquiv hab hbc hac f).2.2 = - f ⟨c, Finset.mem_insert_of_mem - (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl))⟩ := rfl - -/-- **3-element factorisation (left-associated)**: the alternate - `regionIdx {a, b, c} ≃ (localIdx a × localIdx b) × localIdx c` view, used when the - bipartite split sees the pair `(a, b)` together against `c`. -/ -def regionIdxTripleEquiv' {a b c : L.sites} (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) : - L.regionIdx ({a, b, c} : Finset L.sites) ≃ - (L.localIdx a × L.localIdx b) × L.localIdx c := - (L.regionIdxTripleEquiv hab hbc hac).trans (Equiv.prodAssoc _ _ _).symm - -/-- **Pair-complement on the left site**: for the two-element region `{a, b}` with - `a ≠ b`, the index type of the complement of `{a}` reduces to `localIdx b`. - Direct realisation of "evaluate at the unique remaining site `b`". -/ -def regionIdxComplLeftSite {a b : L.sites} (hab : a ≠ b) : - L.regionIdx (({a, b} : Finset L.sites) \ {a}) ≃ L.localIdx b where - toFun f := f ⟨b, Finset.mem_sdiff.mpr - ⟨Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl), - Finset.notMem_singleton.mpr (fun h => hab h.symm)⟩⟩ - invFun y := fun ⟨s, hs⟩ => - have hsb : s = b := by - simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs - rcases hs.1 with h | h - · exact absurd h hs.2 - · exact h - hsb ▸ y - left_inv f := by - funext ⟨s, hs⟩ - have hsb : s = b := by - simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs - rcases hs.1 with h | h - · exact absurd h hs.2 - · exact h - subst hsb - rfl - right_inv y := rfl - -/-- **Pair-complement on the right site**: dual of `regionIdxComplLeftSite` — the - complement of `{b}` in `{a, b}` reduces to `localIdx a`. -/ -def regionIdxComplRightSite {a b : L.sites} (hab : a ≠ b) : - L.regionIdx (({a, b} : Finset L.sites) \ {b}) ≃ L.localIdx a where - toFun f := f ⟨a, Finset.mem_sdiff.mpr - ⟨Finset.mem_insert_self a {b}, Finset.notMem_singleton.mpr hab⟩⟩ - invFun y := fun ⟨s, hs⟩ => - have hsa : s = a := by - simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs - rcases hs.1 with h | h - · exact h - · exact absurd h hs.2 - hsa ▸ y - left_inv f := by - funext ⟨s, hs⟩ - have hsa : s = a := by - simp only [Finset.mem_sdiff, Finset.mem_insert, Finset.mem_singleton] at hs - rcases hs.1 with h | h - · exact h - · exact absurd h hs.2 - subst hsa - rfl - right_inv y := rfl - -/-- **Triple-complement, first site**: for pairwise-distinct `a, b, c : L.sites`, - `regionIdx ({a, b, c} \ {a}) ≃ localIdx b × localIdx c`. Direct construction - (no `regionIdxCongr` transport) so pointwise evaluation reduces by computation, - enabling the marginal-compatibility helpers used by SSA-style proofs. -/ -noncomputable def regionIdxComplFirst {a b c : L.sites} - (hab : a ≠ b) (hbc : b ≠ c) (hac : a ≠ c) : - L.regionIdx (({a, b, c} : Finset L.sites) \ ({a} : Finset _)) ≃ - L.localIdx b × L.localIdx c where - toFun f := - (f ⟨b, Finset.mem_sdiff.mpr ⟨ - Finset.mem_insert_of_mem (Finset.mem_insert_self _ _), - Finset.notMem_singleton.mpr hab.symm⟩⟩, - f ⟨c, Finset.mem_sdiff.mpr ⟨ - Finset.mem_insert_of_mem (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)), - Finset.notMem_singleton.mpr hac.symm⟩⟩) - invFun xbc := fun ⟨v, hv⟩ => - if h : v = b then h ▸ xbc.1 - else - have hvc : v = c := by - rw [Finset.mem_sdiff] at hv - rcases Finset.mem_insert.mp hv.1 with rfl | h2 - · exact absurd (Finset.mem_singleton_self _) hv.2 - · rcases Finset.mem_insert.mp h2 with rfl | h3 - · exact absurd rfl h - · exact Finset.mem_singleton.mp h3 - hvc ▸ xbc.2 - left_inv f := by - funext ⟨v, hv⟩ - rw [Finset.mem_sdiff] at hv - by_cases hvb : v = b - · subst hvb; simp - · have hvc : v = c := by - rcases Finset.mem_insert.mp hv.1 with rfl | h2 - · exact absurd (Finset.mem_singleton_self _) hv.2 - · rcases Finset.mem_insert.mp h2 with rfl | h3 - · exact absurd rfl hvb - · exact Finset.mem_singleton.mp h3 - subst hvc; simp [hvb] - right_inv := by - rintro ⟨xb, xc⟩ - have hcb : c ≠ b := hbc.symm - ext1 <;> simp [hcb] - -/-- **Triple-complement, first two sites**: for pairwise-distinct - `a, b, c : L.sites`, `regionIdx ({a, b, c} \ {a, b}) ≃ localIdx c`. Direct - construction (no `regionIdxCongr` transport) so pointwise evaluation reduces - by computation. -/ -noncomputable def regionIdxComplPairFirstTwo {a b c : L.sites} - (hac : a ≠ c) (hbc : b ≠ c) : - L.regionIdx (({a, b, c} : Finset L.sites) \ ({a, b} : Finset _)) ≃ L.localIdx c where - toFun f := f ⟨c, Finset.mem_sdiff.mpr ⟨ - Finset.mem_insert_of_mem (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)), - by simp only [Finset.mem_insert, Finset.mem_singleton, not_or] - exact ⟨hac.symm, hbc.symm⟩⟩⟩ - invFun xc := fun ⟨v, hv⟩ => - have hvc : v = c := by - rw [Finset.mem_sdiff] at hv - rcases Finset.mem_insert.mp hv.1 with rfl | h2 - · exact absurd (Finset.mem_insert_self _ _) hv.2 - · rcases Finset.mem_insert.mp h2 with rfl | h3 - · exact absurd - (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 - · exact Finset.mem_singleton.mp h3 - hvc ▸ xc - left_inv f := by - funext ⟨v, hv⟩ - rw [Finset.mem_sdiff] at hv - have hvc : v = c := by - rcases Finset.mem_insert.mp hv.1 with rfl | h2 - · exact absurd (Finset.mem_insert_self _ _) hv.2 - · rcases Finset.mem_insert.mp h2 with rfl | h3 - · exact absurd - (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 - · exact Finset.mem_singleton.mp h3 - subst hvc; rfl - right_inv := by intro xc; rfl - -/-- **Triple-complement, last two sites**: for pairwise-distinct - `a, b, c : L.sites`, `regionIdx ({a, b, c} \ {b, c}) ≃ localIdx a`. Direct - construction (no `regionIdxCongr` transport) so pointwise evaluation reduces - by computation. -/ -noncomputable def regionIdxComplPairLastTwo {a b c : L.sites} - (hab : a ≠ b) (hac : a ≠ c) : - L.regionIdx (({a, b, c} : Finset L.sites) \ ({b, c} : Finset _)) ≃ L.localIdx a where - toFun f := f ⟨a, Finset.mem_sdiff.mpr ⟨ - Finset.mem_insert_self _ _, - by simp only [Finset.mem_insert, Finset.mem_singleton, not_or] - exact ⟨hab, hac⟩⟩⟩ - invFun xa := fun ⟨v, hv⟩ => - have hva : v = a := by - rw [Finset.mem_sdiff] at hv - rcases Finset.mem_insert.mp hv.1 with rfl | h2 - · rfl - · rcases Finset.mem_insert.mp h2 with rfl | h3 - · exact absurd (Finset.mem_insert_self _ _) hv.2 - · have hvc : v = c := Finset.mem_singleton.mp h3 - subst hvc - exact absurd - (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 - hva ▸ xa - left_inv f := by - funext ⟨v, hv⟩ - rw [Finset.mem_sdiff] at hv - have hva : v = a := by - rcases Finset.mem_insert.mp hv.1 with rfl | h2 - · rfl - · rcases Finset.mem_insert.mp h2 with rfl | h3 - · exact absurd (Finset.mem_insert_self _ _) hv.2 - · have hvc : v = c := Finset.mem_singleton.mp h3 - subst hvc - exact absurd - (Finset.mem_insert_of_mem (Finset.mem_singleton.mpr rfl)) hv.2 - subst hva; rfl - right_inv := by intro xa; rfl - -/-- `{b} ⊆ {a, b, c}` for any sites `a, b, c`. Pure Finset membership; the - arguments `a` and `c` are kept positional so call sites can pass them - explicitly when convenient. -/ -lemma singleton_b_subset_triple (a b c : L.sites) : - ({b} : Finset L.sites) ⊆ ({a, b, c} : Finset L.sites) := - Finset.singleton_subset_iff.mpr - (Finset.mem_insert_of_mem (Finset.mem_insert_self b _)) - -end LocalNet diff --git a/QuantumSystem/Algebra/LocalNet/Covariance.lean b/QuantumSystem/Algebra/LocalNet/Covariance.lean new file mode 100644 index 0000000..485504a --- /dev/null +++ b/QuantumSystem/Algebra/LocalNet/Covariance.lean @@ -0,0 +1,314 @@ +module + +public import QuantumSystem.Algebra.LocalNet.Net +public import Mathlib.Data.Finset.Grade + +/-! +# Covariance data for a local net + +A **covariance** of a local net over a causal index set `K` is an order automorphism `σ : K ≃o K` +of the regions preserving causal orthogonality, together with, for every region `O`, a +`*`-isomorphism `β_O : 𝔄(O) ≃⋆ₐ[ℂ] 𝔄(σO)` of local algebras that is natural with respect to the +isotony embeddings. The naturality field `β_incl` is the AQFT covariance axiom +`β(𝔄(O)) = 𝔄(σO)` compatibly with inclusions (Naaijkens, *Anyons in Infinite Quantum Systems*, +2012, §3.2; Verch, *Lecture Notes on Operator Algebras and Quantum Field Theory*, +arXiv:2507.00900, 2025, §1.2). These +covariances form a `Group` under composition (`Covariance.id`, `Covariance.comp`, +`Covariance.inv`). For lattice nets (`K = Finset sites`) a covariance is induced by a site +permutation via `Covariance.ofSitePerm`, and conversely every covariance of a lattice net arises +this way (`Covariance.exists_eq_ofSitePerm`), since an order automorphism of `Finset sites` maps +atoms to atoms and the atoms are the singletons. + +This file isolates the abstract covariance *data* and its group structure. Its *action* on the +quasi-local algebra — assembling the per-region `*`-isomorphisms into a `*`-automorphism +`localObservableCovariance` and (for a `Faithful` net) its continuous extension to the quasi-local +C⋆-algebra — is built in `LocalNet.QuasiLocalAlgebra`. A symmetry group `G` acts on the net by +supplying a group homomorphism `G →* N.Covariance` (whose `σ`-component is the geometric action +`G → K ≃o K`); composing it with `localObservableCovarianceHom` — or, for a `Faithful` net, +`quasiLocalCStarCovarianceHom` — yields the automorphic action of `G` on the (quasi-local) algebra. + +## Notation + +`𝔄(O)` and `𝓡(O)` in the prose above are documentation shorthand for the local C⋆-algebra +`N.algebra O` and the local von Neumann algebra `N.localVonNeumannAlgebra R O`; the convention — +and why neither is a Lean notation — is stated in full in `QuantumSystem.Algebra.LocalNet.Net`. +-/ + +@[expose] public section + +namespace LocalNet + +open scoped CausalOrthogonality + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] (N : LocalNet K) + +/-! ### Covariances of the net + +A covariance of a local net is a causal order automorphism `σ` of the index set together with, +for each region, a `*`-isomorphism `β_O : 𝔄(O) ≃⋆ₐ[ℂ] 𝔄(σO)` of local algebras that is natural +with respect to the isotony embeddings. This realises the AQFT covariance axiom `β(𝔄(O)) = 𝔄(σO)` +at the abstract net level. Being purely algebraic, the covariance group needs no faithfulness +hypothesis. +-/ + +/-- A **covariance** of a local net `N`: an order automorphism `σ` of the causal index set + preserving causal orthogonality (`map_orthogonal_iff`), together with, for every region `O`, + a `*`-isomorphism `β O : 𝔄(O) ≃⋆ₐ[ℂ] 𝔄(σO)`, natural with respect to the isotony embeddings + (`β_incl`). This is the AQFT covariance datum at the abstract net level. -/ +structure Covariance where + /-- The underlying automorphism of the causal index set, carrying the region map `O ↦ σO`. -/ + σ : K ≃o K + /-- The region automorphism preserves causal orthogonality. Stated as an `iff` so that the + inverse covariance is again a covariance. -/ + map_orthogonal_iff : ∀ ⦃O₁ O₂ : K⦄, σ O₁ ⟂ σ O₂ ↔ O₁ ⟂ O₂ + /-- The covariance `*`-isomorphism `β_O : 𝔄(O) ≃⋆ₐ[ℂ] 𝔄(σO)` on each region. -/ + β : ∀ O : K, N.algebra O ≃⋆ₐ[ℂ] N.algebra (σ O) + /-- **Naturality / covariance**: `β` intertwines the isotony embeddings, so it maps the net to + itself compatibly with inclusions — the AQFT covariance axiom `β(𝔄(O)) = 𝔄(σO)`. -/ + β_incl : ∀ {O O' : K} (h : O ≤ O') (x : N.algebra O), + β O' (N.incl h x) = N.incl (σ.monotone h) (β O x) + +namespace Covariance + +variable {N} (a : N.Covariance) + +/-- **Extensionality** for covariances: two covariances with the same region automorphism and the + same local `*`-isomorphisms (compared along the induced region equality) are equal. The + remaining fields are propositions, hence irrelevant. -/ +@[ext (iff := false)] lemma ext {s t : N.Covariance} (hσ : s.σ = t.σ) + (hβ : ∀ (O : K) (x : N.algebra O) (e : s.σ O = t.σ O), + N.algebraCongr e (s.β O x) = t.β O x) : s = t := by + revert hσ hβ + obtain ⟨sσ, -, sβ, -⟩ := s + obtain ⟨tσ, -, tβ, -⟩ := t + rintro rfl hβ + have hβ' : sβ = tβ := by + funext O + ext x + simpa using hβ O x rfl + subst hβ' + rfl + +/-- Naturality of the inverse local `*`-isomorphisms with respect to isotony. -/ +lemma β_symm_incl {O O' : K} (h : O ≤ O') (y : N.algebra (a.σ O)) : + (a.β O').symm (N.incl (a.σ.monotone h) y) = N.incl h ((a.β O).symm y) := by + have key := a.β_incl h ((a.β O).symm y) + rw [StarAlgEquiv.apply_symm_apply] at key + rw [← key, StarAlgEquiv.symm_apply_apply] + +/-- The local `*`-isomorphisms commute with the region-equality transport. -/ +lemma β_algebraCongr {O O' : K} (e : O = O') (x : N.algebra O) : + a.β O' (N.algebraCongr e x) = N.algebraCongr (by rw [e]) (a.β O x) := by + subst e; simp + +variable (N) in +/-- The **identity covariance**: the identity region automorphism with the identity local + `*`-isomorphisms. -/ +def id : N.Covariance where + σ := OrderIso.refl K + map_orthogonal_iff _ _ := Iff.rfl + β _ := StarAlgEquiv.refl + β_incl _ _ := rfl + +/-- **Composition of covariances**: apply `b`, then `a`. The region automorphisms compose, and + the local `*`-isomorphisms compose along them. -/ +def comp (a b : N.Covariance) : N.Covariance where + σ := b.σ.trans a.σ + map_orthogonal_iff O₁ O₂ := + (a.map_orthogonal_iff (O₁ := b.σ O₁) (O₂ := b.σ O₂)).trans + (b.map_orthogonal_iff (O₁ := O₁) (O₂ := O₂)) + β O := (b.β O).trans (a.β (b.σ O)) + β_incl h x := by + simp only [StarAlgEquiv.trans_apply] + rw [b.β_incl h] + exact a.β_incl (b.σ.monotone h) ((b.β _) x) + +/-- The **inverse covariance**: the inverse region automorphism with the inverse local + `*`-isomorphisms, transported along `σ(σ⁻¹O) = O`. -/ +def inv (a : N.Covariance) : N.Covariance where + σ := a.σ.symm + map_orthogonal_iff O₁ O₂ := by + rw [← a.map_orthogonal_iff, a.σ.apply_symm_apply, a.σ.apply_symm_apply] + β O := (N.algebraCongr (a.σ.apply_symm_apply O).symm).trans (a.β (a.σ.symm O)).symm + β_incl h x := by + simp only [StarAlgEquiv.trans_apply] + rw [N.incl_algebraCongr _ _ h (a.σ.monotone (a.σ.symm.monotone h)), a.β_symm_incl] + +/-- The identity covariance acts as the identity on every local algebra. -/ +@[simp] lemma id_β_apply (O : K) (x : N.algebra O) : (Covariance.id N).β O x = x := + rfl + +/-- The composite covariance acts by composing the local `*`-isomorphisms along the region maps. -/ +@[simp] lemma comp_β_apply (a b : N.Covariance) (O : K) (x : N.algebra O) : + (a.comp b).β O x = a.β (b.σ O) (b.β O x) := + rfl + +/-- The inverse covariance acts by the inverse local `*`-isomorphism, after transporting the + argument along the region equality `σ(σ⁻¹O) = O`. -/ +@[simp] lemma inv_β_apply (a : N.Covariance) (O : K) (x : N.algebra O) : + (Covariance.inv a).β O x = (a.β (a.σ.symm O)).symm + (N.algebraCongr (a.σ.apply_symm_apply O).symm x) := + rfl + +/-- The identity covariance fixes every region. -/ +@[simp] lemma id_σ_apply (O : K) : (Covariance.id N).σ O = O := + rfl + +/-- Composing covariances composes their region maps. -/ +@[simp] lemma comp_σ_apply (a b : N.Covariance) (O : K) : + (a.comp b).σ O = a.σ (b.σ O) := + rfl + +/-- The identity covariance is a left unit for composition. -/ +lemma id_comp (a : N.Covariance) : (Covariance.id N).comp a = a := by + refine Covariance.ext ?_ fun O x e => ?_ + · exact OrderIso.ext rfl + · exact N.algebraCongr_self e _ + +/-- The identity covariance is a right unit for composition. -/ +lemma comp_id (a : N.Covariance) : a.comp (Covariance.id N) = a := by + refine Covariance.ext ?_ fun O x e => ?_ + · exact OrderIso.ext rfl + · exact N.algebraCongr_self e _ + +/-- Composition of covariances is associative. -/ +lemma comp_assoc (a b c : N.Covariance) : (a.comp b).comp c = a.comp (b.comp c) := by + refine Covariance.ext ?_ fun O x e => ?_ + · exact OrderIso.ext rfl + · exact N.algebraCongr_self e _ + +/-- The inverse covariance is a left inverse for composition; together with the three laws above + this is what makes the covariances a group. -/ +lemma inv_comp (a : N.Covariance) : (Covariance.inv a).comp a = Covariance.id N := by + refine Covariance.ext ?_ fun O x e => ?_ + · exact OrderIso.ext (funext fun O => a.σ.symm_apply_apply O) + · rw [algebraCongr_eq_iff] + simp only [comp_β_apply, inv_β_apply, id_β_apply] + rw [← a.β_algebraCongr, StarAlgEquiv.symm_apply_apply] + rfl + +/-- The covariances of a local net form a **group** under composition, with `Covariance.id` the + unit and `Covariance.inv` the inverse. -/ +noncomputable instance : Group N.Covariance where + mul a b := a.comp b + one := Covariance.id N + inv a := a.inv + mul_assoc := Covariance.comp_assoc + one_mul := Covariance.id_comp + mul_one := Covariance.comp_id + inv_mul_cancel := Covariance.inv_comp + +/-- The group multiplication is composition of covariances. -/ +lemma mul_def (a b : N.Covariance) : a * b = a.comp b := rfl + +/-- The group unit is the identity covariance. -/ +lemma one_def : (1 : N.Covariance) = Covariance.id N := rfl + +/-- The group inverse is the inverse covariance. -/ +lemma inv_def (a : N.Covariance) : a⁻¹ = a.inv := rfl + +/-! ### Lattice covariances from site permutations -/ + +/-- Build a covariance of a lattice net (`K = Finset sites`) from a **site permutation**: the + induced region automorphism is `Λ ↦ Λ.map σ`, which preserves disjointness. This recovers the + site-permutation covariances of spin-system nets (Naaijkens, *Anyons in Infinite Quantum + Systems*, 2012, §3.2). Conversely every + covariance of a lattice net arises this way — that is `exists_eq_ofSitePerm`, proved below via + the fact that an order automorphism of `Finset sites` preserves atoms (singletons) and is + therefore induced by a permutation of the sites. -/ +def ofSitePerm {sites : Type*} {N : LocalNet (Finset sites)} (σ : sites ≃ sites) + (β : ∀ Λ : Finset sites, N.algebra Λ ≃⋆ₐ[ℂ] N.algebra (Λ.map σ.toEmbedding)) + (β_incl : ∀ {Λ Λ' : Finset sites} (h : Λ ⊆ Λ') (x : N.algebra Λ), + β Λ' (N.incl h x) = N.incl (Finset.map_subset_map.mpr h) (β Λ x)) : + N.Covariance where + σ := { toEquiv := σ.finsetCongr, map_rel_iff' := Finset.map_subset_map } + map_orthogonal_iff _ _ := Finset.disjoint_map _ + β := β + β_incl h x := β_incl h x + +/-! #### Every lattice covariance comes from a site permutation + +An order automorphism of `Finset sites` maps atoms to atoms, and the atoms of `Finset sites` are +exactly the singletons; so it is determined by a permutation of the sites, and the covariance it +belongs to is `ofSitePerm` of that permutation. +-/ + +/-- The image of a singleton region under an order automorphism is again a singleton: order + isomorphisms preserve atoms (`OrderIso.isAtom_iff`), and the atoms of `Finset sites` are the + singletons (`Finset.isAtom_iff`). -/ +lemma exists_singleton_image {sites : Type*} (e : Finset sites ≃o Finset sites) (x : sites) : + ∃ y, e {x} = {y} := + Finset.isAtom_iff.1 ((e.isAtom_iff _).2 (Finset.isAtom_singleton x)) + +open Classical in +/-- The site that an order automorphism of lattice regions sends `x` to. -/ +noncomputable def sitePermFun {sites : Type*} (e : Finset sites ≃o Finset sites) (x : sites) : + sites := + (exists_singleton_image e x).choose + +/-- Defining property of `sitePermFun`: `e {x} = {sitePermFun e x}`. -/ +lemma sitePermFun_spec {sites : Type*} (e : Finset sites ≃o Finset sites) (x : sites) : + e {x} = {sitePermFun e x} := (exists_singleton_image e x).choose_spec + +/-- **The site permutation underlying an order automorphism of lattice regions.** Its inverse is + the site map of the inverse automorphism, the two being mutually inverse because `e.symm` undoes + `e` on singletons. -/ +noncomputable def sitePerm {sites : Type*} (e : Finset sites ≃o Finset sites) : sites ≃ sites where + toFun := sitePermFun e + invFun := sitePermFun e.symm + left_inv x := by + have h2 := sitePermFun_spec e.symm (sitePermFun e x) + rw [← sitePermFun_spec e x, e.symm_apply_apply] at h2 + exact (Finset.singleton_inj.1 h2).symm + right_inv y := by + have h2 := sitePermFun_spec e (sitePermFun e.symm y) + rw [← sitePermFun_spec e.symm y, e.apply_symm_apply] at h2 + exact (Finset.singleton_inj.1 h2).symm + +/-- **An order automorphism of lattice regions is the image map of its site permutation.** + Membership in `e Λ` is tested one site at a time: `y ∈ e Λ` iff `{y} ≤ e Λ` iff + `e.symm {y} ≤ Λ` iff `sitePerm e ⁻¹ y ∈ Λ`. -/ +lemma map_sitePerm {sites : Type*} (e : Finset sites ≃o Finset sites) (Λ : Finset sites) : + e Λ = Λ.map (sitePerm e).toEmbedding := by + ext y + rw [Finset.mem_map] + have key : y ∈ e Λ ↔ sitePermFun e.symm y ∈ Λ := by + rw [← Finset.singleton_subset_iff, ← Finset.singleton_subset_iff, + ← sitePermFun_spec e.symm y] + exact (e.symm_apply_le).symm + rw [key] + constructor + · intro h + exact ⟨sitePermFun e.symm y, h, (sitePerm e).right_inv y⟩ + · rintro ⟨x, hx, rfl⟩ + rwa [show sitePermFun e.symm ((sitePerm e).toEmbedding x) = x from (sitePerm e).left_inv x] + +variable {sites : Type*} {N : LocalNet (Finset sites)} + +/-- **The region automorphism of a lattice covariance is induced by a site permutation.** -/ +theorem exists_sitePerm (a : N.Covariance) : + ∃ σ : sites ≃ sites, ∀ Λ : Finset sites, a.σ Λ = Λ.map σ.toEmbedding := + ⟨sitePerm a.σ, map_sitePerm a.σ⟩ + +/-- **Every covariance of a lattice net arises from a site permutation** — the converse of + `ofSitePerm`, and the statement its docstring advertises. The region automorphism is the image + map of a site permutation `σ` (`exists_sitePerm`); transporting the local `*`-isomorphisms + along that identification of regions produces the `β` data, whose naturality is `a`'s own + naturality composed with `incl_algebraCongr`. -/ +theorem exists_eq_ofSitePerm (a : N.Covariance) : + ∃ (σ : sites ≃ sites) + (β : ∀ Λ : Finset sites, N.algebra Λ ≃⋆ₐ[ℂ] N.algebra (Λ.map σ.toEmbedding)) + (β_incl : ∀ {Λ Λ' : Finset sites} (h : Λ ⊆ Λ') (x : N.algebra Λ), + β Λ' (N.incl h x) = N.incl (Finset.map_subset_map.mpr h) (β Λ x)), + a = Covariance.ofSitePerm σ β β_incl := by + obtain ⟨σ, hσ⟩ := a.exists_sitePerm + refine ⟨σ, fun Λ => (a.β Λ).trans (N.algebraCongr (hσ Λ)), ?_, ?_⟩ + · intro Λ Λ' h x + simp only [StarAlgEquiv.trans_apply] + rw [a.β_incl h] + exact N.incl_algebraCongr (hσ Λ) (hσ Λ') (a.σ.monotone h) (Finset.map_subset_map.mpr h) _ + · exact Covariance.ext (OrderIso.ext (funext hσ)) fun Λ x e => rfl + +end Covariance + +end LocalNet diff --git a/QuantumSystem/Algebra/LocalNet/Examples.lean b/QuantumSystem/Algebra/LocalNet/Examples.lean new file mode 100644 index 0000000..aa31524 --- /dev/null +++ b/QuantumSystem/Algebra/LocalNet/Examples.lean @@ -0,0 +1,320 @@ +module + +public import QuantumSystem.Algebra.LocalNet.SplitProperty + +/-! +# Witnesses for the local-net interfaces + +Every class and structure the local-net development introduces is inhabited here, by explicit +construction. Without such witnesses the theorems of `LocalNet.Net`, `LocalNet.QuasiLocalAlgebra` +and `LocalNet.SplitProperty` would be unfalsifiable: nobody could apply them, and no construction +could contradict them. In particular the split property, being a `Prop`-valued hypothesis that +nothing in the development proves, needs a model exhibited before it can be believed consistent. + +The witnesses are deliberately the smallest ones that are not degenerate in the way that matters. + +* **A separating proper containment.** `ProperContainment.integerChain` is the 1-neighbourhood + thickening on the integer chain, `Λ₁ ⋐ Λ₂` iff `nbhd Λ₁ ⊊ Λ₂`. What makes it *separating* is that + `nbhd` enlarges in both directions, so the collar `Λ₂ \ nbhd Λ₁` can touch `Λ₁` from neither side + and *touching* pairs are excluded. Strict enlargement alone would not do this — see the docstring + of `ProperContainment.ofThicken`, and `ProperContainment.ofSSubset` for the degenerate model the + class axioms admit. `ProperContainment.properlyContained_singleton` exhibits an actual pair, so + `⋐` is not empty and the split property below is not vacuously true. +* **A faithful local net.** `LocalNet.Examples.trivialNet` assigns `ℂ` to every region of the + integer chain, with identity isotony embeddings, built through the join-semilattice smart + constructor `LocalNet.mk'`; its `Faithful` instance is immediate. +* **A net of von Neumann algebras with the split property.** + `LocalNet.Examples.scalarNet` is the constant net at `𝓑(ℂ)`, and + `LocalNet.Examples.scalarNet_splitProperty` proves `VonNeumannNet.SplitProperty` for it. Locality + holds because bounded operators on `ℂ` commute. +* **The split property at the representation level, in two representations.** + `LocalNet.Examples.trivialNet_splitProperty` proves `LocalNet.SplitProperty` for `trivialNet` in + the *zero* representation on `ℂ`, and `LocalNet.Examples.unitalRep_splitProperty` in the *unital* + representation `LocalNet.Examples.unitalRep`, which sends `1` to `1` + (`LocalNet.Examples.unitalRep_π_one`). The second is there because the first alone would leave + the representation-level interface inhabited only by a degenerate `π`. +* **A refuter.** All the witnesses here inhabit the *positive* side of the interfaces; the negation + of `IsSplitInclusion` is kept inhabited by `VonNeumannAlgebra.not_isSplitInclusion_diagonalAlgebra` + (in `QuantumSystem.Algebra.VonNeumannAlgebra.SplitInclusion`, next to the predicate it refutes). + +What is deliberately *not* built here is a spin-system net with genuine tensor-product local +algebras — the physically interesting lattice model the module docs of `LocalNet.Net` and +`LocalNet.Covariance` describe. That is a construction in its own right, not a witness; these +witnesses establish consistency and applicability of the interfaces, nothing more. + +**The residual degeneracy, stated so it is not mistaken for evidence.** Every Hilbert space +appearing below is `ℂ`, and on `ℂ` the split property is *automatic*: the only von Neumann algebra +there is `𝓑(ℂ)` (`VonNeumannAlgebra.eq_boundedLinearOperators_complex`), so every net of von +Neumann algebras on `ℂ` splits (`VonNeumannNet.splitProperty_of_complex`), and both +representation-level witnesses below are instances of that one theorem. So the degeneracy is +`dim H = 1` — *not* the zero representation, which is why exchanging it for a unital one changes +nothing mathematically. This is the one reason the literature explicitly sets aside: +Halvorson–Müger's type III₁ proposition carries the escape clause "either `𝓡 = ℂ1` or `𝓡` is a +type III₁ factor", and a reflexive proper containment is rejected precisely because it would force +every local algebra to be type I. These witnesses show the interfaces are inhabited and applicable; +they are **not** evidence about nets whose local algebras are type III₁, and no argument should +treat them as such. The degeneracy is also forced rather than chosen at the C⋆ level: over +`Finset ℤ` with `⟂ = Disjoint`, any two disjoint regions lie under their union, so locality makes +every *constant* net commutative — a noncommutative witness needs the tensor-product net descoped +above. +-/ + +@[expose] public section + +open scoped CausalOrthogonality ProperContainment VonNeumannAlgebra + +namespace ProperContainment + +/-! ### A separating proper containment on the integer chain -/ + +/-- The **1-neighbourhood** of a finite set of integer sites: the region together with its two + neighbouring layers. This is the thickening operator of a nearest-neighbour spin chain. -/ +def nbhd (Λ : Finset ℤ) : Finset ℤ := Λ ∪ Λ.image (· - 1) ∪ Λ.image (· + 1) + +/-- A region is contained in its 1-neighbourhood. -/ +lemma subset_nbhd (Λ : Finset ℤ) : Λ ⊆ nbhd Λ := + fun _ hx => Finset.mem_union_left _ (Finset.mem_union_left _ hx) + +/-- The 1-neighbourhood is monotone. -/ +lemma monotone_nbhd : Monotone nbhd := fun _ _ h => + Finset.union_subset_union (Finset.union_subset_union h (Finset.image_subset_image h)) + (Finset.image_subset_image h) + +/-- The 1-neighbourhood **strictly** enlarges every nonempty region: it adds the site just above + the largest one. This is the hypothesis `ProperContainment.ofThicken` needs. -/ +lemma ssubset_nbhd (Λ : Finset ℤ) (hne : Λ.Nonempty) : Λ ⊂ nbhd Λ := by + refine (Finset.ssubset_iff_of_subset (subset_nbhd Λ)).2 ⟨Λ.max' hne + 1, ?_, ?_⟩ + · exact Finset.mem_union_right _ (Finset.mem_image.2 ⟨Λ.max' hne, Λ.max'_mem hne, rfl⟩) + · exact fun hmem => absurd (Λ.le_max' _ hmem) (by omega) + +/-- **The 1-neighbourhood absorbs both neighbours of every site of the region.** This — and not + the strict enlargement `ssubset_nbhd` — is what makes the induced proper containment + *separating*: the collar `Λ₂ \ nbhd Λ₁` avoids `nbhd Λ₁`, hence contains no site adjacent to + `Λ₁` on either side. A one-sided thickening satisfies the hypotheses of `ofThicken` just as + well and does not have this property. -/ +lemma add_mem_nbhd_of_mem {Λ : Finset ℤ} {x : ℤ} (hx : x ∈ Λ) (d : ℤ) (hd : d = 1 ∨ d = -1) : + x + d ∈ nbhd Λ := by + rcases hd with rfl | rfl + · exact Finset.mem_union_right _ (Finset.mem_image.2 ⟨x, hx, rfl⟩) + · exact Finset.mem_union_left _ + (Finset.mem_union_right _ (Finset.mem_image.2 ⟨x, hx, by omega⟩)) + +/-- **Proper containment on the integer chain**: `Λ₁ ⋐ Λ₂` iff the 1-neighbourhood of `Λ₁` is a + strict subset of `Λ₂`. The buffer layer `nbhd Λ₁ \ Λ₁` separates `Λ₁` from the collar on both + sides (`add_mem_nbhd_of_mem`), so touching pairs — which the literature excludes, since local + algebras of touching regions are not statistically independent — do not satisfy this relation. + + Deliberately a `def` rather than a global `instance`: this file is re-exported by the aggregate + root, so a global instance would silently resolve every downstream `⋐` on `Finset ℤ` to the + nearest-neighbour relation of this one witness. It is activated by `attribute [local instance]` + where this file needs it; a model that wants it elsewhere says so, either the same way or by + rebuilding it from the public `nbhd`, `ssubset_nbhd` and `monotone_nbhd`. -/ +@[reducible] noncomputable def integerChain : ProperContainment (Finset ℤ) := + ProperContainment.ofThicken nbhd ssubset_nbhd monotone_nbhd + +attribute [local instance] integerChain + +/-- **`⋐` is inhabited on the integer chain**: the single site `{0}`, whose 1-neighbourhood is + `{-1, 0, 1}`, is properly contained in `{-2, -1, 0, 1, 2}`. Recorded so that the split property + over this index set is not vacuously true. -/ +lemma properlyContained_singleton : ({0} : Finset ℤ) ⋐ ({-2, -1, 0, 1, 2} : Finset ℤ) := by + change nbhd {0} ⊂ ({-2, -1, 0, 1, 2} : Finset ℤ) + decide + +end ProperContainment + +namespace LocalNet.Examples + +attribute [local instance] ProperContainment.integerChain + +/-! ### A faithful local net -/ + +/-- The **trivial local net** over the integer chain: every region carries the C⋆-algebra `ℂ`, and + every isotony embedding is the identity. Locality holds because `ℂ` is commutative, and is + supplied only for the join region `Λ₁ ∪ Λ₂` — the smart constructor `LocalNet.mk'` pushes it + forward to every common superregion. -/ +noncomputable def trivialNet : LocalNet (Finset ℤ) := + LocalNet.mk' (fun _ : Finset ℤ => ℂ) (fun _ => StarAlgHom.id ℂ ℂ) (fun _ => rfl) + (fun _ _ _ => rfl) (fun _ x y => mul_comm x y) + +/-- The trivial net is faithful: its isotony embeddings are identities. -/ +instance : trivialNet.Faithful where + incl_injective _ := fun _ _ h => h + +/-! ### A covariance moving the regions -/ + +/-- **The unit translation of the integer chain**, as a covariance of the trivial net: the site + permutation `x ↦ x + 1` induces the region automorphism `Λ ↦ Λ + 1`, and every local algebra + being `ℂ` the covariance `*`-isomorphisms are identities. + + This is the witness that makes the covariance API testable. `LocalNet.Covariance.id` inhabits + the structure, but at the identity region map every naturality square of `Covariance` closes by + `rfl` and `Covariance.sitePerm`, `Covariance.exists_sitePerm` and + `Covariance.exists_eq_ofSitePerm` all speak about the trivial permutation. Here the region map + genuinely moves (`σ_shiftCovariance`), so those statements have content. -/ +noncomputable def shiftCovariance : trivialNet.Covariance := + LocalNet.Covariance.ofSitePerm (N := trivialNet) (Equiv.addRight (1 : ℤ)) + (fun _ => StarAlgEquiv.refl) (fun _ _ => rfl) + +/-- **The unit translation moves regions**: it carries the site `0` to the site `1`. So + `shiftCovariance` is not the identity covariance, and the site permutation recovered from it by + `LocalNet.Covariance.exists_sitePerm` is not the identity permutation. -/ +@[simp] lemma σ_shiftCovariance : shiftCovariance.σ ({0} : Finset ℤ) = {1} := rfl + +/-! ### A net of von Neumann algebras with the split property -/ + +/-- The **constant net at `𝓑(ℂ)`** over the integer chain. Isotony is trivial, and locality holds + because bounded operators on `ℂ` commute (`VonNeumannAlgebra.mul_comm_complex`), so every + algebra of the net lies in the commutant of every other. -/ +noncomputable def scalarNet : VonNeumannNet (Finset ℤ) ℂ where + algebra _ := 𝓑(ℂ) + algebra_mono _ _ _ := le_rfl + algebra_le_commutant_of_orthogonal _ _ _ := by + intro x _ + rw [VonNeumannAlgebra.mem_commutant_iff] + exact fun y _ => VonNeumannAlgebra.mul_comm_complex y x + +/-- **The constant net has the split property.** Together with + `ProperContainment.properlyContained_singleton` this is a non-vacuous model of + `VonNeumannNet.SplitProperty`. It is an instance of `VonNeumannNet.splitProperty_of_complex`, + which is also the reason it is no evidence about anything but inhabitation: on `ℂ` every net + splits. -/ +theorem scalarNet_splitProperty : scalarNet.SplitProperty := + VonNeumannNet.splitProperty_of_complex scalarNet + +/-! ### The split property at the representation level + +Two representations of `trivialNet.quasiLocalCStarAlgebra` on `ℂ`, both exhibiting +`LocalNet.SplitProperty`: the zero one, and — so that the interface is not inhabited by a +degenerate `π` alone — a unital one. +-/ + +/-- The zero `*`-representation of the quasi-local C⋆-algebra of the trivial net on `ℂ`. The + representation is degenerate as a `*`-map — it sends `1` to `0` — but that is *not* what makes + the split property hold below: `𝓡(O)` contains `1` regardless, since a von Neumann algebra is + unital, and on `ℂ` it is forced to be `𝓑(ℂ)` for every representation whatsoever + (`VonNeumannNet.splitProperty_of_complex`). The unital representation `unitalRep` below is the + same witness with the degeneracy of `π` removed. -/ +noncomputable def zeroHom : trivialNet.quasiLocalCStarAlgebra →⋆ₙₐ[ℂ] (ℂ →L[ℂ] ℂ) where + toFun _ := 0 + map_smul' _ _ := by simp + map_zero' := rfl + map_add' _ _ := by simp + map_mul' _ _ := (zero_mul (0 : ℂ →L[ℂ] ℂ)).symm + map_star' _ := (star_zero (ℂ →L[ℂ] ℂ)).symm + +/-- The zero representation of the trivial net's quasi-local C⋆-algebra, on `ℂ`. -/ +noncomputable def zeroRep : CStarRep trivialNet.quasiLocalCStarAlgebra where + H := ℂ + π := zeroHom + +/-- **The trivial net has the split property in the zero representation.** Its local von Neumann + algebras act on `ℂ`, where the only von Neumann algebra is `𝓑(ℂ)`, a type I factor. This + inhabits `LocalNet.SplitProperty` itself, not merely the `VonNeumannNet` form. -/ +theorem trivialNet_splitProperty : trivialNet.SplitProperty zeroRep := + VonNeumannNet.splitProperty_of_complex (trivialNet.vonNeumannNet zeroRep) + +/-! #### A unital representation + +The isotony embeddings of `trivialNet` are all the identity of `ℂ`, so its algebra of local +observables collapses onto `ℂ` and its quasi-local C⋆-algebra onto the completion of `ℂ`. Reading +off that scalar and letting it multiply gives a representation on `ℂ` that carries `1` to `1`. +-/ + +/-- **Evaluation of a local observable of the trivial net.** Every local algebra is `ℂ` and every + connecting map the identity, so the inductive limit maps onto `ℂ` by reading off the + component. -/ +noncomputable def evalLocal : trivialNet.localObservables →+* ℂ := + DirectLimit.Ring.lift trivialNet.algebra (fun _ _ h => trivialNet.incl h) ℂ + (fun _ => RingHom.id ℂ) (fun _ _ _ _ => rfl) + +/-- Evaluation reads off the component of a local observable. -/ +@[simp] lemma evalLocal_mk (O : Finset ℤ) (x : ℂ) : + evalLocal (⟦⟨O, x⟩⟧ : trivialNet.localObservables) = x := rfl + +/-- Evaluation preserves the involution: the involution of the limit acts componentwise, and on + `ℂ` it is complex conjugation on both sides. -/ +lemma evalLocal_star (z : trivialNet.localObservables) : + evalLocal (star z) = star (evalLocal z) := by + induction z using DirectLimit.induction with + | _ O X => rw [LocalNet.star_mk, evalLocal_mk, evalLocal_mk]; rfl + +/-- Evaluation is `ℂ`-linear: scalars act componentwise on the limit. -/ +lemma evalLocal_smul (c : ℂ) (z : trivialNet.localObservables) : + evalLocal (c • z) = c • evalLocal z := by + induction z using DirectLimit.induction with + | _ O X => rw [DirectLimit.smul_def, evalLocal_mk, evalLocal_mk]; rfl + +/-- **Evaluation is isometric**: the C⋆-norm of the limit is the norm of the component. -/ +lemma norm_evalLocal (z : trivialNet.localObservables) : ‖evalLocal z‖ = ‖z‖ := by + induction z using DirectLimit.induction with + | _ O X => rw [evalLocal_mk, LocalNet.norm_mk]; rfl + +/-- Evaluation extended to the quasi-local C⋆-algebra, by continuity from the dense image of the + local observables. -/ +noncomputable def evalQuasiLocal : trivialNet.quasiLocalCStarAlgebra →+* ℂ := + UniformSpace.Completion.extensionHom (β := ℂ) evalLocal + (AddMonoidHomClass.isometry_of_norm _ norm_evalLocal).continuous + +/-- The extension agrees with evaluation on the local observables. -/ +@[simp] lemma evalQuasiLocal_coe (z : trivialNet.localObservables) : + evalQuasiLocal (↑z : trivialNet.quasiLocalCStarAlgebra) = evalLocal z := + UniformSpace.Completion.extensionHom_coe _ _ z + +/-- The extension is continuous, being the continuous extension of a uniformly continuous map. -/ +lemma continuous_evalQuasiLocal : Continuous evalQuasiLocal := + UniformSpace.Completion.continuous_extension + +/-- The extension preserves the involution: it does so on the dense image, and both sides are + continuous. -/ +lemma evalQuasiLocal_star (z : trivialNet.quasiLocalCStarAlgebra) : + evalQuasiLocal (star z) = star (evalQuasiLocal z) := by + refine UniformSpace.Completion.induction_on z + (isClosed_eq (continuous_evalQuasiLocal.comp continuous_star) + (continuous_star.comp continuous_evalQuasiLocal)) fun w => ?_ + rw [UniformSpace.Completion.star_coe, evalQuasiLocal_coe, evalQuasiLocal_coe, evalLocal_star] + +/-- The extension is `ℂ`-linear: it is on the dense image, and both sides are continuous. -/ +lemma evalQuasiLocal_smul (c : ℂ) (z : trivialNet.quasiLocalCStarAlgebra) : + evalQuasiLocal (c • z) = c • evalQuasiLocal z := by + refine UniformSpace.Completion.induction_on z + (isClosed_eq (continuous_evalQuasiLocal.comp (continuous_const_smul c)) + ((continuous_const_smul c).comp continuous_evalQuasiLocal)) fun w => ?_ + rw [← UniformSpace.Completion.coe_smul, evalQuasiLocal_coe, evalQuasiLocal_coe, evalLocal_smul] + +/-- **The unital `*`-representation of the quasi-local algebra of the trivial net on `ℂ`**: + evaluate the quasi-local observable to a scalar, then let that scalar multiply. Unlike + `zeroHom` it carries `1` to `1` (`unitalRep_π_one`). -/ +noncomputable def unitalHom : trivialNet.quasiLocalCStarAlgebra →⋆ₙₐ[ℂ] (ℂ →L[ℂ] ℂ) where + toFun z := evalQuasiLocal z • (1 : ℂ →L[ℂ] ℂ) + map_smul' c z := by simp only [evalQuasiLocal_smul, smul_assoc, MonoidHom.id_apply] + map_zero' := by + rw [map_zero] + exact ContinuousLinearMap.ext fun z => by simp + map_add' _ _ := by + rw [map_add] + exact ContinuousLinearMap.ext fun z => by simp [add_mul] + map_mul' _ _ := by rw [map_mul, smul_mul_assoc, one_mul, smul_smul] + map_star' _ := by rw [evalQuasiLocal_star, star_smul, star_one] + +/-- The unital representation of the trivial net's quasi-local C⋆-algebra, on `ℂ`. -/ +noncomputable def unitalRep : CStarRep trivialNet.quasiLocalCStarAlgebra where + H := ℂ + π := unitalHom + +/-- **The unital representation is unital**: `π 1 = 1`. This is what `zeroRep` fails, and the + reason this second witness exists — without it the representation-level split property would be + inhabited only by a `π` that annihilates the whole algebra. -/ +@[simp] lemma unitalRep_π_one : unitalRep.π 1 = 1 := by + change evalQuasiLocal 1 • (1 : ℂ →L[ℂ] ℂ) = 1 + rw [map_one, one_smul] + +/-- **The trivial net has the split property in the unital representation too.** Like + `trivialNet_splitProperty` this is an instance of `VonNeumannNet.splitProperty_of_complex`: the + representation being unital changes the `*`-map but not the Hilbert space, and it is + `dim H = 1` that makes the property hold. Recorded so that the witness set for + `LocalNet.SplitProperty` is not confined to a degenerate representation. -/ +theorem unitalRep_splitProperty : trivialNet.SplitProperty unitalRep := + VonNeumannNet.splitProperty_of_complex (trivialNet.vonNeumannNet unitalRep) + +end LocalNet.Examples diff --git a/QuantumSystem/Algebra/LocalNet/Net.lean b/QuantumSystem/Algebra/LocalNet/Net.lean new file mode 100644 index 0000000..92fd016 --- /dev/null +++ b/QuantumSystem/Algebra/LocalNet/Net.lean @@ -0,0 +1,439 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.Classes +public import Mathlib.Order.DirectedInverseSystem + +/-! +# Local nets of C⋆-algebras + +This file contains the abstract **local net** data in Haag–Kastler form: the regions form an +abstract *causal index set* — an ordered set `K` of regions (in the literature, the open bounded +regions of spacetime ordered by inclusion) equipped with a causal orthogonality relation +`O₁ ⟂ O₂` (`CausalOrthogonality`, abstracting spacelike separation) — and each region is assigned +a local C⋆-algebra, with functorial isotony embeddings and locality. Lattice (quantum spin system) +nets are the instance `K = Finset sites` with `⟂ = Disjoint`. The literature's third axiom on the +index set — every region admits a causally disjoint one — is the mixin `CausalIndexSet`, kept +separate because not every index family of the corpus satisfies it. + +The order is carried as a `Preorder` throughout, not a `PartialOrder`: the literature's index sets +are ordered by inclusion and so are partial orders, but antisymmetry is used nowhere in this +development — not by the net axioms, not by the quasi-local algebra, not by the split property — so +demanding it would restrict the interface without buying anything. A concrete index set that is a +partial order is of course still an instance. + +**Directedness, and what locality means without it.** No definition in this file assumes the index +set directed, so a non-directed `K` — the proper intervals of the circle, wedges — carries the data +of a `LocalNet` perfectly well. The *locality axiom*, however, is weaker there than its name +suggests: commutation of `𝔄(O₁)` with `𝔄(O₂)` has to be stated inside an algebra containing both, +so the axiom binds a common upper bound of the two regions and therefore constrains only the +causally orthogonal pairs that have one. That is Guido–Longo–Roberts–Verch's derived relation `⊥̂` +(`CausalOrthogonality.OrthogonalHat`), which collapses onto `⟂` whenever `K` is directed +(`CausalOrthogonality.orthogonalHat_iff`). The converse fails for a *fixed* orthogonality +relation — an empty `⟂` collapses on every preorder — and becomes true once the relation is +quantified over: directedness of `K` is equivalent to `⊥̂ = ⟂` holding for *every* +`CausalOrthogonality K` (`CausalOrthogonality.isDirectedOrder_iff_forall_orthogonalHat_iff`). +The dichotomy is spelled out at +`LocalNet.exists_locality_of_orthogonalHat` and `LocalNet.exists_locality_of_orthogonal`, and the +regions carrying the interesting statements are precisely the ones it bites on: a region and its +causal complement typically have no common upper bound in the index set — on the circle their +union misses only two points and so is neither connected nor non-dense, hence not a region. A net +over such an index set needing locality for `⟂` itself must be taken as a `VonNeumannNet` +(`QuantumSystem.Algebra.LocalNet.SplitProperty`), where the ambient `𝓑(H)` removes the obstruction. + +The file deliberately stops at the net-level axioms; in particular, additivity/generation axioms +(e.g. `𝔄(O₁ ⊔ O₂)` being generated by the images of `𝔄(O₁)` and `𝔄(O₂)`, as in tensor-product +spin-system nets) are intentionally out of scope here and belong to concrete instantiations. + +The quasi-local algebra built from a net lives in `LocalNet.QuasiLocalAlgebra` (and requires the +index set to be directed); covariance data and its action live in `LocalNet.Covariance` / +`LocalNet.QuasiLocalAlgebra`. + +## Notation + +`𝔄(O)` in the prose above is documentation shorthand, following the AQFT literature, for the local +C⋆-algebra `N.algebra O` of a region; where a representation is in play, `𝓡(O)` abbreviates the +local von Neumann algebra `N.localVonNeumannAlgebra R O`. Neither is a Lean notation, and +deliberately so: both objects carry the net `N` — and `𝓡(O)` also the representation `R` — as +parameters that a bare `𝔄(O)` could not capture, so a faithful notation would have to spell them +out anyway. The symbols stay in the prose; the code writes the projections. +-/ + +@[expose] public section + +/-! ### Causal index sets -/ + +/-- A **causal orthogonality relation** on an index set of regions: `O₁ ⟂ O₂` abstracts causal + (spacelike) disjointness of regions, the relation under which the Haag–Kastler locality axiom + applies. It is symmetric and hereditary under shrinking regions. + + The literature's axiomatization (Guido–Longo–Roberts–Verch §3.1, the only abstract one in the + sources behind `docs/math/causal-index-set.md`) asks for three conditions: symmetry, heredity, + and the existence of causal complements (`∀ O₁, ∃ O₂, O₁ ⟂ O₂`). This class carries the first + two, which are properties of the relation itself; the third is a property of the *index set* + and is carried separately, as the mixin `CausalIndexSet` below. Splitting it off keeps this + class available where the third condition is absent or in doubt: the corpus secures it in + three different ways — through a membership condition (Guido–Longo–Roberts–Verch's regular + diamonds and Köster's proper intervals demand a non-void causal complement to *be* a region), + by admitting the empty region (lattice regions, where `∅` is disjoint from everything), or not + at all (Halvorson–Müger's double cones state no such clause) — and on a time-slab around a + compact Cauchy surface it fails under the reading of Brunetti–Fredenhagen–Verch that their own + category of spacetimes forces, though read literally their index set contains `∅` and the + condition then holds vacuously. A class demanding the condition could not carry such an index + set at all. + + What the third condition buys — on a *directed* index set, through heredity — is that causal + orthogonality refines the *other* derived relation of Guido–Longo–Roberts–Verch (`⊥̃`, a common + orthogonal partner rather than a common upper bound), and through it the duality theory of the + dual net and the DHR selection criterion; without directedness it has no order-theoretic + consequence at all, since adjoining a bottom element related to everything satisfies all three + conditions on any poset. None of that is used here. + + The relation is *not* required to be irreflexive: `O ⟂ O` legitimately holds for degenerate + regions (e.g. the empty lattice region) and forces `𝔄(O)` to be commutative. For Minkowski + double cones one instantiates `⟂` with spacelike separation — note this is *not* set + disjointness (disjoint timelike-separated regions are not causally orthogonal), which is why + the relation is carried as data rather than derived from the order. -/ +class CausalOrthogonality (K : Type*) [Preorder K] where + /-- The causal orthogonality relation `O₁ ⟂ O₂` on regions. -/ + Orthogonal : K → K → Prop + /-- Causal orthogonality is symmetric. -/ + orthogonal_symm : ∀ ⦃O₁ O₂ : K⦄, Orthogonal O₁ O₂ → Orthogonal O₂ O₁ + /-- Causal orthogonality is hereditary under shrinking the left region. -/ + orthogonal_mono_left : ∀ ⦃O₀ O₁ O₂ : K⦄, O₀ ≤ O₁ → Orthogonal O₁ O₂ → Orthogonal O₀ O₂ + +namespace CausalOrthogonality + +/-- `O₁ ⟂ O₂` : the regions `O₁` and `O₂` are causally orthogonal (spacelike separated). + + The token `⟂` is also used globally by Mathlib for `Submodule.IsOrtho` at precedence 50; + this scoped notation is declared at the same precedence so that the two coexist by + type-directed disambiguation. -/ +scoped infixl:50 " ⟂ " => CausalOrthogonality.Orthogonal + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] + +/-- Causal orthogonality is symmetric (dot-notation form). -/ +theorem Orthogonal.symm {O₁ O₂ : K} (h : O₁ ⟂ O₂) : O₂ ⟂ O₁ := + orthogonal_symm h + +/-- Causal orthogonality is hereditary under shrinking the left region. -/ +theorem Orthogonal.mono_left {O₀ O₁ O₂ : K} (h₀ : O₀ ≤ O₁) (h : O₁ ⟂ O₂) : O₀ ⟂ O₂ := + orthogonal_mono_left h₀ h + +/-- Causal orthogonality is hereditary under shrinking the right region. -/ +theorem Orthogonal.mono_right {O₁ O₂ O₃ : K} (h₀ : O₃ ≤ O₂) (h : O₁ ⟂ O₂) : O₁ ⟂ O₃ := + (h.symm.mono_left h₀).symm + +/-- Causal orthogonality is hereditary under shrinking both regions. -/ +theorem Orthogonal.mono {O₁' O₁ O₂' O₂ : K} (h₁ : O₁' ≤ O₁) (h₂ : O₂' ≤ O₂) + (h : O₁ ⟂ O₂) : O₁' ⟂ O₂' := + (h.mono_left h₁).mono_right h₂ + +/-! ### The derived relation `⊥̂` + +Guido–Longo–Roberts–Verch derive from `⟂` a relation written `⊥̂`, obtained by supplementing +`O₁ ⟂ O₂` with the demand that `O₁` and `O₂` have a **common upper bound** in the index set. It is +purely order- and `⟂`-theoretic — no topology, no closures — so it is available over +`CausalOrthogonality` exactly as that class stands. On a directed index set it collapses back onto +`⟂` (`orthogonalHat_iff`); off one it *can* be strictly weaker, though whether it is depends on the +relation — the collapse characterises directedness only after quantifying over all causal +orthogonality relations on the index set (`isDirectedOrder_iff_forall_orthogonalHat_iff`). The whole +`⟂` / `⊥̂` distinction is the price of dropping directedness. + +It is carried here because it, and not `⟂`, is what the locality axiom of `LocalNet` below +actually constrains: see `LocalNet.locality` and `LocalNet.exists_locality_of_orthogonalHat`. + +No notation is introduced for it: the literature's `⊥̂` needs a combining accent, and the relation +appears rarely enough that the spelled-out name reads better than a near-collision with `⟂`. +-/ + +/-- The **derived orthogonality relation `⊥̂`** of Guido–Longo–Roberts–Verch: `O₁` and `O₂` are + causally orthogonal *and* admit a common upper bound in the index set. + + This is the relation on which the locality axiom of a `LocalNet` bites, since commutation of + `𝔄(O₁)` with `𝔄(O₂)` can only be *stated* inside an algebra containing both. On a directed + index set it is `⟂` itself (`orthogonalHat_iff`). -/ +def OrthogonalHat (O₁ O₂ : K) : Prop := + O₁ ⟂ O₂ ∧ ∃ O₃, O₁ ≤ O₃ ∧ O₂ ≤ O₃ + +/-- The derived relation refines causal orthogonality. -/ +theorem OrthogonalHat.orthogonal {O₁ O₂ : K} (h : OrthogonalHat O₁ O₂) : O₁ ⟂ O₂ := + h.1 + +/-- A common upper bound of a pair related by the derived relation. -/ +theorem OrthogonalHat.exists_upperBound {O₁ O₂ : K} (h : OrthogonalHat O₁ O₂) : + ∃ O₃, O₁ ≤ O₃ ∧ O₂ ≤ O₃ := + h.2 + +/-- The derived relation is symmetric. Symmetry of `⟂` is what is used on the first component; + a common upper bound is symmetric on the nose. -/ +theorem OrthogonalHat.symm {O₁ O₂ : K} (h : OrthogonalHat O₁ O₂) : OrthogonalHat O₂ O₁ := + ⟨h.1.symm, h.2.imp fun _ hO => ⟨hO.2, hO.1⟩⟩ + +/-- The derived relation is hereditary under shrinking the left region: heredity of `⟂` handles + the first component and transitivity of `≤` the upper bound. -/ +theorem OrthogonalHat.mono_left {O₀ O₁ O₂ : K} (h₀ : O₀ ≤ O₁) (h : OrthogonalHat O₁ O₂) : + OrthogonalHat O₀ O₂ := + ⟨h.1.mono_left h₀, h.2.imp fun _ hO => ⟨h₀.trans hO.1, hO.2⟩⟩ + +/-- The derived relation is hereditary under shrinking the right region. -/ +theorem OrthogonalHat.mono_right {O₁ O₂ O₃ : K} (h₀ : O₃ ≤ O₂) (h : OrthogonalHat O₁ O₂) : + OrthogonalHat O₁ O₃ := + (h.symm.mono_left h₀).symm + +/-- The derived relation is hereditary under shrinking both regions. -/ +theorem OrthogonalHat.mono {O₁' O₁ O₂' O₂ : K} (h₁ : O₁' ≤ O₁) (h₂ : O₂' ≤ O₂) + (h : OrthogonalHat O₁ O₂) : OrthogonalHat O₁' O₂' := + (h.mono_left h₁).mono_right h₂ + +/-- **Passing to the derived relation is idempotent**: supplementing `⊥̂` with the demand for a + common upper bound asks for nothing new, since `⊥̂` already carries one. No axiom of + `CausalOrthogonality` is used. -/ +theorem orthogonalHat_and_exists_upperBound_iff {O₁ O₂ : K} : + (OrthogonalHat O₁ O₂ ∧ ∃ O₃, O₁ ≤ O₃ ∧ O₂ ≤ O₃) ↔ OrthogonalHat O₁ O₂ := + ⟨And.left, fun h => ⟨h, h.2⟩⟩ + +/-- **On a directed index set the derived relation is causal orthogonality itself.** Directedness + supplies the common upper bound, and no axiom of `CausalOrthogonality` is needed. + + Only this implication holds for a *fixed* orthogonality relation: off a directed index set the + two need not differ, since an empty `⟂` collapses onto `⊥̂` on any preorder. The sharp converse + quantifies over the relation (`isDirectedOrder_iff_forall_orthogonalHat_iff`). Where they do + differ it is `⊥̂` that the locality axiom of a `LocalNet` constrains: on the two-element + antichain `{O₁, O₂}` with `O₁ ⟂ O₂` there is no common upper bound at all, so `⊥̂` is empty + there while `⟂` is not. -/ +theorem orthogonalHat_iff [IsDirectedOrder K] {O₁ O₂ : K} : + OrthogonalHat O₁ O₂ ↔ O₁ ⟂ O₂ := + ⟨OrthogonalHat.orthogonal, fun h => ⟨h, directed_of (· ≤ ·) O₁ O₂⟩⟩ + +/-- **Directedness is exactly the coincidence of `⊥̂` with `⟂`, quantified over the relation.** + For one fixed `CausalOrthogonality K` the coincidence is strictly weaker than directedness — + the empty relation collapses on every preorder — so the literature's "`⊥̂ = ⟂` precisely on + directed index sets" is a statement about the index set, not about a single net's causal + structure. Read that way it is true: the forward direction is `orthogonalHat_iff`, and the + converse tests directedness against the *total* relation, in which every pair is causally + orthogonal and `⊥̂` therefore records nothing but the existence of a common upper bound. -/ +theorem isDirectedOrder_iff_forall_orthogonalHat_iff (K : Type*) [Preorder K] : + IsDirectedOrder K ↔ + ∀ [CausalOrthogonality K] (O₁ O₂ : K), OrthogonalHat O₁ O₂ ↔ O₁ ⟂ O₂ := by + constructor + · intro _ _ _ _ + exact orthogonalHat_iff + · intro h + refine ⟨fun O₁ O₂ => ?_⟩ + letI : CausalOrthogonality K := + { Orthogonal := fun _ _ => True + orthogonal_symm := fun _ _ _ => trivial + orthogonal_mono_left := fun _ _ _ _ _ => trivial } + exact ((h O₁ O₂).2 trivial).2 + +end CausalOrthogonality + +open scoped CausalOrthogonality + +/-- A **causal index set** in the full sense of Guido–Longo–Roberts–Verch §3.1: on top of the + symmetry and heredity carried by `CausalOrthogonality`, the third condition of the + axiomatization — the **existence of causal complements**, every region admitting a causally + disjoint one. An ordered `K` with `[CausalOrthogonality K]` and this mixin is exactly the + adopted general form of the extraction note `docs/math/causal-index-set.md`. + + Carried as a mixin rather than folded into `CausalOrthogonality`, because unlike symmetry and + heredity this condition is a property of the index set rather than of the relation, and the + corpus's index families do not all satisfy it (see the docstring of `CausalOrthogonality`). + It is genuinely model-dependent input: what this development draws from it is + `CausalIndexSet.exists_orthogonalHat` — on a directed index set the locality axiom of a + `LocalNet` bites at every region — while the duality theory it really feeds (the dual net, the + DHR selection criterion) is not formalized here; the literature's causal index set is not + expressible without it. -/ +class CausalIndexSet (K : Type*) [Preorder K] [CausalOrthogonality K] : Prop where + /-- Every region admits a causally disjoint region — axiom c) of the + Guido–Longo–Roberts–Verch axiomatization. -/ + exists_orthogonal : ∀ O₁ : K, ∃ O₂, O₁ ⟂ O₂ + +/-- **On a directed causal index set every region has a `⊥̂`-orthogonal partner**: directedness + upgrades the causal complement supplied by `exists_orthogonal` to the derived relation `⊥̂` + (`CausalOrthogonality.orthogonalHat_iff`), which is the relation the locality axiom of a + `LocalNet` actually constrains. So the locality axiom is nowhere vacuous on such an index set: + `LocalNet.exists_locality_of_orthogonalHat` applies at every region. -/ +theorem CausalIndexSet.exists_orthogonalHat {K : Type*} [Preorder K] [CausalOrthogonality K] + [IsDirectedOrder K] [CausalIndexSet K] (O : K) : + ∃ O', CausalOrthogonality.OrthogonalHat O O' := + let ⟨O', h⟩ := CausalIndexSet.exists_orthogonal O + ⟨O', CausalOrthogonality.orthogonalHat_iff.2 h⟩ + +/-- On the finite regions of a lattice of sites, causal orthogonality is plain set disjointness: + a lattice net is kinematical (equal-time), so "spacelike separated" degenerates to "disjoint in + space" (Naaijkens, *Anyons in Infinite Quantum Systems*, 2012, §3.4). -/ +instance {α : Type*} : CausalOrthogonality (Finset α) where + Orthogonal := Disjoint + orthogonal_symm _ _ h := h.symm + orthogonal_mono_left _ _ _ h₀ h := h.mono_left h₀ + +/-- The lattice regions form a causal index set in the full sense: the **empty region** is + disjoint from every region, so causal complements exist for free. This is how the literature's + lattice index set secures the condition — through `∅ ∈ 𝒦` rather than through a membership + clause — at the price of `⟂` being reflexive at `∅`. -/ +instance {α : Type*} : CausalIndexSet (Finset α) := + ⟨fun Λ => ⟨∅, Finset.disjoint_empty_right Λ⟩⟩ + +/-- A **local net of C⋆-algebras** over a causal index set `K` — an ordered set of + regions with a causal orthogonality relation `⟂` (`CausalOrthogonality`): the assignment + `O ↦ 𝔄(O)` together with isotony embeddings `incl : 𝔄(O) →⋆ₐ[ℂ] 𝔄(O')` for `O ≤ O'`, + functorial (`incl_refl`/`incl_trans`) and local (`locality`). This is the Haag–Kastler net + structure over an abstract index set, *without* the injectivity requirement on the isotony + embeddings; the literature's net (Naaijkens, *Anyons in Infinite Quantum Systems*, 2012, §3.4) + is `LocalNet` together with the `Faithful` class, which carries that requirement. Lattice nets are the instance + `K = Finset sites` with `⟂ = Disjoint`. -/ +structure LocalNet (K : Type*) [Preorder K] [CausalOrthogonality K] where + /-- The local C⋆-algebra `𝔄(O)` assigned to a region. For any self-orthogonal region `O ⟂ O` + (e.g. the empty lattice region) the axioms force `𝔄(O)` to be commutative (instantiate + `locality` at `O₁ = O₂ = O`) but do *not* force it to be trivial: the literature's + `𝔄(∅) = ℂ·1` is not required and must be imposed by a concrete instantiation when needed. -/ + algebra : K → Type* + /-- Each local algebra is a (complex) C⋆-algebra. -/ + [algebraCStar : ∀ O, CStarAlgebra (algebra O)] + /-- **Isotony**: for `O ≤ O'` an inclusion of local algebras as a unital `*`-homomorphism. -/ + incl : ∀ {O O' : K}, O ≤ O' → (algebra O →⋆ₐ[ℂ] algebra O') + /-- The identity inclusion `O ≤ O` acts as the identity. -/ + incl_refl : ∀ {O : K} (x : algebra O), incl (le_refl O) x = x + /-- **Functoriality**: the inclusions compose, so the net is a directed system. -/ + incl_trans : ∀ {O₁ O₂ O₃ : K} (h₁₂ : O₁ ≤ O₂) (h₂₃ : O₂ ≤ O₃) (x : algebra O₁), + incl h₂₃ (incl h₁₂ x) = incl (h₁₂.trans h₂₃) x + /-- **Locality** (microcausality, Einstein causality): observables localised in causally + orthogonal regions commute inside any common larger region. + + The common region `O` is bound *before* the orthogonality hypothesis, and that is not an + accident of phrasing: `Commute x y` for `x : 𝔄(O₁)` and `y : 𝔄(O₂)` is not even typeable + without an algebra containing both, and over a non-directed index set there need be no such + region — nor any quasi-local algebra to fall back on. So the pairs this field constrains are + exactly those causally orthogonal pairs that admit a common upper bound, which is the derived + relation `CausalOrthogonality.OrthogonalHat` (`⊥̂`) rather than `⟂` itself. The two coincide + on directed index sets (`CausalOrthogonality.orthogonalHat_iff`) — and, once the orthogonality + relation is quantified over, only there + (`CausalOrthogonality.isDirectedOrder_iff_forall_orthogonalHat_iff`) — which is where every + consumer of this field lives. + + A net over a genuinely non-directed index set that needs locality for `⟂` itself must be + taken as a `VonNeumannNet` (`QuantumSystem.Algebra.LocalNet.SplitProperty`), whose ambient + `𝓑(H)` lets `algebra_le_commutant_of_orthogonal` state locality with no upper bound. -/ + locality : ∀ {O₁ O₂ O : K} (h₁ : O₁ ≤ O) (h₂ : O₂ ≤ O), O₁ ⟂ O₂ → + ∀ (x : algebra O₁) (y : algebra O₂), Commute (incl h₁ x) (incl h₂ y) + +namespace LocalNet + +attribute [instance] algebraCStar + +section SemilatticeSup + +variable {K : Type*} [SemilatticeSup K] [CausalOrthogonality K] + +/-- Smart constructor for `LocalNet` over a join-semilattice of regions, taking locality only + inside the canonical join region `O₁ ⊔ O₂`: given `incl_trans`, commutation there pushes + forward along `incl : 𝔄(O₁ ⊔ O₂) →⋆ₐ[ℂ] 𝔄(O)` to any common superregion `O`, so the join case + is equivalent to the general `locality` field. For lattice nets (`K = Finset sites`) the join + is the union of regions. -/ +def mk' (algebra : K → Type*) [algebraCStar : ∀ O, CStarAlgebra (algebra O)] + (incl : ∀ {O O' : K}, O ≤ O' → (algebra O →⋆ₐ[ℂ] algebra O')) + (incl_refl : ∀ {O : K} (x : algebra O), incl (le_refl O) x = x) + (incl_trans : ∀ {O₁ O₂ O₃ : K} (h₁₂ : O₁ ≤ O₂) (h₂₃ : O₂ ≤ O₃) (x : algebra O₁), + incl h₂₃ (incl h₁₂ x) = incl (h₁₂.trans h₂₃) x) + (locality_sup : ∀ {O₁ O₂ : K}, O₁ ⟂ O₂ → + ∀ (x : algebra O₁) (y : algebra O₂), + Commute (incl le_sup_left x) (incl le_sup_right y)) : + LocalNet K where + algebra := algebra + incl := incl + incl_refl := incl_refl + incl_trans := incl_trans + locality h₁ h₂ hortho x y := by + have h := (locality_sup hortho x y).map (incl (sup_le h₁ h₂)) + rwa [incl_trans, incl_trans] at h + +end SemilatticeSup + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] (N : LocalNet K) + +/-- The net forms a directed system of `*`-algebras: the isotony embeddings compose and the + identity inclusion is the identity. -/ +instance directedSystem : + DirectedSystem N.algebra (fun _ _ h => ⇑(N.incl h)) where + map_self _ x := N.incl_refl x + map_map _ _ _ hij hjk x := N.incl_trans hij hjk x + +/-! ### What locality constrains + +The `locality` field quantifies over a common upper bound of the two regions, so the pairs it +constrains are exactly those related by the derived relation `⊥̂` of +`CausalOrthogonality.OrthogonalHat`. The two lemmas below say this in both directions of the +directedness dichotomy: `⊥̂`-related pairs always get commutation somewhere, and on a directed +index set that is every causally orthogonal pair. +-/ + +/-- **Locality is locality for the derived relation `⊥̂`**: a causally orthogonal pair admitting a + common upper bound commutes inside one. This is the whole content of the `locality` field — + for a causally orthogonal pair with *no* common upper bound the field says nothing, and cannot, + since the commutation has nowhere to be stated. -/ +theorem exists_locality_of_orthogonalHat {O₁ O₂ : K} + (h : CausalOrthogonality.OrthogonalHat O₁ O₂) : + ∃ (O : K) (h₁ : O₁ ≤ O) (h₂ : O₂ ≤ O), + ∀ (x : N.algebra O₁) (y : N.algebra O₂), Commute (N.incl h₁ x) (N.incl h₂ y) := + let ⟨O, h₁, h₂⟩ := h.exists_upperBound + ⟨O, h₁, h₂, N.locality h₁ h₂ h.orthogonal⟩ + +/-- **On a directed index set locality is locality for `⟂` itself**: directedness supplies the + common upper bound, so every causally orthogonal pair commutes inside some region. This is the + setting of every consumer of the `locality` field — in particular of + `LocalNet.ιLocal_commute_of_orthogonal`, where the quasi-local algebra provides the ambient + algebra in which the commutation is finally stated for all pairs at once. -/ +theorem exists_locality_of_orthogonal [IsDirectedOrder K] {O₁ O₂ : K} (h : O₁ ⟂ O₂) : + ∃ (O : K) (h₁ : O₁ ≤ O) (h₂ : O₂ ≤ O), + ∀ (x : N.algebra O₁) (y : N.algebra O₂), Commute (N.incl h₁ x) (N.incl h₂ y) := + N.exists_locality_of_orthogonalHat (CausalOrthogonality.orthogonalHat_iff.2 h) + +/-! ### Faithful nets + +A net is *faithful* when its isotony embeddings are injective (the standard AQFT +non-degeneracy condition). The quasi-local C⋆-norm built from this hypothesis lives in +`LocalNet.QuasiLocalAlgebra`. +-/ + +/-- A local net is **faithful** when all its isotony embeddings are injective. In the standard + AQFT definition the isotony embeddings are *injective* unital `*`-homomorphisms (Naaijkens, + *Anyons in Infinite Quantum Systems*, 2012, §3.4), so a faithful local net — `LocalNet` + together with `Faithful` — is the literature's net. + Carried as a typeclass rather than a structure field so algebraic constructions can state the + non-degeneracy hypothesis only where they need it. -/ +class Faithful {K : Type*} [Preorder K] [CausalOrthogonality K] (N : LocalNet K) : Prop where + /-- Every isotony embedding of the net is injective. -/ + incl_injective : ∀ {O O' : K} (h : O ≤ O'), Function.Injective (N.incl h) + +/-! ### Region-equality transport -/ + +/-- Transport a local algebra along an equality of regions, as a `*`-isomorphism. Used to identify + local algebras whose regions are propositionally equal. -/ +def algebraCongr {O O' : K} (h : O = O') : N.algebra O ≃⋆ₐ[ℂ] N.algebra O' := by + subst h; exact StarAlgEquiv.refl + +/-- The isotony embeddings are natural with respect to the region-equality transport. -/ +lemma incl_algebraCongr {O₁ O₂ O₁' O₂' : K} (e₁ : O₁ = O₁') (e₂ : O₂ = O₂') + (h : O₁ ≤ O₂) (h' : O₁' ≤ O₂') (x : N.algebra O₁) : + N.algebraCongr e₂ (N.incl h x) = N.incl h' (N.algebraCongr e₁ x) := by + subst e₁; subst e₂; rfl + +/-- Transports along composable region equalities compose. -/ +@[simp] lemma algebraCongr_trans {O₁ O₂ O₃ : K} (h₁ : O₁ = O₂) (h₂ : O₂ = O₃) + (x : N.algebra O₁) : + N.algebraCongr h₂ (N.algebraCongr h₁ x) = N.algebraCongr (h₁.trans h₂) x := by + subst h₁; subst h₂; rfl + +/-- A transport along a reflexive region equality is the identity. -/ +@[simp] lemma algebraCongr_self {O : K} (h : O = O) (x : N.algebra O) : + N.algebraCongr h x = x := by + rw [Subsingleton.elim h rfl]; rfl + +/-- Cancelling a transport against a target value moves it to the other side. -/ +lemma algebraCongr_eq_iff {O O' : K} (h : O = O') (x : N.algebra O) + (y : N.algebra O') : N.algebraCongr h x = y ↔ x = N.algebraCongr h.symm y := by + subst h; simp + +end LocalNet diff --git a/QuantumSystem/Algebra/LocalNet/QuasiLocalAlgebra.lean b/QuantumSystem/Algebra/LocalNet/QuasiLocalAlgebra.lean new file mode 100644 index 0000000..3ecc812 --- /dev/null +++ b/QuantumSystem/Algebra/LocalNet/QuasiLocalAlgebra.lean @@ -0,0 +1,424 @@ +module + +public import QuantumSystem.Algebra.LocalNet.Covariance +public import QuantumSystem.ForMathlib.Algebra.Colimit.DirectLimitStar +public import QuantumSystem.ForMathlib.Analysis.CStarAlgebra.DirectLimit +public import QuantumSystem.ForMathlib.Topology.Algebra.CStarCompletion + +/-! +# The quasi-local algebra of a local net + +The **algebra of local observables** and the **quasi-local C⋆-algebra** of an abstract local net +`LocalNet`. These constructions apply to any local net over a *directed* causal index set +(`IsDirectedOrder`, with a region to start from, `Nonempty`): directedness is what makes the +union of the local algebras an algebra. Lattice nets (`K = Finset sites`) are directed by unions +with the empty region as base point. + +* `LocalNet.localObservables` is the algebraic inductive limit `‾⋃_O 𝔄(O)` of the local algebras + along the isotony embeddings, with cocone `ιLocal`, exhaustion (`exists_ιLocal`) and locality + (`ιLocal_commute_of_orthogonal`). +* For a `Faithful` net the connecting maps are isometric, so the algebra of local observables + carries a C⋆-norm whose completion `LocalNet.quasiLocalCStarAlgebra` is the AQFT quasi-local + algebra `𝔄 = ‾⋃_O 𝔄(O)` (Naaijkens, *Anyons in Infinite Quantum Systems*, 2012, §1.3; + Bratteli–Robinson Vol.2 §6.2), with isometric local embeddings `ιLocalCStar` — unital + `*`-homomorphisms `𝔄(O) →⋆ₐ[ℂ] 𝔄`, injective and with *closed* range, whose ranges are dense + in `𝔄` only taken together (`denseRange_iUnion_ιLocalCStar`). + +A `LocalNet.Covariance` (defined in `LocalNet.Covariance`) acts on these algebras: its per-region +`*`-isomorphisms assemble into a ring endomorphism `localObservableCovariance` of the algebra of local +observables, which is functorial and is shown `ℂ`-linear and `*`-preserving — hence bundled as a +`*`-automorphism `localObservableCovarianceEquiv`, assembled into a group homomorphism +`localObservableCovarianceHom`. For a `Faithful` net it is isometric and extends to a `*`-automorphism +`quasiLocalCStarCovarianceEquiv` of the quasi-local C⋆-algebra. + +## Notation + +`𝔄(O)` and `𝓡(O)` in the prose above are documentation shorthand for the local C⋆-algebra +`N.algebra O` and the local von Neumann algebra `N.localVonNeumannAlgebra R O`; the convention — +and why neither is a Lean notation — is stated in full in `QuantumSystem.Algebra.LocalNet.Net`. +-/ + +@[expose] public section + +namespace LocalNet + +open scoped CausalOrthogonality + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] [IsDirectedOrder K] [Nonempty K] +variable (N : LocalNet K) + +/-! ### Algebra of local observables + +These constructions apply to any abstract local net over a directed causal index set. +-/ + +/-- The **algebra of local observables** of the net: the algebraic inductive limit of the local + algebras along the isotony embeddings. Its C⋆-completion is the quasi-local algebra. -/ +noncomputable abbrev localObservables : Type _ := + DirectLimit N.algebra (fun _ _ h => N.incl h) + +omit [Nonempty K] in +/-- Componentwise behaviour of the involution on the algebra of local observables. The + `Star`, `StarRing`, `Algebra ℂ` and `StarModule ℂ` instances come from the general + direct-limit constructions, since each `algebra O` is a `ℂ`-`*`-algebra and `incl` is a + `*`-algebra homomorphism. -/ +@[simp] lemma star_mk {O : K} (X : N.algebra O) : + star (⟦⟨O, X⟩⟧ : N.localObservables) = ⟦⟨O, star X⟩⟧ := rfl + +/-- The canonical embedding `𝔄(O) ↪ 𝔄_loc` of a local algebra into the algebra of local + observables, as a unital ring homomorphism (the cocone of the inductive limit). -/ +noncomputable def ιLocal (O : K) : + N.algebra O →+* N.localObservables := + DirectLimit.Ring.of N.algebra (fun _ _ h => N.incl h) O + +/-- Compatibility of the cocone with the isotony embeddings: including `X` from `O` into the + larger region `O'` and then into `𝔄_loc` is the same as including `X` directly. -/ +@[simp] lemma ιLocal_incl {O O' : K} (h : O ≤ O') (X : N.algebra O) : + N.ιLocal O' (N.incl h X) = N.ιLocal O X := + DirectLimit.Ring.of_f (G := N.algebra) (f := fun _ _ h => N.incl h) h X + +/-- The cocone is a `*`-homomorphism: it intertwines the local and quasi-local involutions. -/ +@[simp] lemma ιLocal_star {O : K} (X : N.algebra O) : + N.ιLocal O (star X) = star (N.ιLocal O X) := + (star_mk (N := N) X).symm + +/-- The cocone is `ℂ`-linear: scalars act componentwise on the inductive limit. -/ +@[simp] lemma ιLocal_smul (c : ℂ) {O : K} (X : N.algebra O) : + N.ιLocal O (c • X) = c • N.ιLocal O X := + (DirectLimit.smul_def O X c).symm + +/-- The cocone of the inductive limit absorbs the region-equality transport. -/ +@[simp] lemma ιLocal_algebraCongr {O O' : K} (h : O = O') (x : N.algebra O) : + N.ιLocal O' (N.algebraCongr h x) = N.ιLocal O x := by + subst h; rfl + +/-- **Exhaustion**: every element of the algebra of local observables is the image of a local + observable from some region — the union of the local algebras is the whole limit. -/ +theorem exists_ιLocal (z : N.localObservables) : + ∃ (O : K) (X : N.algebra O), z = N.ιLocal O X := by + induction z using DirectLimit.induction with + | _ O X => exact ⟨O, X, rfl⟩ + +/-- **Locality in the algebra of local observables**: observables localised in causally orthogonal + regions commute inside `𝔄_loc`. Pushes both observables into a directed upper bound of the two + regions and lifts the net's `locality` along the ring-hom cocone. -/ +theorem ιLocal_commute_of_orthogonal {O₁ O₂ : K} (hd : O₁ ⟂ O₂) + (X : N.algebra O₁) (Y : N.algebra O₂) : + Commute (N.ιLocal O₁ X) (N.ιLocal O₂ Y) := by + obtain ⟨O, h₁, h₂⟩ := directed_of (· ≤ ·) O₁ O₂ + rw [← N.ιLocal_incl h₁ X, ← N.ιLocal_incl h₂ Y] + exact (N.locality h₁ h₂ hd X Y).map (N.ιLocal O) + +/-! ### Faithful nets and the quasi-local C⋆-algebra + +For a faithful net the connecting maps are isometric, so the algebra of local observables carries +a C⋆-norm whose completion is the quasi-local C⋆-algebra `𝔄 = ‾⋃_O 𝔄(O)`. +-/ + +section CStar + +variable [N.Faithful] + +/-- The algebra of local observables is a normed ring under the C⋆-norm of the inductive limit + (the inclusions are injective, hence isometric). -/ +noncomputable instance : NormedRing N.localObservables := + DirectLimit.cstarNormedRing (fun _ _ h => Faithful.incl_injective h) + +/-- The C⋆-norm of a local observable, viewed in the algebra of local observables, is its norm in + its own region's algebra: the connecting maps are isometric. -/ +@[simp] lemma norm_mk {O : K} (X : N.algebra O) : + ‖(⟦⟨O, X⟩⟧ : N.localObservables)‖ = ‖X‖ := rfl + +/-- The C⋆-norm is compatible with the `ℂ`-algebra structure. -/ +noncomputable instance : NormedAlgebra ℂ N.localObservables where + norm_smul_le c x := by + induction x using DirectLimit.induction with + | _ O X => rw [DirectLimit.smul_def, norm_mk, norm_mk]; exact norm_smul_le c X + +/-- `star` is isometric on the algebra of local observables. -/ +instance : NormedStarGroup N.localObservables where + norm_star_le x := by + induction x using DirectLimit.induction with + | _ O X => rw [star_mk, norm_mk, norm_mk]; exact (norm_star X).le + +/-- The C⋆-identity holds on the algebra of local observables. -/ +instance : CStarRing N.localObservables where + norm_mul_self_le x := by + induction x using DirectLimit.induction with + | _ O X => rw [star_mk, DirectLimit.mul_def, norm_mk, norm_mk] + exact CStarRing.norm_mul_self_le X + +/-- The **quasi-local C⋆-algebra** of a faithful net: the completion of the algebra of local + observables. This is the AQFT quasi-local algebra `𝔄 = ‾⋃_O 𝔄(O)`. -/ +noncomputable abbrev quasiLocalCStarAlgebra : Type _ := + UniformSpace.Completion N.localObservables + +noncomputable example : CStarAlgebra N.quasiLocalCStarAlgebra := inferInstance + +/-- The canonical **local embedding** `𝔄(O) → 𝔄` of a local algebra into the quasi-local + C⋆-algebra: the completion coercion composed with the inductive-limit cocone, bundled as a + unital `*`-homomorphism of `ℂ`-algebras. It is isometric (`norm_ιLocalCStar`) and therefore + injective (`ιLocalCStar_injective`), so each `𝔄(O)` sits in `𝔄` as an isomorphic copy — the + non-degeneracy the AQFT literature builds into its net axioms, here inherited from `Faithful`. + + A *single* such range is not dense: an isometric image of a complete space is complete, hence + closed, so it is dense only when it is all of `𝔄`. What is dense is the union over all regions + (`denseRange_iUnion_ιLocalCStar`), which is the `‾⋃_O 𝔄(O)` of the literature. -/ +noncomputable def ιLocalCStar (O : K) : + N.algebra O →⋆ₐ[ℂ] N.quasiLocalCStarAlgebra where + toFun := (↑) ∘ N.ιLocal O + map_one' := by simp + map_mul' X Y := by + simp only [Function.comp_apply, map_mul, UniformSpace.Completion.coe_mul] + map_zero' := by simp + map_add' X Y := by + simp only [Function.comp_apply, map_add, UniformSpace.Completion.coe_add] + commutes' c := by + simp only [Function.comp_apply, Algebra.algebraMap_eq_smul_one, N.ιLocal_smul, map_one, + UniformSpace.Completion.coe_smul, UniformSpace.Completion.coe_one] + map_star' X := by simp + +/-- The local embedding is the completion coercion after the inductive-limit cocone. -/ +lemma coe_ιLocalCStar (O : K) : ⇑(N.ιLocalCStar O) = (↑) ∘ N.ιLocal O := rfl + +/-- The local embedding is compatible with the isotony embeddings: including into a larger + region first does not change the image in the quasi-local C⋆-algebra. -/ +@[simp] lemma ιLocalCStar_incl {O O' : K} (h : O ≤ O') (X : N.algebra O) : + N.ιLocalCStar O' (N.incl h X) = N.ιLocalCStar O X := by + simp only [coe_ιLocalCStar, Function.comp_apply, ιLocal_incl] + +/-- The local embedding is a `*`-map: it intertwines the local and quasi-local involutions. -/ +@[simp] lemma ιLocalCStar_star {O : K} (X : N.algebra O) : + N.ιLocalCStar O (star X) = star (N.ιLocalCStar O X) := + map_star _ X + +/-- **The local embedding is isometric**: the connecting maps of a faithful net are isometric, so + the C⋆-norm of `𝔄(O)` is the one it inherits from the quasi-local algebra. -/ +@[simp] lemma norm_ιLocalCStar {O : K} (X : N.algebra O) : + ‖N.ιLocalCStar O X‖ = ‖X‖ := by + rw [coe_ιLocalCStar, Function.comp_apply, UniformSpace.Completion.norm_coe] + exact N.norm_mk X + +/-- The local embedding is an isometry (bundled form of `norm_ιLocalCStar`). -/ +lemma isometry_ιLocalCStar (O : K) : Isometry (N.ιLocalCStar O) := + AddMonoidHomClass.isometry_of_norm _ (N.norm_ιLocalCStar (O := O)) + +/-- **The local embedding is injective**: it is isometric, so it is an embedding of `𝔄(O)` onto a + closed C⋆-subalgebra of the quasi-local algebra. -/ +lemma ιLocalCStar_injective (O : K) : Function.Injective (N.ιLocalCStar O) := + (N.isometry_ιLocalCStar O).injective + +/-- The local algebras are dense in the quasi-local C⋆-algebra: every element is a norm-limit of + local observables. Note the union: a single local algebra has *closed*, not dense, image + (`isometry_ιLocalCStar`). -/ +theorem denseRange_iUnion_ιLocalCStar : + Dense (⋃ O : K, Set.range (N.ιLocalCStar O)) := by + refine UniformSpace.Completion.denseRange_coe.mono ?_ + rintro _ ⟨z, rfl⟩ + obtain ⟨O, X, rfl⟩ := N.exists_ιLocal z + exact Set.mem_iUnion.2 ⟨O, X, rfl⟩ + +/-- **Locality in the quasi-local C⋆-algebra**: observables localised in causally orthogonal + regions commute inside `𝔄`. Transports `ιLocal_commute_of_orthogonal` along the completion + coercion. -/ +theorem ιLocalCStar_commute_of_orthogonal {O₁ O₂ : K} (hd : O₁ ⟂ O₂) + (X : N.algebra O₁) (Y : N.algebra O₂) : + Commute (N.ιLocalCStar O₁ X) (N.ιLocalCStar O₂ Y) := + (N.ιLocal_commute_of_orthogonal hd X Y).map UniformSpace.Completion.coeRingHom + +end CStar + +namespace Covariance + +variable {N} (a : N.Covariance) + +/-! ### The induced covariance action -/ + +/-- The **covariance action** `β_a ⟦⟨O, X⟩⟧ = ⟦⟨σO, β_O X⟩⟧` of a covariance on the algebra of local + observables, as a ring homomorphism. Well-defined by naturality (`β_incl`). -/ +noncomputable def localObservableCovariance : N.localObservables →+* N.localObservables := + DirectLimit.Ring.lift N.algebra (fun _ _ h => N.incl h) N.localObservables + (fun O => (N.ιLocal (a.σ O)).comp (a.β O).toAlgEquiv.toAlgHom.toRingHom) + (fun O O' h X => by + change N.ιLocal (a.σ O') (a.β O' (N.incl h X)) = N.ιLocal (a.σ O) (a.β O X) + rw [a.β_incl h] + exact N.ιLocal_incl _ _) + +/-- Componentwise formula for the covariance action: `β_a ⟦⟨O, X⟩⟧ = ⟦⟨σO, β_O X⟩⟧`. -/ +@[simp] lemma localObservableCovariance_mk {O : K} (X : N.algebra O) : + a.localObservableCovariance (⟦⟨O, X⟩⟧ : N.localObservables) = ⟦⟨a.σ O, a.β O X⟩⟧ := + rfl + +/-- The covariance action of the identity covariance is the identity: `β_{id} = id`. -/ +@[simp] lemma localObservableCovariance_id : + (Covariance.id N).localObservableCovariance = RingHom.id N.localObservables := by + refine RingHom.ext fun z => ?_ + induction z using DirectLimit.induction with + | _ O X => rw [localObservableCovariance_mk, RingHom.id_apply]; rfl + +/-- **Functoriality of the covariance action**: composing covariances composes their actions, + `β_{a∘b} = β_a ∘ β_b`. -/ +@[simp] lemma localObservableCovariance_comp (a b : N.Covariance) : + (a.comp b).localObservableCovariance = a.localObservableCovariance.comp b.localObservableCovariance := by + refine RingHom.ext fun z => ?_ + induction z using DirectLimit.induction with + | _ O X => + simp only [RingHom.comp_apply, localObservableCovariance_mk] + rfl + +/-- The covariance action sends the unit covariance to the identity: `β_1 = id`. -/ +@[simp] lemma localObservableCovariance_one : + (1 : N.Covariance).localObservableCovariance = RingHom.id N.localObservables := by + rw [one_def, localObservableCovariance_id] + +/-- The covariance action is multiplicative: `β_{a·b} = β_a ∘ β_b`. -/ +lemma localObservableCovariance_mul (a b : N.Covariance) : + (a * b).localObservableCovariance = a.localObservableCovariance.comp b.localObservableCovariance := by + rw [mul_def, localObservableCovariance_comp] + +/-! #### The covariance action as a `*`-automorphism -/ + +/-- The covariance action is `ℂ`-linear: `β_a (c • z) = c • β_a z`, since each `β` is. -/ +lemma localObservableCovariance_smul (c : ℂ) (z : N.localObservables) : + a.localObservableCovariance (c • z) = c • a.localObservableCovariance z := by + induction z using DirectLimit.induction with + | _ O X => + rw [DirectLimit.smul_def, localObservableCovariance_mk, localObservableCovariance_mk, + DirectLimit.smul_def, map_smul] + +/-- The covariance action preserves the involution: `β_a (star z) = star (β_a z)`, since each `β` + is a `*`-isomorphism. -/ +lemma localObservableCovariance_star (z : N.localObservables) : + a.localObservableCovariance (star z) = star (a.localObservableCovariance z) := by + induction z using DirectLimit.induction with + | _ O X => + rw [star_mk, localObservableCovariance_mk, localObservableCovariance_mk, star_mk, map_star] + +/-- The covariance action as a `*`-algebra automorphism of the algebra of local observables, with + the action of the inverse covariance `a⁻¹` as its inverse. -/ +noncomputable def localObservableCovarianceEquiv : + N.localObservables ≃⋆ₐ[ℂ] N.localObservables where + toFun := a.localObservableCovariance + invFun := a⁻¹.localObservableCovariance + left_inv z := by + rw [← RingHom.comp_apply, ← localObservableCovariance_mul, inv_mul_cancel, localObservableCovariance_one, + RingHom.id_apply] + right_inv z := by + rw [← RingHom.comp_apply, ← localObservableCovariance_mul, mul_inv_cancel, localObservableCovariance_one, + RingHom.id_apply] + map_mul' := map_mul a.localObservableCovariance + map_add' := map_add a.localObservableCovariance + map_smul' := a.localObservableCovariance_smul + map_star' := a.localObservableCovariance_star + +/-- The bundled `*`-automorphism agrees with the underlying ring homomorphism. -/ +@[simp] lemma localObservableCovarianceEquiv_apply (z : N.localObservables) : + a.localObservableCovarianceEquiv z = a.localObservableCovariance z := rfl + +/-- The inverse of the bundled `*`-automorphism is the action of the inverse covariance. -/ +@[simp] lemma localObservableCovarianceEquiv_symm_apply (z : N.localObservables) : + a.localObservableCovarianceEquiv.symm z = a⁻¹.localObservableCovariance z := rfl + +/-- A covariance of the net acts on the algebra of local observables by `*`-algebra automorphisms, + assembled as a group homomorphism into the `*`-automorphism group. -/ +noncomputable def localObservableCovarianceHom : + N.Covariance →* (N.localObservables ≃⋆ₐ[ℂ] N.localObservables) where + toFun a := a.localObservableCovarianceEquiv + map_one' := by + ext z + simp only [localObservableCovarianceEquiv_apply, localObservableCovariance_one, RingHom.id_apply, + StarAlgEquiv.one_apply] + map_mul' a b := by + ext z + simp only [localObservableCovarianceEquiv_apply, localObservableCovariance_mul, RingHom.comp_apply, + StarAlgEquiv.mul_apply] + +/-- The group homomorphism evaluates to the covariance action. -/ +@[simp] lemma localObservableCovarianceHom_apply (z : N.localObservables) : + localObservableCovarianceHom a z = a.localObservableCovariance z := rfl + +/-! #### The covariance automorphism of the quasi-local C⋆-algebra -/ + +section CStarCovariance + +variable [N.Faithful] + +/-- The covariance action is **isometric** on the algebra of local observables: each `β` is a + `*`-isomorphism of C⋆-algebras, hence norm-preserving. -/ +lemma localObservableCovariance_norm (z : N.localObservables) : + ‖a.localObservableCovariance z‖ = ‖z‖ := by + induction z using DirectLimit.induction with + | _ O X => + rw [localObservableCovariance_mk, norm_mk, norm_mk] + exact StarAlgEquiv.norm_map _ X + +/-- The covariance automorphism of the algebra of local observables is uniformly continuous (it is + an isometry), so it extends to the C⋆-completion. -/ +lemma localObservableCovarianceEquiv_uniformContinuous : + UniformContinuous a.localObservableCovarianceEquiv := + (AddMonoidHomClass.isometry_of_norm _ (fun z => by + rw [localObservableCovarianceEquiv_apply]; exact a.localObservableCovariance_norm z)).uniformContinuous + +/-- The inverse covariance automorphism is uniformly continuous as well (the action of `a⁻¹` is + also an isometry). -/ +lemma localObservableCovarianceEquiv_symm_uniformContinuous : + UniformContinuous a.localObservableCovarianceEquiv.symm := by + have h : ∀ z, ‖a.localObservableCovarianceEquiv.symm z‖ = ‖z‖ := fun z => by + rw [localObservableCovarianceEquiv_symm_apply]; exact a⁻¹.localObservableCovariance_norm z + exact (AddMonoidHomClass.isometry_of_norm _ h).uniformContinuous + +/-- The covariance automorphism of the quasi-local C⋆-algebra: the continuous extension of + `localObservableCovarianceEquiv` to the completion. -/ +noncomputable def quasiLocalCStarCovarianceEquiv : + N.quasiLocalCStarAlgebra ≃⋆ₐ[ℂ] N.quasiLocalCStarAlgebra := + UniformSpace.Completion.mapStarAlgEquiv a.localObservableCovarianceEquiv + a.localObservableCovarianceEquiv_uniformContinuous + a.localObservableCovarianceEquiv_symm_uniformContinuous + +/-- On local observables the extension to the completion agrees with the covariance action they + already carry. -/ +@[simp] lemma quasiLocalCStarCovarianceEquiv_coe (z : N.localObservables) : + a.quasiLocalCStarCovarianceEquiv (↑z : N.quasiLocalCStarAlgebra) = + ↑(a.localObservableCovariance z) := + UniformSpace.Completion.mapStarAlgEquiv_coe _ _ _ z + +/-- The covariance automorphism of the quasi-local C⋆-algebra is continuous, being the continuous + extension of an isometry to the completion. -/ +lemma quasiLocalCStarCovarianceEquiv_continuous : + Continuous (⇑a.quasiLocalCStarCovarianceEquiv) := + UniformSpace.Completion.continuous_map + +/-- A covariance of a faithful net acts on the quasi-local C⋆-algebra by `*`-automorphisms, + assembled as a group homomorphism into the `*`-automorphism group. -/ +noncomputable def quasiLocalCStarCovarianceHom : + N.Covariance →* (N.quasiLocalCStarAlgebra ≃⋆ₐ[ℂ] N.quasiLocalCStarAlgebra) where + toFun a := a.quasiLocalCStarCovarianceEquiv + map_one' := by + refine StarAlgEquiv.ext fun z => ?_ + rw [StarAlgEquiv.one_apply] + refine UniformSpace.Completion.induction_on z + (isClosed_eq (1 : N.Covariance).quasiLocalCStarCovarianceEquiv_continuous + continuous_id) ?_ + intro w + simp only [quasiLocalCStarCovarianceEquiv_coe, localObservableCovariance_one, RingHom.id_apply] + map_mul' a b := by + refine StarAlgEquiv.ext fun z => ?_ + rw [StarAlgEquiv.mul_apply] + refine UniformSpace.Completion.induction_on z + (isClosed_eq (a * b).quasiLocalCStarCovarianceEquiv_continuous + (a.quasiLocalCStarCovarianceEquiv_continuous.comp + b.quasiLocalCStarCovarianceEquiv_continuous)) ?_ + intro w + simp only [quasiLocalCStarCovarianceEquiv_coe, localObservableCovariance_mul, RingHom.comp_apply] + +/-- The group homomorphism evaluates to the covariance automorphism of the quasi-local + C⋆-algebra. -/ +@[simp] lemma quasiLocalCStarCovarianceHom_apply + (z : N.quasiLocalCStarAlgebra) : + quasiLocalCStarCovarianceHom a z = a.quasiLocalCStarCovarianceEquiv z := rfl + +end CStarCovariance + +end Covariance + +end LocalNet diff --git a/QuantumSystem/Algebra/LocalNet/SplitProperty.lean b/QuantumSystem/Algebra/LocalNet/SplitProperty.lean new file mode 100644 index 0000000..8164c14 --- /dev/null +++ b/QuantumSystem/Algebra/LocalNet/SplitProperty.lean @@ -0,0 +1,522 @@ +module + +public import QuantumSystem.Algebra.LocalNet.QuasiLocalAlgebra +public import QuantumSystem.Algebra.CStarAlgebra.Representation +public import QuantumSystem.Algebra.VonNeumannAlgebra.SplitInclusion + +/-! +# The split property of a local net + +The **split property** of a local net, in the form of Buchholz (*Product states for local +algebras*, Comm. Math. Phys. 36, 1974) and Doplicher–Longo (*Standard and split inclusions of +von Neumann algebras*, Invent. Math. 75, 1984): fix a representation `R` of the quasi-local +C⋆-algebra on a Hilbert space, with representing map `R.π`, and form the **local von Neumann +algebras** + + `𝓡(O) = R.π(𝔄(O))″` (`LocalNet.localVonNeumannAlgebra`); + +the net has the split property when, for every properly contained pair of regions `O₁ ⋐ O₂`, the +inclusion `𝓡(O₁) ≤ 𝓡(O₂)` is a split inclusion — some type I factor interpolates +(`VonNeumannAlgebra.IsSplitInclusion`). + +Proper containment `⋐` (`ProperContainment`) is the abstract form of the geometric relation +"the closure of `O₁` lies in the interior of `O₂`" of the QFT literature; on a causal index set +it is model-dependent geometric input, like the causal orthogonality `⟂` itself. Its axioms — +containment, monotonicity, and a nondegenerate causal collar inside the outer region — are +necessary conditions rather than a complete axiomatisation of properness, so the split property is +always relative to the chosen `⋐`. They do force `⋐` to be strict (`ProperlyContained.lt`), which +rules out plain containment `⊆`: a reflexive `⋐` would turn the split property into the demand +that every local algebra be a type I factor. Strict containment `⊂` does satisfy all three axioms on +lattice regions — `Λ₂ \ Λ₁` is a collar for it (`ProperContainment.ofSSubset`) — so, unlike +`CausalOrthogonality (Finset α)`, the absence of a canonical `ProperContainment` instance there is a +choice, not an impossibility: `⊂` admits *touching* pairs such as `{0} ⋐ {0, 1}`, which the +literature's closure separation excludes. Separation is what needs a metric or graph structure on +the sites, and it enters through the thickening operator of `ProperContainment.ofThicken`. + +The property is defined at the level of a **net of von Neumann algebras** (`VonNeumannNet`): an +isotone, local assignment `O ↦ 𝓡(O)` over a bare causal index set, with no directedness, no +quasi-local C⋆-algebra and no faithfulness. That is the level at which the index set of the split +property's own literature becomes expressible: the proper intervals of `S¹` are *not* directed +(two intervals covering the circle have no proper upper bound), as Köster's dissertation itself +notes at its definition of a chiral net. Wedges and spacelike cones are further standard +non-directed examples from the wider AQFT literature (not from the sources behind the extraction +note `docs/math/split-inclusion.md`). + +The representation-theoretic net above is one instance of that (`LocalNet.vonNeumannNet`), and +`LocalNet.SplitProperty` is `VonNeumannNet.SplitProperty` at it. The hypotheses +`[IsDirectedOrder K]`, `[Nonempty K]` and `[N.Faithful]` belong to *that instance*, not to the +property: the quasi-local algebra's direct limit needs a directed index set with a base point, and +its C⋆-norm needs injective isotony embeddings. The representation +is not assumed nondegenerate: `𝓡(O)` contains `1` regardless, since a von Neumann algebra is +unital, so a degenerate representation on a nonzero space can satisfy the property trivially +(`𝓡(O) = ℂ1` is a type I factor). On the *zero* space it cannot: a type I factor needs a nonzero +minimal projection, so `IsSplitInclusion` is then identically false. Substantive downstream +theorems impose nondegeneracy or cyclicity where they need it. + +## Main definitions and results + +* `ProperContainment` — the proper-containment relation `O₁ ⋐ O₂` on a causal index set, with + `ProperlyContained.le` / `.mono` / `.exists_orthogonal`, its strictness + (`ProperContainment.irrefl`, `ProperlyContained.lt`), the lattice model + `ProperContainment.ofThicken` built from a strictly enlarging thickening operator, the degenerate + lattice model `ProperContainment.ofSSubset` the axioms cannot exclude, and + `ProperContainment.exists_orthogonal_iff_not_subset`, which measures how much the collar axiom + says on a lattice index set (namely: only strictness). +* `LocalNet.localVonNeumannAlgebra` — the local von Neumann algebra `𝓡(O) = R.π(𝔄(O))″` of a + region in a representation of the quasi-local algebra, containing the represented local + observables (`π_ιLocalCStar_mem_localVonNeumannAlgebra`), with isotony + (`localVonNeumannAlgebra_mono`) and locality + (`localVonNeumannAlgebra_le_commutant_of_orthogonal`). +* `VonNeumannNet` — an isotone, local net of von Neumann algebras over a causal index set, and + `VonNeumannNet.SplitProperty` — **the split property**, stated there. Its consequences: + `SplitProperty.isSplitInclusion` (the split inclusion of a properly contained pair; its tensor + splitting follows by applying `VonNeumannAlgebra.IsSplitInclusion.exists_tensor_decomposition` + to it), `SplitProperty.isSplitInclusion_of_le_of_properlyContained_of_le` (stability under + enlarging the pair), and `SplitProperty.isSplitInclusion_commutant` (the commutant form, + Borchers/Buchholz, `𝓡(O₁) ≤ 𝔑 ≤ 𝓡(O_B)′`, obtained from the nested form via locality). + Its degenerate side is `VonNeumannNet.splitProperty_of_complex`: on a one-dimensional Hilbert + space *every* net has the property, so no one-dimensional model is evidence about anything else. +* `LocalNet.vonNeumannNet` — the net of local von Neumann algebras of a representation, as a + `VonNeumannNet`, and `LocalNet.SplitProperty` — the split property at that instance, with the + three consequences above specialised to it. + +## Notation + +`𝔄(O)` and `𝓡(O)` in the prose above are documentation shorthand for the local C⋆-algebra +`N.algebra O` and the local von Neumann algebra `N.localVonNeumannAlgebra R O`; the convention — +and why neither is a Lean notation — is stated in full in `QuantumSystem.Algebra.LocalNet.Net`. + +`⊗̄` is documentation shorthand for the von Neumann (spatial) tensor product of algebras; that +convention is stated in full in `QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`, where the +algebras it names (`HilbertTensor.vnTensorLeft` / `vnTensorRight`) are defined. +-/ + +@[expose] public section + +open scoped CausalOrthogonality + +/-- **Proper containment of regions**: the abstract form of the relation `O₁ ⋐ O₂` ("the closure + of `O₁` lies in the interior of `O₂`") under which the split property of a local net is + stated. This is model-dependent geometric input on the causal index set, like `⟂` itself. + + The axioms are containment, monotonicity under shrinking the inner and enlarging the outer + region, and a **causal collar** (`exists_orthogonal_of_properlyContained`): the outer region + contains a *nondegenerate* region causally orthogonal to the inner one. Nondegeneracy is + expressed as `¬ O₃ ≤ O₁`, the only sense a bare order affords — and it is what the + literature's separation condition actually delivers. Köster's chiral index set defines its own + `I ⋐ S¹` by "its causal complement `I' := S¹ ∖ Ī` is not the empty set", and `Ī₁ ⊂ I₂` with + `I₂` open forces a nonempty component of `I₂ ∖ Ī₁` that is a proper interval inside `I₂`, + disjoint from `I₁` and not contained in it. + + The collar makes `⋐` **irreflexive** (`ProperContainment.irrefl`), indeed strict + (`ProperlyContained.lt`), which is what the literature's typography `Ī₁ ⊂ I₂` is for: without + it plain containment `⊆` would satisfy the axioms, and a reflexive `⋐` would make the split + property demand that every local algebra *be* a type I factor + (`VonNeumannAlgebra.IsSplitInclusion.isTypeIFactor_of_self`) — the opposite of the type III₁ + structure expected of local algebras. The axioms remain necessary conditions rather than a + complete axiomatisation, so the split property is still relative to the chosen `⋐`; what they + now exclude is the degenerate choices. In particular they are *not* enough to exclude + **touching** regions: on a lattice index set the collar clause is equivalent to plain + `¬ Λ₂ ⊆ Λ₁` (`exists_orthogonal_iff_not_subset`), so a model that wants the literature's + separation must build it in — which is what `ofThicken`'s strictly enlarging thickening does. + Nothing forces `⋐` to be inhabited either: on a finite index set with no room for a collar it + is empty and the split property holds vacuously, which is correct — a downstream theorem + needing a genuine pair must say so itself. -/ +class ProperContainment (K : Type*) [Preorder K] [CausalOrthogonality K] where + /-- The proper-containment relation `O₁ ⋐ O₂` on regions. -/ + ProperlyContained : K → K → Prop + /-- Proper containment implies containment. -/ + le_of_properlyContained : ∀ ⦃O₁ O₂ : K⦄, ProperlyContained O₁ O₂ → O₁ ≤ O₂ + /-- Proper containment survives shrinking the inner region and enlarging the outer one. -/ + properlyContained_mono : ∀ ⦃O₀ O₁ O₂ O₃ : K⦄, O₀ ≤ O₁ → ProperlyContained O₁ O₂ → + O₂ ≤ O₃ → ProperlyContained O₀ O₃ + /-- **Causal collar**: a properly containing region contains a region causally orthogonal to + the inner one and not contained in it. The last clause is the nondegeneracy that makes the + collar a genuine buffer rather than a region orthogonal to everything (such as the empty + lattice region); it is what forces `⋐` to be irreflexive. -/ + exists_orthogonal_of_properlyContained : ∀ ⦃O₁ O₂ : K⦄, ProperlyContained O₁ O₂ → + ∃ O₃, O₃ ≤ O₂ ∧ O₁ ⟂ O₃ ∧ ¬ O₃ ≤ O₁ + +namespace ProperContainment + +/-- `O₁ ⋐ O₂` : the region `O₁` is properly contained in `O₂`. + + The glyph is topology's compact-containment symbol, used here for the *binary relation* the + QFT literature writes `Ī₁ ⊂ I₂`. Köster's dissertation uses the same glyph differently: there + `I ⋐ S¹` is a membership predicate ("`I` is a proper interval of `S¹`"), not a relation + between two regions of the index set — a reader coming from that source should not carry the + predicate reading into this notation (extraction note `docs/math/causal-index-set.md`, + convention (C7)). -/ +scoped infixl:50 " ⋐ " => ProperContainment.ProperlyContained + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] [ProperContainment K] + +/-- Proper containment implies containment (dot-notation form). -/ +theorem ProperlyContained.le {O₁ O₂ : K} (h : O₁ ⋐ O₂) : O₁ ≤ O₂ := + le_of_properlyContained h + +/-- Proper containment survives shrinking the inner region and enlarging the outer one + (dot-notation form). -/ +theorem ProperlyContained.mono {O₀ O₁ O₂ O₃ : K} (h₀ : O₀ ≤ O₁) (h : O₁ ⋐ O₂) + (h₃ : O₂ ≤ O₃) : O₀ ⋐ O₃ := + properlyContained_mono h₀ h h₃ + +/-- The causal collar of a proper containment (dot-notation form). -/ +theorem ProperlyContained.exists_orthogonal {O₁ O₂ : K} (h : O₁ ⋐ O₂) : + ∃ O₃, O₃ ≤ O₂ ∧ O₁ ⟂ O₃ ∧ ¬ O₃ ≤ O₁ := + exists_orthogonal_of_properlyContained h + +/-- **Proper containment is irreflexive**: no region is properly contained in itself. This is the + content of the collar's nondegeneracy clause — a collar inside `O` that is not contained in + `O` cannot exist — and it is what the literature's `Ī₁ ⊂ I₂` typography encodes. -/ +theorem irrefl (O : K) : ¬ (O ⋐ O) := by + rintro h + obtain ⟨_, hle, -, hnle⟩ := h.exists_orthogonal + exact hnle hle + +/-- A properly contained region is not above its container. -/ +theorem ProperlyContained.not_ge {O₁ O₂ : K} (h : O₁ ⋐ O₂) : ¬ O₂ ≤ O₁ := + fun hge => irrefl O₁ (h.mono le_rfl hge) + +/-- **Proper containment is strict**: `O₁ ⋐ O₂` implies `O₁ < O₂`. -/ +theorem ProperlyContained.lt {O₁ O₂ : K} (h : O₁ ⋐ O₂) : O₁ < O₂ := + lt_of_le_not_ge h.le h.not_ge + +/-- A thickening operator that strictly enlarges every nonempty region is enlarging on every + region: on the empty region `∅ ⊆ thicken ∅` is automatic. -/ +lemma subset_thicken_of_ssubset {α : Type*} {thicken : Finset α → Finset α} + (ssubset_thicken : ∀ Λ : Finset α, Λ.Nonempty → Λ ⊂ thicken Λ) (Λ : Finset α) : + Λ ⊆ thicken Λ := by + rcases Λ.eq_empty_or_nonempty with rfl | hne + · exact Finset.empty_subset _ + · exact (ssubset_thicken Λ hne).subset + +/-- **On lattice regions the collar clause says only that `Λ₂` is not contained in `Λ₁`.** With + `⟂ = Disjoint` on `Finset α` the region `Λ₂ \ Λ₁` is automatically orthogonal to `Λ₁`, so it + witnesses the collar as soon as it is nonempty; conversely a collar inside `Λ₂` that is not + inside `Λ₁` forbids `Λ₂ ⊆ Λ₁`. + + This is worth stating because it bounds what the `ProperContainment` axioms can be asked to + do. On a lattice index set they pin down strictness and nothing more: they do **not** by + themselves exclude *touching* pairs such as `{0} ⋐ {0, 1}`, which the literature does exclude + (a pair of local algebras of touching regions is not statistically independent, hence not + split — Buchholz's *Product states for local algebras* records that postulating normal product + states for touching regions already yields contradictions for the free field, the corpus's one + statement that the closure separation is *necessary*). + Genuine separation is therefore the model's job, not the class's — see `ofThicken`, + whose thickening operator supplies a buffer layer between `Λ₁` and the collar. -/ +lemma exists_orthogonal_iff_not_subset {α : Type*} {Λ₁ Λ₂ : Finset α} : + (∃ Λ₃ : Finset α, Λ₃ ≤ Λ₂ ∧ Λ₁ ⟂ Λ₃ ∧ ¬ Λ₃ ≤ Λ₁) ↔ ¬ Λ₂ ⊆ Λ₁ := by + classical + constructor + · rintro ⟨Λ₃, hle, -, hnle⟩ hsub + exact hnle (hle.trans hsub) + · intro hns + refine ⟨Λ₂ \ Λ₁, Finset.sdiff_subset, Finset.disjoint_sdiff, ?_⟩ + obtain ⟨x, hx₂, hx₁⟩ := Finset.not_subset.1 hns + exact fun hsub => hx₁ (hsub (Finset.mem_sdiff.2 ⟨hx₂, hx₁⟩)) + +/-- **Strict containment already satisfies the axioms**, on any lattice index set and with no + structure on the sites: containment and monotonicity are immediate, and `Λ₂ \ Λ₁` is a collar + (`exists_orthogonal_iff_not_subset`). So nothing in the class *prevents* a canonical lattice + instance — what it fails to deliver is separation, and this model is the witness of that + failure: it admits the *touching* pair `{0} ⋐ {0, 1}`, whose local algebras the literature + records as not statistically independent, hence not split. + + Deliberately a `def` rather than an `instance`, and never activated anywhere: it exists to + exhibit the gap, not to be used. A model that wants separation supplies a thickening operator + (`ofThicken`). -/ +@[reducible] def ofSSubset (α : Type*) : ProperContainment (Finset α) where + ProperlyContained Λ₁ Λ₂ := Λ₁ ⊂ Λ₂ + le_of_properlyContained _ _ h := le_of_lt h + properlyContained_mono _ _ _ _ h₀ h h₃ := (h₀.trans_lt h).trans_le h₃ + exists_orthogonal_of_properlyContained _ _ h := + exists_orthogonal_iff_not_subset.2 fun hsub => absurd (h.subset.antisymm hsub) (ne_of_lt h) + +/-- Proper containment of lattice regions from a **thickening operator** (e.g. the + `r`-neighbourhood for a metric or graph structure on the sites): `Λ₁ ⋐ Λ₂` iff the thickening + of `Λ₁` is a *strict* subset of `Λ₂`. + + The thickening is required to be *strictly* enlarging on nonempty regions + (`ssubset_thicken`). What that buys is the chain `Λ₁ ⊊ thicken Λ₁ ⊆ Λ₂` under `Λ₁ ⋐ Λ₂`, with + the collar `Λ₂ \ thicken Λ₁` disjoint from the whole of `thicken Λ₁` rather than merely from + `Λ₁`: the collar is separated from `Λ₁` *by the thickening's own notion of nearness*. + + Whether that is geometric separation is the thickening's business and is **not** a consequence + of the two hypotheses. The one-sided `thicken Λ = Λ ∪ Λ.image (· + 1)` is monotone and strictly + enlarging, yet `{0} ⋐ {-1, 0, 1, 2}` has collar `{-1, 2}`, which touches `{0}` from below. A + thickening that is a genuine neighbourhood operator — enlarging in *every* direction of + adjacency, as the 1-neighbourhood `ProperContainment.nbhd` of the integer chain is in + `QuantumSystem.Algebra.LocalNet.Examples` — does exclude *touching* pairs, which is the + exclusion the literature's `Ī₁ ⊂ I₂` typography performs and one the class axioms alone cannot + perform on a lattice (`exists_orthogonal_iff_not_subset`). What strictness rules out by itself + is `thicken := id`, i.e. the degenerate model `ofSSubset`, in which `{0} ⋐ {0, 1}` is admitted. + + On the empty region no strictness is asked, since a neighbourhood operator has + `thicken ∅ = ∅`; `∅ ⊆ thicken ∅` holds regardless and is all the axioms need there. -/ +@[reducible] def ofThicken {α : Type*} [DecidableEq α] (thicken : Finset α → Finset α) + (ssubset_thicken : ∀ Λ : Finset α, Λ.Nonempty → Λ ⊂ thicken Λ) + (thicken_mono : Monotone thicken) : + ProperContainment (Finset α) where + ProperlyContained Λ₁ Λ₂ := thicken Λ₁ ⊂ Λ₂ + le_of_properlyContained _ _ h := (subset_thicken_of_ssubset ssubset_thicken _).trans h.subset + properlyContained_mono _ _ _ _ h₀ h h₃ := ((thicken_mono h₀).trans_lt h).trans_le h₃ + exists_orthogonal_of_properlyContained Λ₁ Λ₂ h := by + refine ⟨Λ₂ \ thicken Λ₁, Finset.sdiff_subset, + Finset.disjoint_sdiff.mono_left (subset_thicken_of_ssubset ssubset_thicken Λ₁), ?_⟩ + obtain ⟨x, hx₂, hx₁⟩ := Finset.exists_of_ssubset h + exact fun hsub => + hx₁ (subset_thicken_of_ssubset ssubset_thicken Λ₁ (hsub (Finset.mem_sdiff.2 ⟨hx₂, hx₁⟩))) + +end ProperContainment + +/-! ### Nets of von Neumann algebras + +The split property is a statement about a net of *von Neumann* algebras, and it is stated here at +that level. A `VonNeumannNet` is an isotone, local assignment `O ↦ 𝓡(O)` of von Neumann algebras +on a fixed Hilbert space over a bare causal index set: no directedness, no quasi-local C⋆-algebra, +no faithfulness — none of which the property mentions. Non-directed index sets are therefore in +scope — as they must be: the proper intervals of `S¹`, the index set of the chiral split property, +are not directed (two intervals covering the circle have no *proper* upper bound), a point Köster's +dissertation makes explicitly. Wedges and spacelike cones are further standard non-directed +examples from the wider AQFT literature. + +The net of local von Neumann algebras of a representation of the quasi-local C⋆-algebra is one +instance (`LocalNet.vonNeumannNet`), and `LocalNet.SplitProperty` is this property at that +instance. +-/ + +open scoped ProperContainment VonNeumannAlgebra + +/-- A **net of von Neumann algebras** over a causal index set `K`, acting on a fixed Hilbert space + `H`: the assignment `O ↦ 𝓡(O)` together with **isotony** (`algebra_mono`) and **locality** + (`algebra_le_commutant_of_orthogonal`, Einstein causality `𝓡(O₁) ≤ 𝓡(O₂)′` for `O₁ ⟂ O₂`). + + This is the level at which the split property is stated in the literature — Buchholz's local + rings `𝓡(O)`, Doplicher–Longo's `W*`-inclusions, Köster's chiral nets assigning a von Neumann + algebra to each proper interval. The index set carries only a `Preorder` and `⟂`; in + particular it is not assumed directed, so the index sets those sources use are expressible. -/ +structure VonNeumannNet (K : Type*) [Preorder K] [CausalOrthogonality K] + (H : Type*) [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] where + /-- The local von Neumann algebra `𝓡(O)` assigned to a region. -/ + algebra : K → VonNeumannAlgebra H + /-- **Isotony**: a larger region carries a larger algebra. -/ + algebra_mono : Monotone algebra + /-- **Locality** (microcausality): causally orthogonal regions carry commuting algebras. -/ + algebra_le_commutant_of_orthogonal : ∀ ⦃O₁ O₂ : K⦄, O₁ ⟂ O₂ → algebra O₁ ≤ (algebra O₂)′ + +namespace VonNeumannNet + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-- **The split property** of a net of von Neumann algebras (Buchholz; Doplicher–Longo): for every + properly contained pair of regions `O₁ ⋐ O₂`, some type I factor interpolates between the + local algebras, `𝓡(O₁) ≤ 𝔑 ≤ 𝓡(O₂)`. This is genuinely model-dependent input — it fails for + nets without the requisite phase-space (nuclearity) behaviour — and it is relative to the + chosen proper containment `⋐` (see `ProperContainment`). + + This is the *nested* form. The historically original *commutant* form is + `SplitProperty.isSplitInclusion_commutant`, which follows from this one through locality; the + converse needs Haag duality and is not available here. -/ +def SplitProperty [ProperContainment K] (vnNet : VonNeumannNet K H) : Prop := + ∀ ⦃O₁ O₂ : K⦄, O₁ ⋐ O₂ → + VonNeumannAlgebra.IsSplitInclusion (vnNet.algebra O₁) (vnNet.algebra O₂) + +/-- **Every net of von Neumann algebras on `ℂ` has the split property** — whatever the net, + whatever the proper containment. On the one-dimensional Hilbert space the only von Neumann + algebra is `𝓑(ℂ)` (`VonNeumannAlgebra.eq_boundedLinearOperators_complex`), a type I factor, so + every inclusion of the net is the split inclusion `𝓑(ℂ) ≤ 𝓑(ℂ)`. + + Stated here, rather than left implicit inside a witness, because it fixes what a + one-dimensional model can be evidence *for*: it inhabits `SplitProperty` and distinguishes + nothing — no net, no representation, no choice of `⋐`. The degeneracy is `dim H = 1` and + nothing else; it is the escape clause Halvorson–Müger's type III₁ proposition carries + explicitly ("either `𝓡 = ℂ1` or `𝓡` is a type III₁ factor"). -/ +theorem splitProperty_of_complex [ProperContainment K] (vnNet : VonNeumannNet K ℂ) : + vnNet.SplitProperty := by + intro O₁ O₂ _ + rw [VonNeumannAlgebra.eq_boundedLinearOperators_complex (vnNet.algebra O₁), + VonNeumannAlgebra.eq_boundedLinearOperators_complex (vnNet.algebra O₂)] + exact (VonNeumannAlgebra.isTypeIFactor_boundedLinearOperators (H := ℂ)).isSplitInclusion_self + +variable [ProperContainment K] {vnNet : VonNeumannNet K H} + +/-- **The split inclusion of a properly contained pair**: under the split property, `O₁ ⋐ O₂` + yields the split inclusion `𝓡(O₁) ≤ 𝓡(O₂)`. Buchholz's tensor splitting — a unitary + `U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)` carrying `𝓡(O₁)` into the tensor factor `𝓑(ℓ²(F)) ⊗̄ 1` and the + commutant `𝓡(O₂)′` into `1 ⊗̄ 𝓑(eH)`, with the interpolating type I factor and its commutant + identified exactly — is obtained by applying + `VonNeumannAlgebra.IsSplitInclusion.exists_tensor_decomposition` to the conclusion. -/ +theorem SplitProperty.isSplitInclusion (hs : vnNet.SplitProperty) {O₁ O₂ : K} (h : O₁ ⋐ O₂) : + VonNeumannAlgebra.IsSplitInclusion (vnNet.algebra O₁) (vnNet.algebra O₂) := + hs h + +/-- The split property extends to enlarged pairs: if `O₀ ≤ O₁ ⋐ O₂ ≤ O₃` then the inclusion + `𝓡(O₀) ≤ 𝓡(O₃)` is split as well. -/ +lemma SplitProperty.isSplitInclusion_of_le_of_properlyContained_of_le (hs : vnNet.SplitProperty) + {O₀ O₁ O₂ O₃ : K} (h₀ : O₀ ≤ O₁) (h : O₁ ⋐ O₂) (h₃ : O₂ ≤ O₃) : + VonNeumannAlgebra.IsSplitInclusion (vnNet.algebra O₀) (vnNet.algebra O₃) := + hs (h.mono h₀ h₃) + +/-- **The commutant form of the split property** (Borchers' conjecture as displayed in Buchholz, + *Product states for local algebras*): for a properly contained pair `O₁ ⋐ O₂` and any region + `O_B` causally orthogonal to `O₂`, a type I factor interpolates between `𝓡(O₁)` and the + commutant `𝓡(O_B)′`, + + `𝓡(O₁) ≤ 𝔑 ≤ 𝓡(O_B)′`. + + This is the historically original shape of the property, for a *disjoint* pair with slack + rather than a nested one. The nested form implies it through locality + (`algebra_le_commutant_of_orthogonal`); the converse needs Haag duality, which is not assumed + anywhere here. Buchholz displays the symmetric four-term chain `𝓡(O₁) ⊂ M₁ ⊂ M₂′ ⊂ 𝓡(O₂)′` + with two interpolating type I factors; this is its one-sided compression, which is the form + the adopted definition of a split inclusion carries. -/ +theorem SplitProperty.isSplitInclusion_commutant (hs : vnNet.SplitProperty) {O₁ O₂ O_B : K} + (h : O₁ ⋐ O₂) (hd : O₂ ⟂ O_B) : + VonNeumannAlgebra.IsSplitInclusion (vnNet.algebra O₁) (vnNet.algebra O_B)′ := + (hs h).mono le_rfl (vnNet.algebra_le_commutant_of_orthogonal hd) + +end VonNeumannNet + +namespace LocalNet + +open scoped ProperContainment VonNeumannAlgebra + +section LocalAlgebras + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] [IsDirectedOrder K] [Nonempty K] +variable (N : LocalNet K) [N.Faithful] + +/-- The **local von Neumann algebra** `𝓡(O) = R.π(𝔄(O))″` of a region in a representation of the + quasi-local C⋆-algebra: the von Neumann algebra generated by the image of the local algebra. + The generating set is star-closed (`star_range_π_ιLocalCStar`), so + `VonNeumannAlgebra.generated` — which symmetrizes in general — is literally the double + commutant here (`coe_localVonNeumannAlgebra`). Since `R.π` is a possibly non-unital + `*`-homomorphism, `𝓡(O)` always contains `1` even when `R.π(𝔄(O))` does not, matching the `″` + convention. -/ +noncomputable def localVonNeumannAlgebra (R : CStarRep N.quasiLocalCStarAlgebra) (O : K) : + VonNeumannAlgebra R.H := + VonNeumannAlgebra.generated (Set.range fun a : N.algebra O => R.π (N.ιLocalCStar O a)) + +/-- Local observables are represented inside the local von Neumann algebra of their region. -/ +lemma π_ιLocalCStar_mem_localVonNeumannAlgebra (R : CStarRep N.quasiLocalCStarAlgebra) (O : K) + (a : N.algebra O) : R.π (N.ιLocalCStar O a) ∈ N.localVonNeumannAlgebra R O := + VonNeumannAlgebra.mem_generated_of_mem ⟨a, rfl⟩ + +/-- The generating set of `𝓡(O)` is star-closed: `𝔄(O)` is star-closed, and both the embedding + into the quasi-local algebra and `R.π` are `*`-maps, so `Set.star_range` applies to the + composite. -/ +lemma star_range_π_ιLocalCStar (R : CStarRep N.quasiLocalCStarAlgebra) (O : K) : + star (Set.range fun a : N.algebra O => R.π (N.ιLocalCStar O a)) + = Set.range fun a : N.algebra O => R.π (N.ιLocalCStar O a) := + Set.star_range fun a => by rw [N.ιLocalCStar_star, map_star] + +/-- **`𝓡(O)` is literally the double commutant `R.π(𝔄(O))″`.** `VonNeumannAlgebra.generated` + symmetrizes its generating set in general; here the set is already star-closed + (`star_range_π_ιLocalCStar`), so no symmetrization happens and the definition agrees with the + `″` of the operator-algebra literature. -/ +lemma coe_localVonNeumannAlgebra (R : CStarRep N.quasiLocalCStarAlgebra) (O : K) : + (N.localVonNeumannAlgebra R O : Set (R.H →L[ℂ] R.H)) + = (Set.range fun a : N.algebra O => R.π (N.ιLocalCStar O a)).centralizer.centralizer := + VonNeumannAlgebra.coe_generated_of_star_eq (N.star_range_π_ιLocalCStar R O) + +/-- **Isotony** of the local von Neumann algebras: `O ≤ O'` gives `𝓡(O) ≤ 𝓡(O')`. -/ +lemma localVonNeumannAlgebra_mono (R : CStarRep N.quasiLocalCStarAlgebra) {O O' : K} + (h : O ≤ O') : N.localVonNeumannAlgebra R O ≤ N.localVonNeumannAlgebra R O' := by + refine VonNeumannAlgebra.generated_mono ?_ + rintro _ ⟨a, rfl⟩ + exact ⟨N.incl h a, by simp⟩ + +/-- **Locality** of the local von Neumann algebras: causally orthogonal regions have commuting + algebras, `𝓡(O₁) ≤ 𝓡(O₂)′`. Einstein causality survives the double commutant. -/ +lemma localVonNeumannAlgebra_le_commutant_of_orthogonal (R : CStarRep N.quasiLocalCStarAlgebra) + {O₁ O₂ : K} (hd : O₁ ⟂ O₂) : + N.localVonNeumannAlgebra R O₁ ≤ (N.localVonNeumannAlgebra R O₂)′ := by + have key : VonNeumannAlgebra.generated + (Set.range fun a : N.algebra O₁ => R.π (N.ιLocalCStar O₁ a)) + ≤ VonNeumannAlgebra.commutantSet + (Set.range fun b : N.algebra O₂ => R.π (N.ιLocalCStar O₂ b)) := by + refine VonNeumannAlgebra.generated_le ?_ + rintro _ ⟨a, rfl⟩ + rw [SetLike.mem_coe, VonNeumannAlgebra.mem_commutantSet_iff] + rintro _ ⟨b, rfl⟩ + refine ⟨(N.ιLocalCStar_commute_of_orthogonal hd.symm b a).map R.π, ?_⟩ + have hb : star (R.π (N.ιLocalCStar O₂ b)) = R.π (N.ιLocalCStar O₂ (star b)) := by + rw [← map_star, N.ιLocalCStar_star] + rw [hb] + exact (N.ιLocalCStar_commute_of_orthogonal hd.symm (star b) a).map R.π + exact key.trans_eq (VonNeumannAlgebra.commutant_generated _).symm + +/-- **The net of local von Neumann algebras** of a representation, as a `VonNeumannNet`: isotony + is `localVonNeumannAlgebra_mono` and locality is + `localVonNeumannAlgebra_le_commutant_of_orthogonal`, both already proved. This is the bridge + from the C⋆-level net data to the von-Neumann-level object the split property is about. -/ +noncomputable def vonNeumannNet (R : CStarRep N.quasiLocalCStarAlgebra) : + VonNeumannNet K R.H where + algebra := N.localVonNeumannAlgebra R + algebra_mono _ _ h := N.localVonNeumannAlgebra_mono R h + algebra_le_commutant_of_orthogonal _ _ hd := + N.localVonNeumannAlgebra_le_commutant_of_orthogonal R hd + +/-- The algebras of the net `vonNeumannNet R` are the local von Neumann algebras themselves. -/ +@[simp] lemma vonNeumannNet_algebra (R : CStarRep N.quasiLocalCStarAlgebra) (O : K) : + (N.vonNeumannNet R).algebra O = N.localVonNeumannAlgebra R O := rfl + +/-- **The split property** of a local net in a representation `π` of the quasi-local C⋆-algebra + (Buchholz; Doplicher–Longo): the split property of the associated net of local von Neumann + algebras (`VonNeumannNet.SplitProperty`) — for every properly contained pair of regions + `O₁ ⋐ O₂`, some type I factor interpolates, `𝓡(O₁) ≤ M ≤ 𝓡(O₂)`. + + The property itself is defined at the von-Neumann-net level, where it needs neither a + directed index set nor a quasi-local C⋆-algebra; the hypotheses `[IsDirectedOrder K]`, + `[Nonempty K]` and `[N.Faithful]` here are the cost of *this instance*, whose local algebras + are built from a representation of the quasi-local C⋆-algebra. Nets over non-directed index + sets are stated through `VonNeumannNet.SplitProperty` directly. -/ +def SplitProperty [ProperContainment K] (R : CStarRep N.quasiLocalCStarAlgebra) : Prop := + (N.vonNeumannNet R).SplitProperty + +end LocalAlgebras + +section SplitConsequences + +variable {K : Type*} [Preorder K] [CausalOrthogonality K] [IsDirectedOrder K] [Nonempty K] +variable [ProperContainment K] {N : LocalNet K} [N.Faithful] +variable {R : CStarRep N.quasiLocalCStarAlgebra} + +/-- The split property extends to enlarged pairs: if `O₀ ≤ O₁ ⋐ O₂ ≤ O₃` then the inclusion + `𝓡(O₀) ≤ 𝓡(O₃)` is split as well. Specialisation of + `VonNeumannNet.SplitProperty.isSplitInclusion_of_le_of_properlyContained_of_le`. -/ +lemma SplitProperty.isSplitInclusion_of_le_of_properlyContained_of_le (hs : N.SplitProperty R) + {O₀ O₁ O₂ O₃ : K} (h₀ : O₀ ≤ O₁) (h : O₁ ⋐ O₂) (h₃ : O₂ ≤ O₃) : + VonNeumannAlgebra.IsSplitInclusion + (N.localVonNeumannAlgebra R O₀) (N.localVonNeumannAlgebra R O₃) := + VonNeumannNet.SplitProperty.isSplitInclusion_of_le_of_properlyContained_of_le hs h₀ h h₃ + +/-- **The split inclusion of a properly contained pair**: under the split property, `O₁ ⋐ O₂` + yields the split inclusion `𝓡(O₁) ≤ 𝓡(O₂)`. Specialisation of + `VonNeumannNet.SplitProperty.isSplitInclusion`, whose docstring describes the tensor splitting + that follows. (The instantiated existential is deliberately not restated here: spelling it out + over `R.H` forces the elaborator to unfold the quasi-local algebra inside `R`'s type, while + consuming the general statement through this corollary is cheap.) -/ +theorem SplitProperty.isSplitInclusion (hs : N.SplitProperty R) {O₁ O₂ : K} (h : O₁ ⋐ O₂) : + VonNeumannAlgebra.IsSplitInclusion + (N.localVonNeumannAlgebra R O₁) (N.localVonNeumannAlgebra R O₂) := + VonNeumannNet.SplitProperty.isSplitInclusion hs h + +/-- **The commutant form of the split property** (Borchers' conjecture as displayed in Buchholz, + *Product states for local algebras*): for a properly contained pair `O₁ ⋐ O₂` and any region + `O_B` causally orthogonal to `O₂`, a type I factor interpolates between `𝓡(O₁)` and the + commutant `𝓡(O_B)′`. Specialisation of + `VonNeumannNet.SplitProperty.isSplitInclusion_commutant`, whose docstring records what the + converse would need (Haag duality) and how this one-sided form relates to Buchholz's + symmetric four-term chain. -/ +theorem SplitProperty.isSplitInclusion_commutant (hs : N.SplitProperty R) {O₁ O₂ O_B : K} + (h : O₁ ⋐ O₂) (hd : O₂ ⟂ O_B) : + VonNeumannAlgebra.IsSplitInclusion + (N.localVonNeumannAlgebra R O₁) (N.localVonNeumannAlgebra R O_B)′ := + VonNeumannNet.SplitProperty.isSplitInclusion_commutant hs h hd + +end SplitConsequences + +end LocalNet diff --git a/QuantumSystem/Algebra/Star/DoubleCommutant/SOTClosedSubAlgebra.lean b/QuantumSystem/Algebra/Star/DoubleCommutant/SOTClosedSubAlgebra.lean index 6e8a99e..8e3c171 100644 --- a/QuantumSystem/Algebra/Star/DoubleCommutant/SOTClosedSubAlgebra.lean +++ b/QuantumSystem/Algebra/Star/DoubleCommutant/SOTClosedSubAlgebra.lean @@ -8,13 +8,24 @@ public import QuantumSystem.ForMathlib.Analysis.LocallyConvex.StrongOperatorTopo This file proves the SOT version of the double commutant theorem by reducing to the WOT version. +Throughout, `A` is a possibly non-unital `*`-subalgebra (`NonUnitalStarSubalgebra`) acting +non-degenerately (`InnerProductSpace.ActsNondegenerately`); each result also carries a +`_starSubalgebra` corollary for the unital special case, where non-degeneracy is automatic. + ## Main results * `mem_sotClosure_of_mem_doubleCommutant`: any `T ∈ A''` lies in the SOT-closure of `A`. This is the key technical result, using diagonal amplification and the cyclic subspace argument. -* `isWOTClosed_of_isSOTClosed_starSubalgebra`: a SOT-closed *-subalgebra is WOT-closed. -* `doubleCommutant_eq_of_isSOTClosed`: if a *-subalgebra is SOT-closed, it equals its double - commutant (the "hard half" of the SOT double commutant theorem). +* `sotClosure_eq_doubleCommutant`, `wotClosure_eq_doubleCommutant`: the **density form** of the + theorem — for every non-degenerate `*`-subalgebra, with no closedness hypothesis whatsoever, + `A'' = closure_SOT A = closure_WOT A`. This is the strongest statement the argument below + establishes; everything after it is a corollary. +* `StrongOperatorTopology.isSOTClosed_of_isWOTClosed` (with `continuous_sotToWOT`): SOT is finer + than WOT, so every WOT-closed set is SOT-closed — no hypothesis on the set. Stated here, not in + the `ForMathlib` topology files, because those import Mathlib only. +* `isWOTClosed_of_isSOTClosed`: a SOT-closed non-degenerate *-subalgebra is WOT-closed. +* `doubleCommutant_eq_of_isSOTClosed`: if a non-degenerate *-subalgebra is SOT-closed, it equals + its double commutant (the "hard half" of the SOT double commutant theorem). ## Strategy @@ -31,11 +42,57 @@ The proof proceeds in two main steps: hence `A = A''`. Since double commutants are WOT-closed, `A` is WOT-closed. The final theorem `doubleCommutant_eq_of_isSOTClosed` then reduces to the WOT version via -`isWOTClosed_of_isSOTClosed_starSubalgebra`. +`isWOTClosed_of_isSOTClosed`. -/ @[expose] public section +/-! +## SOT is finer than WOT + +The comparison of the two type copies lives here rather than in either `ForMathlib` topology +file, because `ForMathlib` files import Mathlib only and the comparison needs both copies. +-/ + +namespace StrongOperatorTopology + +open WeakOperatorTopology + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] + +local notation "B" => (H →L[ℂ] H) +local notation "BSOT" => (H →SLₚₜ[RingHom.id ℂ] H) +local notation "BWOT" => (H →WOT[ℂ] H) + +/-- The identity map on operators, read as a map from the SOT type-copy to the WOT type-copy. -/ +noncomputable def sotToWOT (T : BSOT) : BWOT := + toWOTEquiv (H := H) ((toSOTEquiv (H := H)).symm T) + +@[simp] lemma sotToWOT_apply (T : BSOT) (x : H) : (sotToWOT (H := H) T) x = T x := rfl + +/-- **SOT is finer than WOT.** The identity map from the SOT type-copy to the WOT type-copy is +continuous: a net converging pointwise in norm converges in every matrix coefficient. -/ +lemma continuous_sotToWOT : Continuous (sotToWOT (H := H)) := by + refine ContinuousLinearMapWOT.continuous_of_dual_apply_continuous ?_ + intro x y + have hval : ∀ T : BSOT, y ((sotToWOT (H := H) T) x) = y (T x) := fun _ => rfl + simp only [hval] + exact y.continuous.comp ((continuous_apply x).comp + (PointwiseConvergenceCLM.isEmbedding_coeFn (RingHom.id ℂ) H H).continuous) + +/-- The SOT view of a set is the preimage of its WOT view under `sotToWOT`. -/ +lemma Set.toSOT_eq_preimage_toWOT (S : Set B) : + Set.toSOT (H := H) S = sotToWOT (H := H) ⁻¹' Set.toWOT (H := H) S := rfl + +/-- A WOT-closed set is SOT-closed. No hypothesis on `S` is needed: this is nothing but SOT being +the finer topology. The converse needs convexity and is not proved here. -/ +theorem isSOTClosed_of_isWOTClosed {S : Set B} (hS : IsWOTClosed (H := H) S) : + IsSOTClosed (H := H) S := by + rw [IsSOTClosed, Set.toSOT_eq_preimage_toWOT] + exact hS.preimage continuous_sotToWOT + +end StrongOperatorTopology + namespace SOTClosedSubalgebra open InnerProductSpace StrongOperatorTopology WeakOperatorTopology @@ -43,21 +100,25 @@ open InnerProductSpace StrongOperatorTopology WeakOperatorTopology variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] local notation "B" => (H →L[ℂ] H) -local notation "BSOT" => ContinuousLinearMapSOT H +local notation "BSOT" => (H →SLₚₜ[RingHom.id ℂ] H) local notation "BWOT" => (H →WOT[ℂ] H) -/-- Any element of the double commutant of a *-subalgebra lies in the SOT-closure of the algebra. +/-- Any element of the double commutant of a (possibly non-unital) *-subalgebra acting +non-degenerately lies in the SOT-closure of the algebra. This is the key technical result: we show that for any `T ∈ A''` and any finite set of points `x₁, ..., xₙ ∈ H`, the tuple `(Tx₁, ..., Txₙ)` lies in the closure of `{(Sx₁, ..., Sxₙ) | S ∈ A}`. -This uses diagonal amplification and the cyclic subspace argument. +This uses diagonal amplification and the cyclic subspace argument. For the unital special case +see `mem_sotClosure_of_mem_doubleCommutant_starSubalgebra`. -/ -lemma mem_sotClosure_of_mem_doubleCommutant (A : StarSubalgebra ℂ B) (T : B) +lemma mem_sotClosure_of_mem_doubleCommutant (A : NonUnitalStarSubalgebra ℂ B) (T : B) + (hnd : ActsNondegenerately (A : Set B)) (hT : T ∈ Set.centralizer (Set.centralizer (A : Set B))) : - (⟨T⟩ : BSOT) ∈ closure (Set.toSOT (H := H) (A : Set B)) := by - -- The SOT is induced by inducingFn : BSOT → (H → H). - -- Use the criterion: T ∈ closure(S) iff inducingFn T ∈ closure(inducingFn '' S) in product topology - rw [ContinuousLinearMapSOT.isInducing_inducingFn.closure_eq_preimage_closure_image] + (toSOTEquiv T : BSOT) ∈ closure (Set.toSOT (H := H) (A : Set B)) := by + -- The SOT is induced by the coercion `BSOT → (H → H)`. + -- Use the criterion: T ∈ closure(S) iff `↑T ∈ closure (↑'' S)` in the product topology. + rw [(PointwiseConvergenceCLM.isEmbedding_coeFn (RingHom.id ℂ) H + H).isInducing.closure_eq_preimage_closure_image] simp only [Set.mem_preimage] -- Need to show T (as a function H → H) is in the closure of {S : H → H | S ∈ A} -- in the product topology on H → H. @@ -92,8 +153,14 @@ lemma mem_sotClosure_of_mem_doubleCommutant (A : StarSubalgebra ℂ B) (T : B) let e : Fin n ≃ I_fin := Fintype.equivOfCardEq (by simp [n]) let xVec : Fin n → H := fun i => (e i).val let x_amp : Hn (H := H) n := (WithLp.equiv _ _).symm xVec - let Aamp : StarSubalgebra ℂ (Hn (H := H) n →L[ℂ] Hn (H := H) n) := + let Aamp : NonUnitalStarSubalgebra ℂ (Hn (H := H) n →L[ℂ] Hn (H := H) n) := A.map (diagonalStarAlgHom (H := H) n) + have hndAmp : ActsNondegenerately (Aamp : Set (Hn (H := H) n →L[ℂ] Hn (H := H) n)) := by + have hcoe : (Aamp : Set (Hn (H := H) n →L[ℂ] Hn (H := H) n)) = + diagonal (H := H) (n := n) '' (A : Set B) := by + simp [Aamp, diagonalStarAlgHom] + rw [hcoe] + exact WOTClosedSubalgebra.actsNondegenerately_image_diagonal (H := H) hnd n -- Choose a uniform ε let ε := I_fin.attach.inf' hI_fin_ne.attach (fun x => (heps' x).choose) have hε_pos' : ε > 0 := by @@ -110,7 +177,7 @@ lemma mem_sotClosure_of_mem_doubleCommutant (A : StarSubalgebra ℂ B) (T : B) have hTn : diagonal (H := H) (n := n) T ∈ Set.centralizer (Set.centralizer (Aamp : Set _)) := by have hdiag := diagonal_mem_double_commutant (H := H) (n := n) (A := (A : Set _)) (T := T) hT - simp only [StarSubalgebra.coe_map, Aamp, diagonalStarAlgHom] + simp only [NonUnitalStarSubalgebra.coe_map, Aamp, diagonalStarAlgHom] simpa using hdiag -- T^(n) preserves reducing subspaces of A^(n) have hPres : WOTClosedSubalgebra.PreservesReducingSubspaces (H := Hn (H := H) n) @@ -121,7 +188,7 @@ lemma mem_sotClosure_of_mem_doubleCommutant (A : StarSubalgebra ℂ B) (T : B) have hxmem : diagonal (H := H) (n := n) T x_amp ∈ WOTClosedSubalgebra.cyclicSubspace (H := Hn (H := H) n) Aamp x_amp := WOTClosedSubalgebra.mem_cyclicSubspace_of_preservesReducingSubspaces - (H := Hn (H := H) n) Aamp hPres x_amp + (H := Hn (H := H) n) Aamp hndAmp hPres x_amp -- The set {a x | a ∈ Aamp} equals {diagonal S x | S ∈ A} have hAamp_orbit : Set.range (fun a : Aamp => (a : Hn (H := H) n →L[ℂ] Hn (H := H) n) x_amp) = {y | ∃ S ∈ (A : Set _), y = diagonal (H := H) (n := n) S x_amp} := by @@ -129,13 +196,13 @@ lemma mem_sotClosure_of_mem_doubleCommutant (A : StarSubalgebra ℂ B) (T : B) simp only [Set.mem_range, Set.mem_setOf_eq] constructor · rintro ⟨⟨a, ha⟩, rfl⟩ - simp only [Aamp, StarSubalgebra.mem_map, diagonalStarAlgHom, StarAlgHom.coe_mk', + simp only [Aamp, NonUnitalStarSubalgebra.mem_map, diagonalStarAlgHom, StarAlgHom.coe_mk', AlgHom.coe_mk, RingHom.coe_mk, MonoidHom.coe_mk, OneHom.coe_mk] at ha rcases ha with ⟨S, hS, rfl⟩ exact ⟨S, hS, rfl⟩ · rintro ⟨S, hS, rfl⟩ refine ⟨⟨diagonal (H := H) (n := n) S, ?_⟩, rfl⟩ - simp only [Aamp, StarSubalgebra.mem_map, diagonalStarAlgHom, StarAlgHom.coe_mk', + simp only [Aamp, NonUnitalStarSubalgebra.mem_map, diagonalStarAlgHom, StarAlgHom.coe_mk', AlgHom.coe_mk, RingHom.coe_mk, MonoidHom.coe_mk, OneHom.coe_mk] exact ⟨S, hS, rfl⟩ -- The map S ↦ diagonal S x_amp is linear, so the orbit is a submodule @@ -202,19 +269,72 @@ lemma mem_sotClosure_of_mem_doubleCommutant (A : StarSubalgebra ℂ B) (T : B) calc ‖S z - T z‖ _ ≤ dist (diagonal (H := H) (n := n) T x_amp) (diagonal (H := H) (n := n) S x_amp) := hnorm_ineq _ < ε := hdist - · -- Show S ∈ inducingFn '' Set.toSOT A + · -- Show S, as a function, lies in the image of `Set.toSOT A`. simp only [Set.mem_image] - exact ⟨⟨S⟩, Set.mem_toSOT_iff.mpr hS, rfl⟩ - · -- I is empty, so the pi set is the whole space + exact ⟨toSOTEquiv S, Set.mem_toSOT_iff.mpr hS, rfl⟩ + · -- I is empty, so the pi set is the whole space; any element of `A` works, e.g. `0`. simp only [Set.not_nonempty_iff_eq_empty] at hI_nonempty - use (1 : H →L[ℂ] H) + use (0 : H →L[ℂ] H) constructor · apply htU rw [hI_nonempty, Set.empty_pi] exact Set.mem_univ _ - · exact ⟨⟨1⟩, Set.mem_toSOT_iff.mpr A.one_mem, rfl⟩ + · exact ⟨toSOTEquiv 0, Set.mem_toSOT_iff.mpr A.zero_mem, rfl⟩ -theorem isWOTClosed_of_isSOTClosed_starSubalgebra (A : StarSubalgebra ℂ B) +/-- Unital special case of `mem_sotClosure_of_mem_doubleCommutant`: since `1 ∈ A`, the algebra +acts non-degenerately and the general lemma applies. -/ +lemma mem_sotClosure_of_mem_doubleCommutant_starSubalgebra (A : StarSubalgebra ℂ B) (T : B) + (hT : T ∈ Set.centralizer (Set.centralizer (A : Set B))) : + (toSOTEquiv T : BSOT) ∈ closure (Set.toSOT (H := H) (A : Set B)) := + mem_sotClosure_of_mem_doubleCommutant A.toNonUnitalStarSubalgebra T + (actsNondegenerately_of_one_mem A.one_mem) hT + +/-- **The density form of the bicommutant theorem, SOT half.** For *every* non-degenerate +`*`-subalgebra — with no closedness hypothesis at all — the double commutant is exactly the +SOT-closure: + +`A'' = closure_SOT A`. + +This is the statement the proof actually establishes; the three-condition equivalence +(`DoubleCommutant.bicommutant_tfae`) is the corollary obtained by asking when the two sides equal +`A` itself. -/ +theorem sotClosure_eq_doubleCommutant (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) : + closure (Set.toSOT (H := H) (A : Set B)) = + Set.toSOT (H := H) (Set.centralizer (Set.centralizer (A : Set B))) := by + refine Set.eq_of_subset_of_subset ?_ ?_ + · -- The right-hand side is SOT-closed and contains `A`, so it contains the SOT-closure. + refine closure_minimal (fun T hT => ?_) (isSOTClosed_centralizer_centralizer (H := H) _) + exact Set.subset_centralizer_centralizer (S := (A : Set B)) hT + · intro T hT + exact mem_sotClosure_of_mem_doubleCommutant A _ hnd (Set.mem_toSOT_iff.mp hT) + +/-- **The density form of the bicommutant theorem, WOT half.** For every non-degenerate +`*`-subalgebra, with no closedness hypothesis, `A'' = closure_WOT A`. + +Together with `sotClosure_eq_doubleCommutant` this says that the SOT- and WOT-closures of a +non-degenerate `*`-subalgebra coincide, both being `A''`. -/ +theorem wotClosure_eq_doubleCommutant (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) : + closure (Set.toWOT (H := H) (A : Set B)) = + Set.toWOT (H := H) (Set.centralizer (Set.centralizer (A : Set B))) := by + refine Set.eq_of_subset_of_subset ?_ ?_ + · refine closure_minimal (fun T hT => ?_) (isWOTClosed_centralizer_centralizer (H := H) _) + exact Set.subset_centralizer_centralizer (Set.mem_toWOT_iff.mp hT) + · intro T hT + -- Go through the SOT-closure and push it forward along the continuous map `sotToWOT`. + have hSOT : (toSOTEquiv ((toWOTEquiv (H := H)).symm T) : BSOT) ∈ + closure (Set.toSOT (H := H) (A : Set B)) := + mem_sotClosure_of_mem_doubleCommutant A _ hnd (Set.mem_toWOT_iff.mp hT) + have hsub : closure (Set.toSOT (H := H) (A : Set B)) ⊆ + sotToWOT (H := H) ⁻¹' closure (Set.toWOT (H := H) (A : Set B)) := + closure_minimal (fun S hS => subset_closure (Set.mem_toSOT_iff.mp hS)) + (isClosed_closure.preimage continuous_sotToWOT) + simpa using hsub hSOT + +/-- A SOT-closed (possibly non-unital) *-subalgebra acting non-degenerately is WOT-closed. -/ +theorem isWOTClosed_of_isSOTClosed (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) (hSOT : IsSOTClosed (H := H) (A : Set B)) : IsWOTClosed (H := H) (A : Set B) := by -- Strategy: Show A = A'', then use that A'' is WOT-closed. @@ -223,21 +343,40 @@ theorem isWOTClosed_of_isSOTClosed_starSubalgebra (A : StarSubalgebra ℂ B) apply Set.eq_of_subset_of_subset Set.subset_centralizer_centralizer intro T hT -- T ∈ A'' → T ∈ SOT-closure(A) → T ∈ A (since A is SOT-closed) - have hT_in_SOT := mem_sotClosure_of_mem_doubleCommutant A T hT - simp only [Set.toSOT] at hT_in_SOT - rwa [IsClosed.closure_eq hSOT, Set.mem_setOf_eq] at hT_in_SOT + have hT_in_SOT := mem_sotClosure_of_mem_doubleCommutant A T hnd hT + rw [IsClosed.closure_eq hSOT] at hT_in_SOT + simpa [Set.mem_toSOT_iff] using hT_in_SOT rw [hA_eq_Acc] exact isWOTClosed_centralizer_centralizer (H := H) (A : Set B) +/-- Unital special case of `isWOTClosed_of_isSOTClosed`: a SOT-closed unital *-subalgebra is +WOT-closed. -/ +theorem isWOTClosed_of_isSOTClosed_starSubalgebra (A : StarSubalgebra ℂ B) + (hSOT : IsSOTClosed (H := H) (A : Set B)) : + IsWOTClosed (H := H) (A : Set B) := + isWOTClosed_of_isSOTClosed A.toNonUnitalStarSubalgebra + (actsNondegenerately_of_one_mem A.one_mem) hSOT + /-- The double commutant theorem (hard half) for SOT-closed *-subalgebras. -A SOT-closed *-subalgebra equals its double commutant. +A SOT-closed (possibly non-unital) *-subalgebra acting non-degenerately equals its double +commutant. For the unital special case see `doubleCommutant_eq_of_isSOTClosed_starSubalgebra`. -/ -theorem doubleCommutant_eq_of_isSOTClosed (A : StarSubalgebra ℂ B) +theorem doubleCommutant_eq_of_isSOTClosed (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) (hSOT : IsSOTClosed (H := H) (A : Set B)) : Set.centralizer (Set.centralizer (A : Set B)) = (A : Set B) := by -- Reduce to the WOT version. - have hWOT : IsWOTClosed (H := H) (A : Set B) := isWOTClosed_of_isSOTClosed_starSubalgebra A hSOT - exact WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed (H := H) A hWOT + have hWOT : IsWOTClosed (H := H) (A : Set B) := isWOTClosed_of_isSOTClosed A hnd hSOT + exact WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed (H := H) A hnd hWOT + +/-- Unital special case of `doubleCommutant_eq_of_isSOTClosed`: a SOT-closed unital +*-subalgebra equals its double commutant. Since `1 ∈ A`, the algebra acts non-degenerately +and the general theorem applies. -/ +theorem doubleCommutant_eq_of_isSOTClosed_starSubalgebra (A : StarSubalgebra ℂ B) + (hSOT : IsSOTClosed (H := H) (A : Set B)) : + Set.centralizer (Set.centralizer (A : Set B)) = (A : Set B) := + doubleCommutant_eq_of_isSOTClosed A.toNonUnitalStarSubalgebra + (actsNondegenerately_of_one_mem A.one_mem) hSOT end SOTClosedSubalgebra diff --git a/QuantumSystem/Algebra/Star/DoubleCommutant/TFAE.lean b/QuantumSystem/Algebra/Star/DoubleCommutant/TFAE.lean new file mode 100644 index 0000000..95481b2 --- /dev/null +++ b/QuantumSystem/Algebra/Star/DoubleCommutant/TFAE.lean @@ -0,0 +1,309 @@ +module + +public import QuantumSystem.Algebra.Star.DoubleCommutant.SOTClosedSubAlgebra +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.FiniteRank +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.RankOne +public import QuantumSystem.ForMathlib.Analysis.VonNeumannAlgebra.Commutant +public import Mathlib.Tactic.TFAE + +@[expose] public section + +/-! +# The bicommutant theorem as a list of equivalent conditions + +This file packages the two halves proved in +`QuantumSystem.Algebra.Star.DoubleCommutant.WOTClosedSubAlgebra` and +`QuantumSystem.Algebra.Star.DoubleCommutant.SOTClosedSubAlgebra` into the three-condition form of +von Neumann's bicommutant theorem: for a `*`-subalgebra `A ⊆ B(H)` acting non-degenerately, + +1. `A` equals its own double commutant, +2. `A` is closed in the weak operator topology, +3. `A` is closed in the strong operator topology + +are equivalent. + +## Main results + +* `DoubleCommutant.bicommutant_tfae`: the equivalence for a possibly non-unital `*`-subalgebra + acting non-degenerately. +* `DoubleCommutant.bicommutant_tfae_starSubalgebra`: the unital special case, where non-degeneracy + is automatic. This is the form in which the theorem is usually quoted. +* `DoubleCommutant.bicommutant_tfae_image` (and its unital special case + `DoubleCommutant.bicommutant_tfae_image_starSubalgebra`): the two closedness conditions spelled + out as closedness of the image of `A` in the corresponding Mathlib type-copy. +* `DoubleCommutant.one_mem_of_isSOTClosed` (and `WOTClosedSubalgebra.one_mem_of_isWOTClosed`): a + non-degenerate `*`-subalgebra satisfying any of the three conditions contains `1`. This is what + the non-unital form adds over the unital one, and it is also what makes the extra scope purely + negative. +* `DoubleCommutant.bicommutant_tfae_finiteRankOperators`: the witness that the non-unital + generality is inhabited — on an infinite-dimensional `H` the theorem applies to `F(H)`, which + does not contain `1`. That it is inhabited only *negatively* is + `DoubleCommutant.not_isWOTClosed_finiteRankOperators`, + `DoubleCommutant.not_isSOTClosed_finiteRankOperators` and + `DoubleCommutant.centralizer_centralizer_ne_finiteRankOperators`. +* `VonNeumannAlgebra.ofIsWOTClosed`, `VonNeumannAlgebra.ofIsSOTClosed`: the payoff — a WOT-closed + (resp. SOT-closed) unital `*`-subalgebra *is* a von Neumann algebra, which is what makes the + topological definition of a von Neumann algebra usable against Mathlib's algebraic one. + +## Which form is standard + +The literature overwhelmingly quotes the **unital** three-condition form +(`bicommutant_tfae_starSubalgebra`); the non-degenerate form of `bicommutant_tfae` is the more +general one, adopted here because it also covers algebras that do not contain `1` — the compact +operators `K(H)` and the finite-rank operators `F(H)` on an infinite-dimensional `H` are +non-degenerate and non-unital, and the unital form says nothing about them. Norm-closedness, which +some sources additionally assume, is deliberately *not* assumed: no step of the proof consumes it +and each of the three conditions implies it. + +The extra scope so gained is inhabited only by algebras for which all three conditions *fail*: +by `one_mem_of_isSOTClosed`, a non-degenerate algebra satisfying any one of them contains `1` and +is therefore already covered by the unital form. That is not a defect — it is what the general +form is for, since the unital form cannot so much as state that `F(H)` is not WOT-closed. + +## Note on non-degeneracy + +Non-degeneracy is not a technical convenience: without it the equivalence is false. For a +decomposition `H = H₁ ⊕ H₂` with `H₂ ≠ 0` the algebra `B(H₁) ⊕ 0` is WOT-closed but is strictly +smaller than its double commutant, which is `B(H₁) ⊕ ℂ1`. (For `H₂ = 0` there is nothing to +separate and the algebra is all of `B(H)`.) Containing `1` is the standard sufficient condition, +which is why the unital corollaries carry no extra hypothesis; conversely, non-degeneracy plus any +one of the three conditions *implies* `1 ∈ A` (`WOTClosedSubalgebra.one_mem_of_isWOTClosed`, +`one_mem_of_isSOTClosed`), so the two hypothesis packages differ exactly on the algebras where the +equivalence holds negatively. + +Non-degeneracy is consumed **only** by the implication 3 → 1. The other two implications are +hypothesis-free: 1 → 2 because every double commutant is WOT-closed, and 2 → 3 because the SOT is +finer than the WOT (`StrongOperatorTopology.isSOTClosed_of_isWOTClosed`). The proof below is +routed as the cycle 1 → 2 → 3 → 1 precisely so that `hnd` is used at that one point and nowhere +else. +-/ + +/-! +## `F(H)` against the rest of the development + +The finite-rank operators are defined in +`QuantumSystem.ForMathlib.Analysis.InnerProductSpace.FiniteRank`, which imports Mathlib only. The +facts below combine them with declarations from *other* `ForMathlib` files +(`InnerProductSpace.ActsNondegenerately`, +`ContinuousLinearMap.exists_eq_smul_one_of_forall_rankOne_comm`), so they live here. +-/ + +/-- `S′` denotes the commutant `Set.centralizer S` of a set of operators, the prime notation of the +operator-algebra literature (see `docs/math/bicommutant-theorem.md`, convention (C8)). Local to this +file: the file never opens the `VonNeumannAlgebra` scope, so this does not collide with +`VonNeumannAlgebra.commutant`'s own `′` (`QuantumSystem.ForMathlib.Analysis.VonNeumannAlgebra.Commutant`), +which is the same notation one level up, for a `VonNeumannAlgebra` rather than a bare `Set`. -/ +local postfix:max "′" => Set.centralizer + +namespace InnerProductSpace + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-- **`F(H)` acts non-degenerately**, on every Hilbert space: the rank-one operator +`|x⟩⟨x| : z ↦ ⟪x, z⟫ • x` does not annihilate `x` unless `x = 0`. -/ +theorem actsNondegenerately_finiteRankOperators : + ActsNondegenerately (finiteRankOperators (H := H) : Set (H →L[ℂ] H)) := by + intro x hx + have h := hx (rankOne ℂ x x) (rankOne_mem_finiteRankOperators x x) + have h' : (inner ℂ x x : ℂ) • x = 0 := by simpa using h + rcases smul_eq_zero.mp h' with h1 | h1 + · exact inner_self_eq_zero.mp h1 + · exact h1 + +/-- **The commutant of `F(H)` is the scalars.** An operator commuting with every finite-rank +operator commutes in particular with every rank-one operator `|x⟩⟨y|`, and that already forces it +to be a multiple of the identity +(`ContinuousLinearMap.exists_eq_smul_one_of_forall_rankOne_comm`). -/ +theorem centralizer_finiteRankOperators : + (finiteRankOperators (H := H) : Set (H →L[ℂ] H))′ + = Set.range fun c : ℂ => c • (1 : H →L[ℂ] H) := by + refine Set.Subset.antisymm (fun S hS => ?_) ?_ + · have hcomm : ∀ x y : H, S ∘L rankOne ℂ x y = rankOne ℂ x y ∘L S := by + intro x y + have h := hS _ (rankOne_mem_finiteRankOperators x y) + simpa [ContinuousLinearMap.mul_def] using h.symm + obtain ⟨c, hc⟩ := ContinuousLinearMap.exists_eq_smul_one_of_forall_rankOne_comm hcomm + exact ⟨c, hc.symm⟩ + · rintro _ ⟨c, rfl⟩ + exact Set.center_subset_centralizer _ (Set.smul_mem_center c Set.one_mem_center) + +/-- **`F(H)'' = B(H)`.** The double commutant of the finite-rank operators is everything, because +their commutant is the scalars (`centralizer_finiteRankOperators`) and the scalars are central. + +On an infinite-dimensional `H` this is strictly larger than `F(H)`, which is why all three +conditions of the bicommutant theorem fail for `F(H)`; on a finite-dimensional `H` it is an +equality, `F(H)` being all of `B(H)` there. -/ +theorem centralizer_centralizer_finiteRankOperators : + (finiteRankOperators (H := H) : Set (H →L[ℂ] H))′′ = Set.univ := by + rw [centralizer_finiteRankOperators, Set.centralizer_eq_top_iff_subset] + rintro _ ⟨c, rfl⟩ + exact Set.smul_mem_center c Set.one_mem_center + +end InnerProductSpace + +namespace DoubleCommutant + +open InnerProductSpace StrongOperatorTopology WeakOperatorTopology + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +local notation "B" => (H →L[ℂ] H) + +/-- **The bicommutant theorem.** For a possibly non-unital `*`-subalgebra of `B(H)` acting +non-degenerately, equalling its own double commutant, being WOT-closed, and being SOT-closed are +all equivalent. + +Only the implication 3 → 1 consumes the non-degeneracy hypothesis; see the module docstring. -/ +theorem bicommutant_tfae (A : NonUnitalStarSubalgebra ℂ B) (hnd : ActsNondegenerately (A : Set B)) : + List.TFAE [ + (A : Set B)′′ = (A : Set B), + IsWOTClosed (H := H) (A : Set B), + IsSOTClosed (H := H) (A : Set B)] := by + tfae_have 1 → 2 := by + intro h + rw [← h] + exact isWOTClosed_centralizer_centralizer (H := H) (A : Set B) + tfae_have 2 → 3 := isSOTClosed_of_isWOTClosed (H := H) + tfae_have 3 → 1 := SOTClosedSubalgebra.doubleCommutant_eq_of_isSOTClosed A hnd + tfae_finish + +/-- Unital special case of `DoubleCommutant.bicommutant_tfae`, and the form in which the theorem is +usually quoted: since `1 ∈ A`, the algebra acts non-degenerately and no extra hypothesis is +needed. -/ +theorem bicommutant_tfae_starSubalgebra (A : StarSubalgebra ℂ B) : + List.TFAE [ + (A : Set B)′′ = (A : Set B), + IsWOTClosed (H := H) (A : Set B), + IsSOTClosed (H := H) (A : Set B)] := + bicommutant_tfae A.toNonUnitalStarSubalgebra (actsNondegenerately_of_one_mem A.one_mem) + +/-- `DoubleCommutant.bicommutant_tfae` with the two closedness conditions spelled out as closedness +of the image of `A` in the WOT resp. pointwise-convergence type-copy of `B(H)`. Stated at the same +generality as `DoubleCommutant.bicommutant_tfae`, since `isWOTClosed_iff_isClosed_image` and +`isSOTClosed_iff_isClosed_image` hold for an arbitrary set of operators. -/ +theorem bicommutant_tfae_image (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) : + List.TFAE [ + (A : Set B)′′ = (A : Set B), + IsClosed (ContinuousLinearMapWOT.ContinuousLinearMap.toWOTCLM + (σ := RingHom.id ℂ) (E := H) (F := H) '' (A : Set B)), + IsClosed (ContinuousLinearMap.toPointwiseConvergenceCLM ℂ (RingHom.id ℂ) H H + '' (A : Set B))] := by + rw [← isWOTClosed_iff_isClosed_image, ← isSOTClosed_iff_isClosed_image] + exact bicommutant_tfae A hnd + +/-- Unital special case of `DoubleCommutant.bicommutant_tfae_image`; equivalently, +`DoubleCommutant.bicommutant_tfae_starSubalgebra` with the two closedness conditions spelled out as +closedness of the image of `A` in the WOT resp. pointwise-convergence type-copy of `B(H)`. -/ +theorem bicommutant_tfae_image_starSubalgebra (A : StarSubalgebra ℂ B) : + List.TFAE [ + (A : Set B)′′ = (A : Set B), + IsClosed (ContinuousLinearMapWOT.ContinuousLinearMap.toWOTCLM + (σ := RingHom.id ℂ) (E := H) (F := H) '' (A : Set B)), + IsClosed (ContinuousLinearMap.toPointwiseConvergenceCLM ℂ (RingHom.id ℂ) H H + '' (A : Set B))] := + bicommutant_tfae_image A.toNonUnitalStarSubalgebra (actsNondegenerately_of_one_mem A.one_mem) + +/-- **A non-degenerate `*`-subalgebra satisfying any one of the three conditions contains `1`.** +Stated for the WOT in `WOTClosedSubalgebra.one_mem_of_isWOTClosed`; this is the SOT companion. + +Together the two say that the extra scope of the non-unital form over the unital one contains only +*negative* instances: a non-degenerate `*`-subalgebra that does not contain `1` fails all three +conditions. -/ +theorem one_mem_of_isSOTClosed (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) (hA : IsSOTClosed (H := H) (A : Set B)) : + (1 : B) ∈ A := + WOTClosedSubalgebra.one_mem_of_isWOTClosed A hnd + (SOTClosedSubalgebra.isWOTClosed_of_isSOTClosed A hnd hA) + +/-- **The non-unital generality is inhabited.** On an infinite-dimensional Hilbert space the +finite-rank operators `F(H)` act non-degenerately and do not contain `1`, so +`DoubleCommutant.bicommutant_tfae` applies to them while +`DoubleCommutant.bicommutant_tfae_starSubalgebra` does not. This is the witness that the +non-degeneracy hypothesis really is weaker than unitality, and not merely formally so. + +It is inhabited only *negatively*, and necessarily so: by `DoubleCommutant.one_mem_of_isSOTClosed` +a non-degenerate `*`-subalgebra satisfying any of the three conditions contains `1`. For `F(H)` all +three therefore fail together, exactly as the equivalence demands — see +`DoubleCommutant.not_isWOTClosed_finiteRankOperators`, +`DoubleCommutant.not_isSOTClosed_finiteRankOperators` and +`DoubleCommutant.centralizer_centralizer_ne_finiteRankOperators`, the last of which sharpens the +failure to `F(H)'' = B(H) ≠ F(H)`. -/ +theorem bicommutant_tfae_finiteRankOperators (h : ¬ FiniteDimensional ℂ H) : + (1 : B) ∉ finiteRankOperators (H := H) ∧ + List.TFAE [ + (finiteRankOperators (H := H) : Set B)′′ = (finiteRankOperators (H := H) : Set B), + IsWOTClosed (H := H) (finiteRankOperators (H := H) : Set B), + IsSOTClosed (H := H) (finiteRankOperators (H := H) : Set B)] := + ⟨one_notMem_finiteRankOperators h, + bicommutant_tfae _ actsNondegenerately_finiteRankOperators⟩ + +/-- On an infinite-dimensional `H` the finite-rank operators are **not** WOT-closed: they act +non-degenerately, so WOT-closedness would force `1 ∈ F(H)`. -/ +theorem not_isWOTClosed_finiteRankOperators (h : ¬ FiniteDimensional ℂ H) : + ¬ IsWOTClosed (H := H) (finiteRankOperators (H := H) : Set B) := fun hA => + one_notMem_finiteRankOperators h + (WOTClosedSubalgebra.one_mem_of_isWOTClosed _ actsNondegenerately_finiteRankOperators hA) + +/-- On an infinite-dimensional `H` the finite-rank operators are **not** SOT-closed. -/ +theorem not_isSOTClosed_finiteRankOperators (h : ¬ FiniteDimensional ℂ H) : + ¬ IsSOTClosed (H := H) (finiteRankOperators (H := H) : Set B) := fun hA => + one_notMem_finiteRankOperators h + (one_mem_of_isSOTClosed _ actsNondegenerately_finiteRankOperators hA) + +/-- On an infinite-dimensional `H` the finite-rank operators are **not** their own double +commutant: `F(H)'' = B(H)` by `InnerProductSpace.centralizer_centralizer_finiteRankOperators`, +while `1 ∉ F(H)`. -/ +theorem centralizer_centralizer_ne_finiteRankOperators (h : ¬ FiniteDimensional ℂ H) : + (finiteRankOperators (H := H) : Set B)′′ ≠ (finiteRankOperators (H := H) : Set B) := by + intro heq + refine one_notMem_finiteRankOperators h ?_ + change (1 : B) ∈ (finiteRankOperators (H := H) : Set B) + rw [← heq, centralizer_centralizer_finiteRankOperators] + trivial + +end DoubleCommutant + +namespace VonNeumannAlgebra + +open InnerProductSpace StrongOperatorTopology WeakOperatorTopology + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-- **A WOT-closed unital `*`-subalgebra is a von Neumann algebra.** This is the reason the +bicommutant theorem is proved: Mathlib defines `VonNeumannAlgebra` by the *algebraic* condition +`A'' = A`, and this constructor supplies that field from the *topological* condition, so that an +algebra produced as a weak limit closure can be used as a von Neumann algebra. -/ +noncomputable def ofIsWOTClosed (A : StarSubalgebra ℂ (H →L[ℂ] H)) + (hA : IsWOTClosed (H := H) (A : Set (H →L[ℂ] H))) : VonNeumannAlgebra H where + toStarSubalgebra := A + centralizer_centralizer' := + WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed_starSubalgebra A hA + +/-- The carrier of `VonNeumannAlgebra.ofIsWOTClosed A hA` is `A` itself: the constructor supplies +only the `centralizer_centralizer'` field and leaves the underlying `*`-subalgebra untouched. -/ +@[simp] lemma coe_ofIsWOTClosed (A : StarSubalgebra ℂ (H →L[ℂ] H)) + (hA : IsWOTClosed (H := H) (A : Set (H →L[ℂ] H))) : + (ofIsWOTClosed A hA : Set (H →L[ℂ] H)) = (A : Set (H →L[ℂ] H)) := rfl + +/-- **An SOT-closed unital `*`-subalgebra is a von Neumann algebra.** The SOT companion of +`VonNeumannAlgebra.ofIsWOTClosed`. -/ +noncomputable def ofIsSOTClosed (A : StarSubalgebra ℂ (H →L[ℂ] H)) + (hA : IsSOTClosed (H := H) (A : Set (H →L[ℂ] H))) : VonNeumannAlgebra H := + ofIsWOTClosed A (SOTClosedSubalgebra.isWOTClosed_of_isSOTClosed_starSubalgebra A hA) + +/-- The carrier of `VonNeumannAlgebra.ofIsSOTClosed A hA` is `A` itself; the SOT companion of +`VonNeumannAlgebra.coe_ofIsWOTClosed`. -/ +@[simp] lemma coe_ofIsSOTClosed (A : StarSubalgebra ℂ (H →L[ℂ] H)) + (hA : IsSOTClosed (H := H) (A : Set (H →L[ℂ] H))) : + (ofIsSOTClosed A hA : Set (H →L[ℂ] H)) = (A : Set (H →L[ℂ] H)) := rfl + +/-- A WOT-closed unital `*`-subalgebra is the von Neumann algebra it generates: the topological +closedness condition and `VonNeumannAlgebra.generated` agree on it. -/ +lemma generated_coe_of_isWOTClosed (A : StarSubalgebra ℂ (H →L[ℂ] H)) + (hA : IsWOTClosed (H := H) (A : Set (H →L[ℂ] H))) : + (generated (A : Set (H →L[ℂ] H)) : Set (H →L[ℂ] H)) = (A : Set (H →L[ℂ] H)) := by + rw [coe_generated_of_star_eq (StarMemClass.star_coe_eq A), + WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed_starSubalgebra A hA] + +end VonNeumannAlgebra diff --git a/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean b/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean index 36de546..4693e84 100644 --- a/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean +++ b/QuantumSystem/Algebra/Star/DoubleCommutant/WOTClosedSubAlgebra.lean @@ -3,6 +3,7 @@ module public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.DiagonalAmplification public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.InvariantSubspace public import QuantumSystem.ForMathlib.Analysis.LocallyConvex.WeakOperatorTopology +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.AdjointNotation /-! # The double commutant theorem (hard half) @@ -14,20 +15,27 @@ This file contains the proof of (a weak-operator-topology form of) von Neumann's Mathlib packages von Neumann algebras using the algebraic characterization: a `StarSubalgebra` is a von Neumann algebra when it equals its double commutant (implemented via `Set.centralizer`). -Classically, for a `*-`subalgebra of bounded operators on a Hilbert space, this algebraic condition -is equivalent to being closed in the weak operator topology (WOT). +Classically, for a possibly non-unital `*-`subalgebra of bounded operators on a Hilbert space +acting *non-degenerately* (no nonzero vector is annihilated by the whole algebra — +`InnerProductSpace.ActsNondegenerately`), this algebraic condition is equivalent to being closed +in the weak operator topology (WOT). Without non-degeneracy the equivalence fails +(e.g. `B(H₁) ⊕ 0` is WOT-closed but not its own double commutant), and containing `1` is the +standard special case: it implies non-degeneracy, and conversely a non-degenerate WOT-closed +`*-`subalgebra automatically contains `1` (`one_mem_of_isWOTClosed`). The "hard half" is the direction -*WOT-closed `*-`subalgebra* ⇒ *equals its double commutant*. +*WOT-closed non-degenerate `*-`subalgebra* ⇒ *equals its double commutant*. We state and prove this as an equality of sets of operators `Set.centralizer (Set.centralizer (A : Set B)) = (A : Set B)`. ## What this file contains -This file provides the main theorem `doubleCommutant_eq_of_isWOTClosed` and the intermediate -infrastructure used in its proof. The key objects are: +This file provides the main theorem `doubleCommutant_eq_of_isWOTClosed` (with unital special case +`doubleCommutant_eq_of_isWOTClosed_starSubalgebra`), its converse-direction corollary +`one_mem_of_isWOTClosed`, and the intermediate infrastructure used in its proof. The key objects +are: * `cyclicSubspace A x`: the closed subspace generated by the orbit `A • x`. It is shown to be *reducing* for `A`. @@ -67,40 +75,63 @@ To prove the hard-half statement `A'' ⊆ A`, we argue by separation: namespace WOTClosedSubalgebra open InnerProductSpace WeakOperatorTopology +open scoped Adjoint variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] local notation "B" => (H →L[ℂ] H) local notation "BWOT" => (H →WOT[ℂ] H) +/-! +## Non-degeneracy and diagonal amplification + +Non-degeneracy (`InnerProductSpace.ActsNondegenerately`) is the hypothesis under which the +theorem holds for a possibly non-unital `*`-subalgebra; it passes to the diagonal amplification +componentwise. +-/ + +omit [CompleteSpace H] in +/-- Diagonal amplification preserves non-degeneracy: if `S` acts non-degenerately on `H`, then +`diagonal '' S` acts non-degenerately on `H^n`. -/ +lemma actsNondegenerately_image_diagonal {S : Set (B)} (hS : ActsNondegenerately S) (n : ℕ) : + ActsNondegenerately (diagonal (H := H) (n := n) '' S) := by + intro y hy + have hzero : ∀ i : Fin n, y.ofLp i = 0 := by + intro i + refine hS _ fun T hT => ?_ + have h0 : diagonal (H := H) (n := n) T y = 0 := hy _ ⟨T, hT, rfl⟩ + calc T (y.ofLp i) = (diagonal (H := H) (n := n) T y).ofLp i := (diagonal_apply T y i).symm + _ = 0 := by rw [h0]; rfl + ext i + exact hzero i + /-! ## Cyclic reducing subspaces -For a `StarSubalgebra` `A` and a vector `x`, the closed subspace generated by the orbit `A • x` -is reducing for `A`. Consequently, any operator in `reducingHull A` sends `x` back into this -cyclic reducing subspace. +For a `NonUnitalStarSubalgebra` `A` and a vector `x`, the closed subspace generated by the orbit +`A • x` is reducing for `A`. Consequently, any operator in `reducingHull A` sends `x` back into +this cyclic reducing subspace, provided `A` acts non-degenerately — non-degeneracy is what puts +`x` itself into the cyclic subspace (`mem_cyclicSubspace_of_actsNondegenerately`), replacing the +appeal to `1 ∈ A` of the unital argument. This is the core invariant-subspace ingredient in the classical “projection” approach. -/ -noncomputable def cyclicSubspace (A : StarSubalgebra ℂ B) (x : H) : Submodule ℂ H := +/-- The closed subspace generated by the orbit `A • x` of a vector `x` under a (possibly +non-unital) `*`-subalgebra `A`. -/ +noncomputable def cyclicSubspace (A : NonUnitalStarSubalgebra ℂ B) (x : H) : Submodule ℂ H := (Submodule.span ℂ (Set.range fun a : A => (a : B) x)).topologicalClosure -lemma subset_cyclicSubspace (A : StarSubalgebra ℂ B) (x : H) : +/-- The orbit `A • x` is contained in the cyclic subspace it generates. -/ +lemma subset_cyclicSubspace (A : NonUnitalStarSubalgebra ℂ B) (x : H) : (Set.range fun a : A => (a : B) x) ⊆ cyclicSubspace (H := H) A x := by intro y hy let S : Submodule ℂ H := Submodule.span ℂ (Set.range fun a : A => (a : B) x) have : y ∈ S := Submodule.subset_span hy exact (Submodule.le_topologicalClosure S) this -lemma mem_cyclicSubspace (A : StarSubalgebra ℂ B) (x : H) : x ∈ cyclicSubspace (H := H) A x := by - -- Use `1 ∈ A`. - have hx : (x : H) ∈ Set.range (fun a : A => (a : B) x) := by - refine ⟨(1 : A), ?_⟩ - simp - exact (subset_cyclicSubspace (H := H) A x) hx - -lemma isInvariant_cyclicSubspace_of_mem (A : StarSubalgebra ℂ B) (x : H) (a : B) +/-- The cyclic subspace is invariant under every element of `A`. -/ +lemma isInvariant_cyclicSubspace_of_mem (A : NonUnitalStarSubalgebra ℂ B) (x : H) (a : B) (ha : a ∈ (A : Set B)) : IsInvariant a (cyclicSubspace (H := H) A x) := by -- Work with the dense submodule `span (A • x)`. @@ -120,7 +151,7 @@ lemma isInvariant_cyclicSubspace_of_mem (A : StarSubalgebra ℂ B) (x : H) (a : have hab : a * (b : B) ∈ (A : Set B) := by -- Coerce `b : A` to `B`. -- `ha` provides `a ∈ A` and `A` is a subalgebra. - simpa using (A.mul_mem ha b.property) + simpa using (mul_mem ha b.property) -- Package `a*b` back as an element of `A`. refine Submodule.subset_span ?_ refine ⟨⟨a * (b : B), hab⟩, ?_⟩ @@ -147,20 +178,22 @@ lemma isInvariant_cyclicSubspace_of_mem (A : StarSubalgebra ℂ B) (x : H) (a : (Submodule.mem_map).2 ⟨y, hy, rfl⟩ exact hclosure this -lemma isReducing_cyclicSubspace (A : StarSubalgebra ℂ B) (x : H) : +/-- The cyclic subspace is reducing for `A`: `A` is star-closed, so invariance under adjoints +comes for free. -/ +lemma isReducing_cyclicSubspace (A : NonUnitalStarSubalgebra ℂ B) (x : H) : IsReducing (H := H) (A : Set B) (cyclicSubspace (H := H) A x) := by intro a ha refine ⟨?_, ?_⟩ · exact isInvariant_cyclicSubspace_of_mem (H := H) (A := A) (x := x) (a := a) ha · -- Use `A` is star-closed. - have ha' : ContinuousLinearMap.adjoint a ∈ (A : Set B) := by + have ha' : a† ∈ (A : Set B) := by -- `star` on `B` is adjoint. - have : (star a) ∈ (A : Set B) := A.star_mem' ha + have : (star a) ∈ (A : Set B) := star_mem (s := A) ha simpa [ContinuousLinearMap.star_eq_adjoint] using this -- Now apply invariance to `a†`. simpa using (isInvariant_cyclicSubspace_of_mem (H := H) (A := A) (x := x) - (a := ContinuousLinearMap.adjoint a) ha') + (a := a†) ha') /-- `T` preserves the reducing subspaces for `S` if it leaves invariant every `S`-reducing subspace (and also its orthogonal complement). -/ @@ -168,7 +201,48 @@ def PreservesReducingSubspaces (S : Set (B)) (T : B) : Prop := ∀ (K : Submodule ℂ H) [K.HasOrthogonalProjection], IsReducing (H := H) S K → IsInvariant T K ∧ IsInvariant T Kᗮ -lemma mem_cyclicSubspace_of_preservesReducingSubspaces (A : StarSubalgebra ℂ B) {T : B} +/-- If `A` acts non-degenerately, then `x` lies in its own cyclic subspace. + +This replaces the appeal to `1 ∈ A` in the unital argument: the orthogonal projection `P` onto +the cyclic subspace lies in `A'`, so every `T ∈ A` kills `x - P x`, and non-degeneracy forces +`x = P x`. -/ +lemma mem_cyclicSubspace_of_actsNondegenerately (A : NonUnitalStarSubalgebra ℂ B) + (hA : ActsNondegenerately (A : Set B)) (x : H) : + x ∈ cyclicSubspace (H := H) A x := by + classical + let K : Submodule ℂ H := cyclicSubspace (H := H) A x + have hKclosed : IsClosed (K : Set H) := by + dsimp [K, cyclicSubspace] + exact + Submodule.isClosed_topologicalClosure + (s := Submodule.span ℂ (Set.range fun a : A => (a : B) x)) + letI : IsClosed (K : Set H) := hKclosed + letI : CompleteSpace K := (show CompleteSpace (↥(K : Set H)) from inferInstance) + letI : K.HasOrthogonalProjection := by infer_instance + have hRed : IsReducing (H := H) (A : Set B) K := isReducing_cyclicSubspace (H := H) A x + have hP : K.starProjection ∈ Set.centralizer (A : Set B) := + starProjection_mem_centralizer_of_isReducing (S := (A : Set B)) (K := K) hRed + -- Every `T ∈ A` annihilates `x - P x`. + have hker : ∀ T ∈ (A : Set B), T (x - K.starProjection x) = 0 := by + intro T hT + have hTx : T x ∈ K := (subset_cyclicSubspace (H := H) A x) ⟨⟨T, hT⟩, rfl⟩ + have hcomm : T * K.starProjection = K.starProjection * T := hP T hT + have h1 : T (K.starProjection x) = K.starProjection (T x) := by + simpa [ContinuousLinearMap.mul_apply] using congrArg (fun f => f x) hcomm + have h2 : K.starProjection (T x) = T x := + (Submodule.starProjection_eq_self_iff (K := K)).2 hTx + simp [map_sub, h1, h2] + -- Non-degeneracy forces `x = P x ∈ K`. + have hx0 : x = K.starProjection x := sub_eq_zero.mp (hA _ hker) + have hPx : K.starProjection x ∈ K := Submodule.starProjection_apply_mem (U := K) x + change x ∈ K + rw [hx0] + exact hPx + +/-- If `A` acts non-degenerately and `T` preserves all `A`-reducing subspaces, then `T` sends +`x` into the cyclic subspace generated by `A • x`. -/ +lemma mem_cyclicSubspace_of_preservesReducingSubspaces (A : NonUnitalStarSubalgebra ℂ B) + (hA : ActsNondegenerately (A : Set B)) {T : B} (hT : PreservesReducingSubspaces (H := H) (A : Set B) T) (x : H) : T x ∈ cyclicSubspace (H := H) A x := by classical @@ -183,7 +257,7 @@ lemma mem_cyclicSubspace_of_preservesReducingSubspaces (A : StarSubalgebra ℂ B letI : IsClosed (K : Set H) := hKclosed letI : CompleteSpace K := (show CompleteSpace (↥(K : Set H)) from inferInstance) letI : K.HasOrthogonalProjection := by infer_instance - have hx : x ∈ K := mem_cyclicSubspace (H := H) A x + have hx : x ∈ K := mem_cyclicSubspace_of_actsNondegenerately (H := H) A hA x have hRed : IsReducing (H := H) (A : Set B) K := isReducing_cyclicSubspace (H := H) A x have hInv : IsInvariant T K := (hT K hRed).1 exact (IsInvariant.iff_forall_mem (T := T) (K := K)).1 hInv x hx @@ -220,11 +294,14 @@ lemma centralizer_centralizer_subset_reducingHull (S : Set (B)) : intro T hT exact preservesReducingSubspaces_of_mem_centralizer_centralizer (H := H) (S := S) hT -/-- Hard direction of the double commutant theorem: a WOT-closed `*-`subalgebra is a double commutant. +/-- Hard direction of the double commutant theorem: a WOT-closed (possibly non-unital) +`*-`subalgebra acting non-degenerately equals its own double commutant. -This is stated as a set equality of commutants. +This is stated as a set equality of commutants. For the unital special case (where +non-degeneracy is automatic) see `doubleCommutant_eq_of_isWOTClosed_starSubalgebra`. -/ -theorem doubleCommutant_eq_of_isWOTClosed (A : StarSubalgebra ℂ B) +theorem doubleCommutant_eq_of_isWOTClosed (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) (hA : IsWOTClosed (H := H) (A : Set B)) : Set.centralizer (Set.centralizer (A : Set B)) = (A : Set B) := by classical @@ -271,22 +348,28 @@ theorem doubleCommutant_eq_of_isWOTClosed (A : StarSubalgebra ℂ B) (starRingEnd ℂ (coeff (e i))) • (InnerProductSpace.toDual ℂ H).symm ((e i).1.2) let x : Hn (H := H) n := (WithLp.equiv _ _).symm xFun let u : Hn (H := H) n := (WithLp.equiv _ _).symm uFun - let Aamp : StarSubalgebra ℂ (Hn (H := H) n →L[ℂ] Hn (H := H) n) := + let Aamp : NonUnitalStarSubalgebra ℂ (Hn (H := H) n →L[ℂ] Hn (H := H) n) := A.map (diagonalStarAlgHom (H := H) n) + have hndAmp : ActsNondegenerately (Aamp : Set (Hn (H := H) n →L[ℂ] Hn (H := H) n)) := by + have hcoe : (Aamp : Set (Hn (H := H) n →L[ℂ] Hn (H := H) n)) = + diagonal (H := H) (n := n) '' (A : Set B) := by + simp [Aamp, diagonalStarAlgHom] + rw [hcoe] + exact actsNondegenerately_image_diagonal (H := H) hnd n let g : (Hn (H := H) n) →L[ℂ] ℂ := (InnerProductSpace.toDual ℂ (Hn (H := H) n)) u have h_eval (S : B) : f ((toWOTEquiv (H := H)) S) = g (diagonal (H := H) (n := n) S x) := by classical have hg : g (diagonal (H := H) (n := n) S x) = - ∑ i : Fin n, @inner ℂ H _ (uFun i) (S (xFun i)) := by + ∑ i : Fin n, ⟪uFun i, S (xFun i)⟫_ℂ := by simp only [g, u, x] rw [InnerProductSpace.toDual_apply_apply, PiLp.inner_apply] congr 1 funext i simp only [diagonal_apply, uFun, xFun, WithLp.equiv_symm_apply] have hf : f ((toWOTEquiv (H := H)) S) = - ∑ i : Fin n, @inner ℂ H _ (uFun i) (S (xFun i)) := by + ∑ i : Fin n, ⟪uFun i, S (xFun i)⟫_ℂ := by classical have hsum : (∑ j : (↥I), (coeff j) • ((inducingFnRestrictCLM (H := H) I) ((toWOTEquiv (H := H)) S) j)) = @@ -314,7 +397,7 @@ theorem doubleCommutant_eq_of_isWOTClosed (A : StarSubalgebra ℂ B) simp only [toWOTEquiv, ContinuousLinearMap.toWOT_apply] conv_lhs => rw [show (e i).1.2 (S (e i).1.1) = - @inner ℂ H _ ((InnerProductSpace.toDual ℂ H).symm (e i).1.2) (S (e i).1.1) from + ⟪(InnerProductSpace.toDual ℂ H).symm (e i).1.2, S (e i).1.1⟫_ℂ from InnerProductSpace.toDual_symm_apply.symm] simp only [uFun, xFun, inner_smul_left, starRingEnd_self_apply, smul_eq_mul] simpa [hg] using hf @@ -363,7 +446,7 @@ theorem doubleCommutant_eq_of_isWOTClosed (A : StarSubalgebra ℂ B) -- The diagonal embedding of `T` lies in the double commutant of `Aamp`. have hdiag_mem : diagonal (H := H) (n := n) T ∈ - Set.centralizer (Set.centralizer ((Aamp : StarSubalgebra ℂ _ ) : Set _)) := by + Set.centralizer (Set.centralizer ((Aamp : NonUnitalStarSubalgebra ℂ _ ) : Set _)) := by have hT' : T ∈ Set.centralizer (Set.centralizer (A : Set B)) := hT have hdiag : diagonal (H := H) (n := n) T ∈ (diagonal (H := H) (n := n) '' (A : Set B)).centralizer.centralizer := @@ -386,6 +469,7 @@ theorem doubleCommutant_eq_of_isWOTClosed (A : StarSubalgebra ℂ B) mem_cyclicSubspace_of_preservesReducingSubspaces (H := Hn (H := H) n) (A := Aamp) + hndAmp (T := diagonal (H := H) (n := n) T) hPres x -- Conclude: `f(T) = 0` since `g` vanishes on the cyclic subspace containing `diagonal T x`. @@ -396,4 +480,26 @@ theorem doubleCommutant_eq_of_isWOTClosed (A : StarSubalgebra ℂ B) exact hneq (by simp only [TWOT, f] at this ⊢; exact this) · exact Set.subset_centralizer_centralizer +/-- **A non-degenerate WOT-closed `*`-subalgebra automatically contains `1`.** This is the exact +extra content of the non-degenerate form over the unital one: granted, the non-degenerate form +implies the unital one, and conversely — every commutant contains `1`, so `A = A''` forces +`1 ∈ A`. -/ +theorem one_mem_of_isWOTClosed (A : NonUnitalStarSubalgebra ℂ B) + (hnd : ActsNondegenerately (A : Set B)) + (hA : IsWOTClosed (H := H) (A : Set B)) : + (1 : B) ∈ A := by + have h : (1 : B) ∈ (A : Set B) := by + rw [← doubleCommutant_eq_of_isWOTClosed A hnd hA] + exact Set.one_mem_centralizer + exact h + +/-- Unital special case of `doubleCommutant_eq_of_isWOTClosed`: a WOT-closed unital +`*-`subalgebra equals its own double commutant. Since `1 ∈ A`, the algebra acts +non-degenerately and the general theorem applies. -/ +theorem doubleCommutant_eq_of_isWOTClosed_starSubalgebra (A : StarSubalgebra ℂ B) + (hA : IsWOTClosed (H := H) (A : Set B)) : + Set.centralizer (Set.centralizer (A : Set B)) = (A : Set B) := + doubleCommutant_eq_of_isWOTClosed A.toNonUnitalStarSubalgebra + (actsNondegenerately_of_one_mem A.one_mem) hA + end WOTClosedSubalgebra diff --git a/QuantumSystem/Algebra/VonNeumannAlgebra/Basic.lean b/QuantumSystem/Algebra/VonNeumannAlgebra/Basic.lean new file mode 100644 index 0000000..f7ba3ca --- /dev/null +++ b/QuantumSystem/Algebra/VonNeumannAlgebra/Basic.lean @@ -0,0 +1,599 @@ +module + +public import Mathlib.Analysis.VonNeumannAlgebra.Basic +public import QuantumSystem.ForMathlib.Algebra.Star.PartialIsometry + +/-! +# Basic theory of von Neumann factors and the comparison of projections + +This file sets up the basic vocabulary of the comparison theory of projections in a von Neumann +algebra, the foundation of the type classification (and, downstream, of the type I factor +structure theorem). It also supplies `𝓑(H)`, the von Neumann algebra of *all* bounded operators +— the object the literature writes `B(H)` — which Mathlib's `VonNeumannAlgebra H` does not provide +as a distinguished element. + +The file is organised in three parts: + +1. **Factors, minimal projections and Murray–von Neumann equivalence** — the basic definitions + `IsFactor`, `IsMinimalProjection`, `IsAbelianProjection` and the equivalence relation + `p ∼[N] q`. +2. **Central projections** — projections in the centre `N ∩ N'`; in a factor these are trivial, + the seed of the central-support lemma `IsFactor.exists_mul_ne`. +3. **Comparison of projections** — the calculus of partial isometries, the subordination relation + `p ≼[N] q`, and the comparison theorem for minimal projections + (`IsMinimalProjection.mvNSub_of_isFactor`). + +## Main definitions + +* `VonNeumannAlgebra.boundedLinearOperators H` — the algebra of all bounded operators, with carrier + `Set.univ`, denoted `𝓑(H)` (`VonNeumannAlgebra.coe_boundedLinearOperators` / + `VonNeumannAlgebra.mem_boundedLinearOperators`). +* `VonNeumannAlgebra.boundedLinearOperators.starAlgEquiv` — the canonical `⋆`-isomorphism + `𝓑(H) ≃⋆ₐ[ℂ] (H →L[ℂ] H)` identifying the bundled von Neumann algebra with the operator type + (the `⋆`-algebra analogue of `Subalgebra.topEquiv`). +* `VonNeumannAlgebra.IsFactor N` — `N` has trivial centre: every element of `N ∩ N'` is a scalar. +* `VonNeumannAlgebra.IsMinimalProjection N e` — `e` is a nonzero star projection in `N` with + trivial corner `e N e = ℂ e`. This implies the order-theoretic minimality (no proper nonzero + subprojection in `N`, expressed algebraically as: any projection `f ∈ N` with `e * f = f`, i.e. + the Loewner relation `f ≤ e`, is `0` or `e`), recorded as + `IsMinimalProjection.no_proper_subprojection`. +* `VonNeumannAlgebra.IsAbelianProjection N p` — `p` is a star projection in `N` with commutative + corner `p N p`. +* `VonNeumannAlgebra.MvNEquiv N p q` — `p` and `q` are Murray–von Neumann equivalent inside `N`: + there is a partial isometry `v ∈ N` with source `v⋆v = p` and range `vv⋆ = q`. Written `p ∼[N] q`. +* `VonNeumannAlgebra.IsCentralProjection N e` — `e` is a projection in `N ∩ N'`. +* `VonNeumannAlgebra.MvNSub N p q` — `p ≼[N] q`: `p` is Murray–von Neumann equivalent to a + subprojection of `q`. + +## Main results + +* `VonNeumannAlgebra.MvNEquiv.refl` / `symm` / `trans` — Murray–von Neumann equivalence is an + equivalence relation on the projections of `N`. +* `VonNeumannAlgebra.IsFactor.central_projection_eq` — in a factor every central projection is + `0` or `1`. +* `VonNeumannAlgebra.isStarProjection_mem_commutant_iff` — a star projection lies in the commutant + `N'` exactly when its range is invariant under every element of `N` (the projection–reducing + subspace bridge used to build central supports). +* `VonNeumannAlgebra.IsFactor.exists_mul_ne` — the central-support lemma: in a factor, a nonzero + `q` meets the `N`-orbit of any nonzero `e ∈ N`. +* `VonNeumannAlgebra.MvNSub.refl` / `MvNSub.trans` — subordination is a preorder on projections. +* `VonNeumannAlgebra.mvNSub_of_posCorner` — the scaling step of the comparison theorem: a positive + scalar corner `(q a e)⋆(q a e) = c • e`, `c > 0`, yields `e ≼[N] q`. +* `VonNeumannAlgebra.IsMinimalProjection.mvNSub_of_isFactor` — the comparison theorem for minimal + projections: in a factor, a minimal projection is subordinate to every nonzero projection. +* `VonNeumannAlgebra.eq_boundedLinearOperators_complex` — on the one-dimensional Hilbert space + `𝓑(ℂ)` is the *only* von Neumann algebra, every bounded operator on `ℂ` being a scalar + (`VonNeumannAlgebra.apply_eq_mul_apply_one`, `VonNeumannAlgebra.mul_comm_complex`). This is what + makes one-dimensional models of downstream properties both easy and uninformative. + +The full comparison theorem (any two projections in a factor are comparable) requires central +supports and polar decomposition for general projections and is not developed here; the +minimal-projection case above is the form the type I structure theorem needs. + +## Notation + +The symbols of the operator-algebra literature live in the opt-in `VonNeumannAlgebra` scope; +activate them with `open scoped VonNeumannAlgebra`. + +| Symbol | Expansion | How to activate | +|---|---|---| +| `p ∼[N] q` | `VonNeumannAlgebra.MvNEquiv N p q` | `open scoped VonNeumannAlgebra` | +| `p ≼[N] q` | `VonNeumannAlgebra.MvNSub N p q` | `open scoped VonNeumannAlgebra` | +| `𝓑(H)` | `VonNeumannAlgebra.boundedLinearOperators H` | `open scoped VonNeumannAlgebra` | + +The `𝓑(H)` glyph overloads the type-level notation `𝓑(H) = H →L[ℂ] H` of +`ForMathlib.Analysis.CStarAlgebra.HilbertSpace`; the two denote the same object B(H) at different +levels and are related by `boundedLinearOperators.starAlgEquiv`. The expected type disambiguates. +-/ + +@[expose] public section + +namespace VonNeumannAlgebra + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-! ### The full algebra `𝓑(H)` of all bounded operators -/ + +/-- The von Neumann algebra `𝓑(H)` of **all bounded linear operators** on `H` — the object the +literature writes `B(H)`. Its carrier is `Set.univ`; the double-commutant property is the +bicommutant inclusion `s ⊆ s''` applied to `s = univ`, together with `univ` being the largest set. +Mathlib's bundled `VonNeumannAlgebra H` provides no such distinguished element, so this file +supplies it. -/ +noncomputable def boundedLinearOperators (H : Type*) [NormedAddCommGroup H] + [InnerProductSpace ℂ H] [CompleteSpace H] : VonNeumannAlgebra H where + toStarSubalgebra := ⊤ + centralizer_centralizer' := + Set.Subset.antisymm (Set.subset_univ _) Set.subset_centralizer_centralizer + +/-- `𝓑(H)` denotes the von Neumann algebra of all bounded operators on `H` +(`VonNeumannAlgebra.boundedLinearOperators H`). This overloads the type-level notation +`𝓑(H) = H →L[ℂ] H` of `ForMathlib.Analysis.CStarAlgebra.HilbertSpace`: the two denote the same +mathematical object B(H) at different levels (the operator *type* vs. the bundled *von Neumann +algebra* of all operators), and the expected type disambiguates. The two levels are related by the +canonical `⋆`-isomorphism `boundedLinearOperators.starAlgEquiv`. -/ +scoped notation:max "𝓑(" H ")" => VonNeumannAlgebra.boundedLinearOperators H + +/-- The carrier of `𝓑(H)` is all of `H →L[ℂ] H`. -/ +@[simp] lemma coe_boundedLinearOperators : + ((𝓑(H) : VonNeumannAlgebra H) : Set (H →L[ℂ] H)) = Set.univ := rfl + +/-- Every bounded operator lies in `𝓑(H)`. -/ +@[simp] lemma mem_boundedLinearOperators (x : H →L[ℂ] H) : x ∈ (𝓑(H) : VonNeumannAlgebra H) := + Set.mem_univ x + +/-- **The two levels of `𝓑(H)` agree.** The underlying `⋆`-subalgebra of the bundled von Neumann +algebra `𝓑(H)`, coerced to a type, is canonically `⋆`-isomorphic to the operator type +`H →L[ℂ] H` (itself the type-level `𝓑(H)` of `ForMathlib.Analysis.CStarAlgebra.HilbertSpace`). +This is the `⋆`-algebra analogue of `Subalgebra.topEquiv` / `Submodule.topEquiv`, making explicit +that the notation overload denotes one and the same object B(H). The equivalence is phrased on +`(𝓑(H)).toStarSubalgebra` because Mathlib equips the `⋆`-subalgebra — not the bundled +`VonNeumannAlgebra` — with the `ℂ`-algebra structure. -/ +noncomputable def boundedLinearOperators.starAlgEquiv : + (𝓑(H) : VonNeumannAlgebra H).toStarSubalgebra ≃⋆ₐ[ℂ] (H →L[ℂ] H) := + StarAlgEquiv.ofStarAlgHom + (𝓑(H) : VonNeumannAlgebra H).toStarSubalgebra.subtype + ({ toFun := fun x => ⟨x, StarSubalgebra.mem_top⟩ + map_one' := rfl + map_mul' := fun _ _ => rfl + map_zero' := rfl + map_add' := fun _ _ => rfl + commutes' := fun _ => rfl + map_star' := fun _ => rfl } : + (H →L[ℂ] H) →⋆ₐ[ℂ] (𝓑(H) : VonNeumannAlgebra H).toStarSubalgebra) + (fun _ => rfl) (fun _ => rfl) + +/-- A von Neumann algebra is closed under scalar multiplication. -/ +lemma smul_mem {N : VonNeumannAlgebra H} (c : ℂ) {x : H →L[ℂ] H} (hx : x ∈ N) : c • x ∈ N := by + rw [Algebra.smul_def] + exact mul_mem (algebraMap_mem N.toStarSubalgebra c) hx + +/-! ### The one-dimensional case + +On `ℂ` every bounded operator is a scalar, so there is exactly one von Neumann algebra. This is +what makes one-dimensional witnesses of net- and inclusion-level properties go through without +computing any generated algebra — and, read the other way, what makes them evidence of +inhabitation only: on `ℂ` no algebra, no net and no representation is distinguished from any +other. +-/ + +/-- A bounded operator on `ℂ` is multiplication by its value at `1`. -/ +lemma apply_eq_mul_apply_one (x : ℂ →L[ℂ] ℂ) (w : ℂ) : x w = w * x 1 := by + rw [← smul_eq_mul, ← map_smul, smul_eq_mul, mul_one] + +/-- **Bounded operators on `ℂ` commute.** Each is multiplication by a scalar +(`apply_eq_mul_apply_one`), and scalars commute. -/ +lemma mul_comm_complex (x y : ℂ →L[ℂ] ℂ) : x * y = y * x := by + refine ContinuousLinearMap.ext fun z => ?_ + rw [ContinuousLinearMap.mul_apply, ContinuousLinearMap.mul_apply, + apply_eq_mul_apply_one x (y z), apply_eq_mul_apply_one y (x z), + apply_eq_mul_apply_one y z, apply_eq_mul_apply_one x z] + ring + +/-- **On `ℂ` there is only one von Neumann algebra.** Every von Neumann algebra on the +one-dimensional Hilbert space is `𝓑(ℂ)`: it contains `1` and is closed under scalars, while +every bounded operator on `ℂ` is a scalar multiple of `1` (`apply_eq_mul_apply_one`). -/ +lemma eq_boundedLinearOperators_complex (N : VonNeumannAlgebra ℂ) : N = 𝓑(ℂ) := by + refine SetLike.ext fun x => ⟨fun _ => mem_boundedLinearOperators x, fun _ => ?_⟩ + have hx : x = (x 1) • (1 : ℂ →L[ℂ] ℂ) := by + refine ContinuousLinearMap.ext fun z => ?_ + rw [ContinuousLinearMap.smul_apply, ContinuousLinearMap.one_apply, smul_eq_mul, + apply_eq_mul_apply_one x z, mul_comm] + rw [hx] + exact smul_mem _ (one_mem N) + +/-! ### Factors, minimal projections and Murray–von Neumann equivalence -/ + +/-- A von Neumann algebra `N` is a **factor** when its centre is trivial: every operator lying in +both `N` and its commutant is a scalar multiple of the identity. -/ +def IsFactor (N : VonNeumannAlgebra H) : Prop := + ∀ x : H →L[ℂ] H, x ∈ N → x ∈ N.commutant → ∃ c : ℂ, x = c • 1 + +/-- A **minimal projection** of `N`: a nonzero star projection `e ∈ N` whose corner is trivial, +`e N e = ℂ e`. This is the conventional operator-algebraic definition (Takesaki, Kadison–Ringrose); +it implies minimality in the order sense (no proper nonzero subprojection), recorded as +`IsMinimalProjection.no_proper_subprojection`. The corner formulation is the one that +supports comparison theory without invoking Borel functional calculus. -/ +def IsMinimalProjection (N : VonNeumannAlgebra H) (e : H →L[ℂ] H) : Prop := + IsStarProjection e ∧ e ∈ N ∧ e ≠ 0 ∧ ∀ a ∈ N, ∃ c : ℂ, e * a * e = c • e + +omit [CompleteSpace H] in +/-- **A nonzero operator witnesses a nonzero space.** On a subsingleton `H` every operator is `0`, +so exhibiting any `x ≠ 0` already gives `Nontrivial H`. This is why almost none of the results +below need `[Nontrivial H]` as a hypothesis: they carry a nonzero projection, which supplies it. +The exceptions are the statements that quantify over projections without asserting one exists +(`IsFactor.isTypeI_iff_exists_isMinimalProjection`, `isTypeIFactor_iff_isFactor_and_isTypeI`) and +the ones producing a minimal projection of `𝓑(H)` out of nothing. -/ +lemma nontrivial_of_ne_zero {x : H →L[ℂ] H} (hx : x ≠ 0) : Nontrivial H := + let ⟨y, hy⟩ := ContinuousLinearMap.exists_ne_zero hx + ⟨⟨x y, 0, hy⟩⟩ + +/-- A von Neumann algebra with a minimal projection acts on a nonzero space: the minimal +projection is nonzero, so it sends some vector to a nonzero vector, witnessing `Nontrivial H`. -/ +lemma IsMinimalProjection.nontrivial {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + (he : IsMinimalProjection N e) : Nontrivial H := + nontrivial_of_ne_zero he.2.2.1 + +/-- A minimal projection has no proper nonzero subprojection in `N`: if a projection `f ∈ N` +satisfies `f ≤ e` (the Loewner order on projections, equivalently the range inclusion +`ran f ⊆ ran e`, written algebraically as `e * f = f`), then `f = 0` or `f = e`. This recovers the +order-theoretic form of minimality from the corner definition `e N e = ℂ e`. -/ +lemma IsMinimalProjection.no_proper_subprojection {N : VonNeumannAlgebra H} + {e : H →L[ℂ] H} (he : IsMinimalProjection N e) + {f : H →L[ℂ] H} (hf : IsStarProjection f) (hfN : f ∈ N) (hsub : e * f = f) : + f = 0 ∨ f = e := by + have hfe : f * e = f := by + have := congrArg star hsub + rwa [star_mul, he.1.isSelfAdjoint.star_eq, hf.isSelfAdjoint.star_eq] at this + have hefe : e * f * e = f := by rw [hsub, hfe] + obtain ⟨c, hc⟩ := he.2.2.2 f hfN + rw [hefe] at hc + have hidem : f * f = f := hf.isIdempotentElem + rw [hc] at hidem + have h2 : (c • e) * (c • e) = (c * c) • (e : H →L[ℂ] H) := by + rw [smul_mul_smul_comm, he.1.isIdempotentElem] + have hcc : (c * c) • (e : H →L[ℂ] H) = c • e := by rw [← h2, hidem] + have hc2 : c * c = c := smul_left_injective ℂ he.2.2.1 hcc + have h0 : c * (c - 1) = 0 := by rw [mul_sub, mul_one, hc2, sub_self] + rcases mul_eq_zero.mp h0 with h | h + · exact Or.inl (by rw [hc, h, zero_smul]) + · exact Or.inr (by rw [hc, sub_eq_zero.mp h, one_smul]) + +/-- An **abelian projection** of `N`: a star projection `p ∈ N` whose corner `p N p` is +commutative. Minimal projections are abelian (`IsMinimalProjection.isAbelianProjection`); the +general type I property (`IsTypeI`) is phrased through abelian projections. -/ +def IsAbelianProjection (N : VonNeumannAlgebra H) (p : H →L[ℂ] H) : Prop := + IsStarProjection p ∧ p ∈ N ∧ + ∀ a ∈ N, ∀ b ∈ N, (p * a * p) * (p * b * p) = (p * b * p) * (p * a * p) + +/-- A minimal projection is abelian: its corner `e N e = ℂ e` is one-dimensional, hence +commutative. -/ +lemma IsMinimalProjection.isAbelianProjection {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + (he : IsMinimalProjection N e) : IsAbelianProjection N e := by + refine ⟨he.1, he.2.1, fun a haN b hbN => ?_⟩ + obtain ⟨c, hc⟩ := he.2.2.2 a haN + obtain ⟨d, hd⟩ := he.2.2.2 b hbN + rw [hc, hd, smul_mul_smul_comm, smul_mul_smul_comm, mul_comm c d] + +/-- **Murray–von Neumann equivalence** of projections inside `N`: there is a partial isometry +`v ∈ N` with source projection `v⋆v = p` and range projection `vv⋆ = q`. -/ +def MvNEquiv (N : VonNeumannAlgebra H) (p q : H →L[ℂ] H) : Prop := + ∃ v : H →L[ℂ] H, v ∈ N ∧ IsPartialIsometry v ∧ star v * v = p ∧ v * star v = q + +/-- `p ∼[N] q` denotes Murray–von Neumann equivalence `MvNEquiv N p q` of projections inside `N`. -/ +scoped notation:50 p:51 " ∼[" N "] " q:51 => MvNEquiv N p q + +/-- Murray–von Neumann equivalence is reflexive on projections of `N`. -/ +theorem MvNEquiv.refl {N : VonNeumannAlgebra H} {p : H →L[ℂ] H} + (hp : IsStarProjection p) (hpN : p ∈ N) : p ∼[N] p := + ⟨p, hpN, hp.isPartialIsometry, by rw [hp.isSelfAdjoint.star_eq, hp.isIdempotentElem.eq], + by rw [hp.isSelfAdjoint.star_eq, hp.isIdempotentElem.eq]⟩ + +/-- Murray–von Neumann equivalence is symmetric. -/ +theorem MvNEquiv.symm {N : VonNeumannAlgebra H} {p q : H →L[ℂ] H} + (h : p ∼[N] q) : q ∼[N] p := by + obtain ⟨v, hv, hpi, hvp, hvq⟩ := h + exact ⟨star v, star_mem hv, IsPartialIsometry.star hpi, by rw [star_star, hvq], + by rw [star_star, hvp]⟩ + +/-- Murray–von Neumann equivalence is transitive. -/ +theorem MvNEquiv.trans {N : VonNeumannAlgebra H} {p q r : H →L[ℂ] H} + (hpq : p ∼[N] q) (hqr : q ∼[N] r) : p ∼[N] r := by + obtain ⟨v, hv, hvpi, hvp, hvq⟩ := hpq + obtain ⟨w, hw, hwpi, hwq, hwr⟩ := hqr + have hq : IsStarProjection q := hvq ▸ hvpi.isStarProjection_mul_star_self + refine ⟨w * v, mul_mem hw hv, ?_, ?_, ?_⟩ + · unfold IsPartialIsometry + calc w * v * star (w * v) * (w * v) + = w * (v * star v) * (star w * w) * v := by simp only [star_mul, mul_assoc] + _ = w * q * q * v := by rw [hvq, hwq] + _ = w * q * v := by rw [mul_assoc w q q, hq.isIdempotentElem] + _ = w * (star w * w) * v := by rw [hwq] + _ = w * v := by rw [← mul_assoc w (star w) w, hwpi] + · calc star (w * v) * (w * v) = star v * (star w * w) * v := by simp only [star_mul, mul_assoc] + _ = star v * (v * star v) * v := by rw [hwq, hvq] + _ = (star v * v) * (star v * v) := by simp only [mul_assoc] + _ = star v * v := hvpi.isStarProjection_star_mul_self.isIdempotentElem + _ = p := hvp + · calc (w * v) * star (w * v) = w * (v * star v) * star w := by simp only [star_mul, mul_assoc] + _ = w * (star w * w) * star w := by rw [hvq, ← hwq] + _ = (w * star w) * (w * star w) := by simp only [mul_assoc] + _ = w * star w := hwpi.isStarProjection_mul_star_self.isIdempotentElem + _ = r := hwr + +/-! ### Central projections -/ + +/-- A **central projection** of `N`: a star projection lying in both `N` and its commutant +(equivalently, in the centre `N ∩ N'`). -/ +def IsCentralProjection (N : VonNeumannAlgebra H) (e : H →L[ℂ] H) : Prop := + IsStarProjection e ∧ e ∈ N ∧ e ∈ N.commutant + +/-- `0` is a central projection. -/ +lemma isCentralProjection_zero (N : VonNeumannAlgebra H) : IsCentralProjection N 0 := + ⟨IsStarProjection.zero _, zero_mem _, zero_mem _⟩ + +/-- `1` is a central projection. -/ +lemma isCentralProjection_one (N : VonNeumannAlgebra H) : IsCentralProjection N 1 := + ⟨IsStarProjection.one _, one_mem _, one_mem _⟩ + +/-- In a factor, every central projection is trivial: it is `0` or `1`. This is the +projection-level form of the triviality of the centre. On a subsingleton `H` the conclusion is +vacuous (every operator is `0 = 1`), which is why no nontriviality hypothesis is needed. -/ +theorem IsFactor.central_projection_eq {N : VonNeumannAlgebra H} + (hN : IsFactor N) {e : H →L[ℂ] H} (he : IsCentralProjection N e) : + e = 0 ∨ e = 1 := by + rcases subsingleton_or_nontrivial H with hH | hH + · haveI := hH + exact Or.inl (Subsingleton.elim _ _) + obtain ⟨c, hc⟩ := hN e he.2.1 he.2.2 + have hidem : e * e = e := he.1.isIdempotentElem + rw [hc] at hidem + have hcc : (c * c) • (1 : H →L[ℂ] H) = c • 1 := by + rw [← hidem, smul_mul_smul_comm, mul_one] + have hc2 : c * c = c := smul_left_injective ℂ one_ne_zero hcc + have h0 : c * (c - 1) = 0 := by rw [mul_sub, mul_one, hc2, sub_self] + rcases mul_eq_zero.mp h0 with h | h + · exact Or.inl (by rw [hc, h, zero_smul]) + · exact Or.inr (by rw [hc, sub_eq_zero.mp h, one_smul]) + +/-- A star projection lies in the commutant `N'` exactly when its range is invariant under every +element of `N`. This is the bridge between reducing subspaces and (eventually central) +projections: it follows from the double-commutant characterisation +`VonNeumannAlgebra.IsStarProjection.mem_iff` together with `commutant_commutant`. -/ +theorem isStarProjection_mem_commutant_iff {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + (he : IsStarProjection e) : + e ∈ N.commutant ↔ ∀ y ∈ N, (e.range) ∈ Module.End.invtSubmodule (y : Module.End ℂ H) := by + rw [IsStarProjection.mem_iff he N.commutant, commutant_commutant] + +/-- **Central support (factor case).** In a factor, a nonzero operator `q` meets the +"`N`-orbit" of any nonzero `e ∈ N`: there is `a ∈ N` with `q a e ≠ 0`. The statement needs no +projection hypothesis on `e` or `q`, only `e ∈ N`, `e ≠ 0` and `q ≠ 0`. The proof takes the +orthogonal projection `P` onto the closed `N`-invariant subspace generated by `e H`; `P` reduces +both `N` and `N'`, so it is central, hence `0` or `1`; since `P` acts as the identity on `e H` +(so `P ≠ 0`, as `e ≠ 0`), `P = 1`, so the generated subspace is the whole space and `q` cannot +annihilate it. This is the geometric input of the comparison theorem. -/ +theorem IsFactor.exists_mul_ne {N : VonNeumannAlgebra H} (hN : IsFactor N) + {e q : H →L[ℂ] H} (heN : e ∈ N) (he0 : e ≠ 0) (hq0 : q ≠ 0) : ∃ a ∈ N, q * a * e ≠ 0 := by + haveI : Nontrivial H := nontrivial_of_ne_zero he0 + by_contra hcon + have hcon' : ∀ a ∈ N, q * a * e = 0 := fun a haN => by + by_contra h; exact hcon ⟨a, haN, h⟩ + set S : Set H := {y | ∃ a ∈ N, ∃ x, (a : H →L[ℂ] H) (e x) = y} with hS + set M : Submodule ℂ H := (Submodule.span ℂ S).topologicalClosure with hM + set P : H →L[ℂ] H := M.starProjection with hP + have hPproj : IsStarProjection P := isStarProjection_starProjection + have hSsub : S ⊆ M := Submodule.subset_span.trans (Submodule.le_topologicalClosure _) + have hSmem : ∀ (a : H →L[ℂ] H), a ∈ N → ∀ x, (a : H →L[ℂ] H) (e x) ∈ M := + fun a haN x => hSsub ⟨a, haN, x, rfl⟩ + have hinv : ∀ (y : H →L[ℂ] H), (∀ s ∈ S, y s ∈ M) → + P.range ∈ Module.End.invtSubmodule (y : Module.End ℂ H) := by + intro y hyS + have hcomapclosed : IsClosed ((M.comap (y : H →ₗ[ℂ] H)) : Set H) := by + rw [Submodule.comap_coe] + exact ((Submodule.span ℂ S).isClosed_topologicalClosure).preimage y.continuous + have hle : M ≤ M.comap (y : H →ₗ[ℂ] H) := by + apply Submodule.topologicalClosure_minimal (Submodule.span ℂ S) _ hcomapclosed + rw [Submodule.span_le] + intro s hs + simp only [Submodule.comap_coe, Set.mem_preimage, SetLike.mem_coe] + exact hyS s hs + rw [hP, Submodule.range_starProjection]; exact hle + have hPcomm : P ∈ N.commutant := by + rw [isStarProjection_mem_commutant_iff hPproj] + intro y hyN + refine hinv y ?_ + rintro s ⟨a, haN, x, rfl⟩ + rw [show y ((a : H →L[ℂ] H) (e x)) = (y * a) (e x) from rfl] + exact hSmem (y * a) (mul_mem hyN haN) x + have hPN : P ∈ N := by + rw [IsStarProjection.mem_iff hPproj N] + intro y hyN' + refine hinv y ?_ + rintro s ⟨a, haN, x, rfl⟩ + have hay : a * y = y * a := mem_commutant_iff.mp hyN' a haN + have hey : e * y = y * e := mem_commutant_iff.mp hyN' e heN + have heq : y ((a : H →L[ℂ] H) (e x)) = (a : H →L[ℂ] H) (e (y x)) := by + rw [show y ((a : H →L[ℂ] H) (e x)) = (y * a) (e x) from rfl, ← hay] + rw [show (a * y) (e x) = (a : H →L[ℂ] H) (y (e x)) from rfl] + rw [show y (e x) = (y * e) x from rfl, ← hey]; rfl + rw [heq]; exact hSmem a haN (y x) + have hPcentral : IsCentralProjection N P := ⟨hPproj, hPN, hPcomm⟩ + have hP1 : P = 1 := by + rcases hN.central_projection_eq hPcentral with h0 | h1 + · exact absurd (by + ext x + have hpx : P (e x) = e x := by + rw [hP, Submodule.starProjection_eq_self_iff]; exact hSmem 1 (one_mem _) x + rw [h0] at hpx; simpa using hpx.symm : e = 0) he0 + · exact h1 + have hMtop : M = ⊤ := by + have hMr : M = P.range := (Submodule.range_starProjection M).symm + rw [hMr, hP1]; exact Submodule.eq_top_iff'.2 fun y => ⟨y, rfl⟩ + have hqM : M ≤ LinearMap.ker (q : H →ₗ[ℂ] H) := by + apply Submodule.topologicalClosure_minimal (Submodule.span ℂ S) _ q.isClosed_ker + rw [Submodule.span_le] + rintro s ⟨a, haN, x, rfl⟩ + simp only [SetLike.mem_coe, LinearMap.mem_ker, ContinuousLinearMap.coe_coe] + rw [show q ((a : H →L[ℂ] H) (e x)) = (q * a * e) x from rfl, hcon' a haN]; rfl + apply hq0 + ext x + have hx : x ∈ LinearMap.ker (q : H →ₗ[ℂ] H) := hqM (hMtop ▸ Submodule.mem_top) + simpa using hx + +/-! ### Comparison of projections -/ + +/-- The source projection of a Murray–von Neumann equivalence is a star projection. -/ +lemma MvNEquiv.isStarProjection_left {N : VonNeumannAlgebra H} {p q : H →L[ℂ] H} + (h : p ∼[N] q) : IsStarProjection p := by + obtain ⟨v, _, hpi, hvp, _⟩ := h; exact hvp ▸ hpi.isStarProjection_star_mul_self + +/-- The range projection of a Murray–von Neumann equivalence is a star projection. -/ +lemma MvNEquiv.isStarProjection_right {N : VonNeumannAlgebra H} {p q : H →L[ℂ] H} + (h : p ∼[N] q) : IsStarProjection q := by + obtain ⟨v, _, hpi, _, hvq⟩ := h; exact hvq ▸ hpi.isStarProjection_mul_star_self + +/-- For projections, the subprojection relation `e * f = f` is left/right symmetric. -/ +lemma isStarProjection_subproj_comm {R : Type*} [Ring R] [StarRing R] {e f : R} + (he : IsStarProjection e) (hf : IsStarProjection f) (h : e * f = f) : f * e = f := by + have := congrArg star h + rwa [star_mul, he.isSelfAdjoint.star_eq, hf.isSelfAdjoint.star_eq] at this + +/-- The source projection of a partial isometry acts as a right identity. -/ +lemma IsPartialIsometry.mul_source {R : Type*} [Monoid R] [StarMul R] {v : R} + (h : IsPartialIsometry v) : v * (star v * v) = v := by rw [← mul_assoc]; exact h + +/-- `p ≼ q` in `N`: `p` is Murray–von Neumann equivalent to a subprojection of `q`. -/ +def MvNSub (N : VonNeumannAlgebra H) (p q : H →L[ℂ] H) : Prop := + ∃ q' : H →L[ℂ] H, q' ∈ N ∧ q * q' = q' ∧ p ∼[N] q' + +/-- `p ≼[N] q` denotes the subordination relation `MvNSub N p q`: `p` is Murray–von Neumann +equivalent to a subprojection of `q` inside `N`. -/ +scoped notation:50 p:51 " ≼[" N "] " q:51 => MvNSub N p q + +/-- Subordination is reflexive on projections of `N`. -/ +lemma MvNSub.refl {N : VonNeumannAlgebra H} {p : H →L[ℂ] H} + (hp : IsStarProjection p) (hpN : p ∈ N) : p ≼[N] p := + ⟨p, hpN, hp.isIdempotentElem, MvNEquiv.refl hp hpN⟩ + +/-- An equivalence `q ∼[N] r'` transports a subprojection `q' ≤ q` to a subprojection of `r'` that +is Murray–von Neumann equivalent to `q'`. -/ +theorem MvNEquiv.exists_subproj_equiv {N : VonNeumannAlgebra H} {q r' q' : H →L[ℂ] H} + (hqr : q ∼[N] r') (hq' : IsStarProjection q') (hq'N : q' ∈ N) (hsub : q * q' = q') : + ∃ r'' : H →L[ℂ] H, IsStarProjection r'' ∧ r'' ∈ N ∧ r' * r'' = r'' ∧ q' ∼[N] r'' := by + obtain ⟨w, hwN, hwpi, hwq, hwr⟩ := hqr + refine ⟨w * q' * star w, ⟨?_, ?_⟩, mul_mem (mul_mem hwN hq'N) (star_mem hwN), ?_, ?_⟩ + · change (w * q' * star w) * (w * q' * star w) = w * q' * star w + simp only [mul_assoc] + rw [← mul_assoc (star w) w (q' * star w), hwq, ← mul_assoc q q' (star w), hsub, + ← mul_assoc q' q' (star w), hq'.isIdempotentElem] + · change star (w * q' * star w) = w * q' * star w + rw [star_mul, star_mul, star_star, hq'.isSelfAdjoint.star_eq, mul_assoc] + · rw [← hwr] + simp only [mul_assoc] + rw [← mul_assoc (star w) w (q' * star w), hwq, ← mul_assoc q q' (star w), hsub] + · refine ⟨w * q', mul_mem hwN hq'N, ?_, ?_, ?_⟩ + · change (w * q') * star (w * q') * (w * q') = w * q' + rw [star_mul, hq'.isSelfAdjoint.star_eq] + simp only [mul_assoc] + rw [← mul_assoc (star w) w q', hwq, hsub, hq'.isIdempotentElem, hq'.isIdempotentElem] + · change star (w * q') * (w * q') = q' + rw [star_mul, hq'.isSelfAdjoint.star_eq, mul_assoc, ← mul_assoc (star w) w q', hwq, hsub, + hq'.isIdempotentElem] + · change (w * q') * star (w * q') = w * q' * star w + rw [star_mul, hq'.isSelfAdjoint.star_eq] + simp only [mul_assoc] + rw [← mul_assoc q' q' (star w), hq'.isIdempotentElem] + +/-- Subordination is transitive: `≼` is a preorder on the projections of `N`. -/ +lemma MvNSub.trans {N : VonNeumannAlgebra H} {p q r : H →L[ℂ] H} + (hpq : p ≼[N] q) (hqr : q ≼[N] r) : p ≼[N] r := by + obtain ⟨q', hq'N, hqsub, hpq'⟩ := hpq + obtain ⟨r', hr'N, hrsub, hqr'⟩ := hqr + obtain ⟨r'', _, hr''N, hr'sub, hq'r''⟩ := + hqr'.exists_subproj_equiv hpq'.isStarProjection_right hq'N hqsub + refine ⟨r'', hr''N, ?_, hpq'.trans hq'r''⟩ + calc r * r'' = r * (r' * r'') := by rw [hr'sub] + _ = (r * r') * r'' := by rw [mul_assoc] + _ = r' * r'' := by rw [hrsub] + _ = r'' := hr'sub + +/-- **Scaling step of the comparison theorem.** If the positive corner element +`(q a e)⋆ (q a e)` equals a positive scalar multiple `c • e` of `e` (with `c > 0`), then `e` is +subordinate to `q`: the normalised element `(√c)⁻¹ • (q a e)` is a partial isometry with source +`e` and range a subprojection of `q`. The hypotheses isolate the two analytic inputs of the +comparison theorem — the corner being scalar (minimality) and its positivity. -/ +theorem mvNSub_of_posCorner {N : VonNeumannAlgebra H} {e q a : H →L[ℂ] H} + (he : IsStarProjection e) (hq : IsStarProjection q) + (heN : e ∈ N) (hqN : q ∈ N) (haN : a ∈ N) + {c : ℝ} (hc : 0 < c) + (hcorner : star (q * a * e) * (q * a * e) = (c : ℂ) • e) : + e ≼[N] q := by + set γ : ℂ := ((Real.sqrt c)⁻¹ : ℂ) with hγ + set v : H →L[ℂ] H := γ • (q * a * e) with hv + have hvN : v ∈ N := smul_mem γ (mul_mem (mul_mem hqN haN) heN) + have hsrc : star v * v = e := by + rw [hv, star_smul, smul_mul_smul_comm, hcorner, smul_smul] + have hstar : star γ = γ := by rw [hγ, star_inv₀, ← starRingEnd_apply, Complex.conj_ofReal] + rw [hstar, hγ, ← mul_inv, ← Complex.ofReal_mul, Real.mul_self_sqrt hc.le, + inv_mul_cancel₀ (by exact_mod_cast hc.ne'), one_smul] + have hve : v * e = v := by + rw [hv, smul_mul_assoc, mul_assoc (q * a) e e, he.isIdempotentElem] + have hvpi : IsPartialIsometry v := by + unfold IsPartialIsometry + rw [mul_assoc, hsrc, hve] + refine ⟨v * star v, mul_mem hvN (star_mem hvN), ?_, ⟨v, hvN, hvpi, hsrc, rfl⟩⟩ + have hqv : q * v = v := by + rw [hv, mul_smul_comm, ← mul_assoc, ← mul_assoc, hq.isIdempotentElem] + rw [← mul_assoc, hqv] + +/-- **Positivity of the corner scalar.** For a minimal projection `e` and `a ∈ N` with +`q a e ≠ 0`, the corner element `(q a e)⋆ (q a e) = e (a⋆ q a) e` equals a *strictly positive +real* scalar multiple of `e`. (Reality comes from self-adjointness; strict positivity from +evaluating on a nonzero vector of `e H` on which `q a e` does not vanish.) -/ +lemma IsMinimalProjection.posCorner {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + (he : IsMinimalProjection N e) {q a : H →L[ℂ] H} (hq : IsStarProjection q) + (hqN : q ∈ N) (haN : a ∈ N) (hne : q * a * e ≠ 0) : + ∃ c : ℝ, 0 < c ∧ star (q * a * e) * (q * a * e) = (c : ℂ) • e := by + set x := q * a * e with hx + have hxx : star x * x = e * (star a * q * a) * e := by + rw [hx, star_mul, star_mul, he.1.isSelfAdjoint.star_eq, hq.isSelfAdjoint.star_eq] + rw [mul_assoc, mul_assoc, mul_assoc, ← mul_assoc q q, hq.isIdempotentElem] + simp only [mul_assoc] + obtain ⟨c', hc'⟩ := he.2.2.2 (star a * q * a) (mul_mem (mul_mem (star_mem haN) hqN) haN) + rw [← hxx] at hc' + have hconj : (starRingEnd ℂ) c' = c' := by + have hsa : star (star x * x) = star x * x := by rw [star_mul, star_star] + rw [hc', star_smul, he.1.isSelfAdjoint.star_eq] at hsa + rw [starRingEnd_apply]; exact smul_left_injective ℂ he.2.2.1 hsa + have hxe : x * e = x := by rw [hx, mul_assoc, he.1.isIdempotentElem] + obtain ⟨η, hη⟩ : ∃ η, x η ≠ 0 := by + by_contra h + exact hne (by ext η; exact not_not.mp (not_exists.mp h η)) + set ξ := e η with hξ + have hxξ : x ξ ≠ 0 := by + rw [hξ, ← ContinuousLinearMap.comp_apply, ← ContinuousLinearMap.mul_def, hxe]; exact hη + have heξ : e ξ = ξ := by + rw [hξ, ← ContinuousLinearMap.comp_apply, ← ContinuousLinearMap.mul_def, he.1.isIdempotentElem] + have hξne : ξ ≠ 0 := fun h => hxξ (by rw [h, map_zero]) + have hinner : ‖x ξ‖ ^ 2 = c'.re * ‖ξ‖ ^ 2 := by + have e1 : inner ℂ ((star x * x) ξ) ξ = inner ℂ (x ξ) (x ξ) := by + rw [ContinuousLinearMap.mul_apply, ContinuousLinearMap.star_eq_adjoint, + ContinuousLinearMap.adjoint_inner_left] + have e2 : inner ℂ ((star x * x) ξ) ξ = (starRingEnd ℂ) c' * inner ℂ ξ ξ := by + rw [hc', ContinuousLinearMap.smul_apply, heξ, inner_smul_left] + have e3 : inner ℂ (x ξ) (x ξ) = (starRingEnd ℂ) c' * inner ℂ ξ ξ := e1.symm.trans e2 + have hre := congrArg RCLike.re e3 + rw [inner_self_eq_norm_sq, inner_self_eq_norm_sq_to_K] at hre + simp only [RCLike.mul_re, RCLike.mul_im, RCLike.conj_re, RCLike.conj_im, RCLike.ofReal_re, + RCLike.ofReal_im, pow_two, mul_zero, zero_mul, add_zero, sub_zero] at hre + rw [show RCLike.re c' = c'.re from rfl] at hre + nlinarith [hre] + have hξpos : 0 < ‖ξ‖ ^ 2 := pow_pos (norm_pos_iff.mpr hξne) 2 + have hxξpos : 0 < ‖x ξ‖ ^ 2 := pow_pos (norm_pos_iff.mpr hxξ) 2 + have hcre : c' = (c'.re : ℂ) := (Complex.conj_eq_iff_re.mp hconj).symm + refine ⟨c'.re, by nlinarith [hinner, hξpos, hxξpos], ?_⟩ + rw [hc']; exact congrArg (· • e) hcre + +/-- A minimal projection is subordinate to any projection it "meets": if some `a ∈ N` has +`q a e ≠ 0`, then `e ≼ q`. Combined with central supports (which guarantee `q a e ≠ 0` for every +nonzero `q` in a factor) this yields the comparison theorem `minimal e ≼ q`. -/ +lemma IsMinimalProjection.mvNSub_of_ne {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + (he : IsMinimalProjection N e) {q a : H →L[ℂ] H} (hq : IsStarProjection q) + (hqN : q ∈ N) (haN : a ∈ N) (hne : q * a * e ≠ 0) : e ≼[N] q := by + obtain ⟨c, hcpos, hcorner⟩ := he.posCorner hq hqN haN hne + exact mvNSub_of_posCorner he.1 hq he.2.1 hqN haN hcpos hcorner + +/-- **Comparison theorem (minimal projection case).** In a factor, a minimal projection `e` is +Murray–von Neumann subordinate to *every* nonzero projection `q`: `e ≼ q`. This combines the +scaling lemma (via `mvNSub_of_ne`) with the central-support input +(`IsFactor.exists_mul_ne`, which supplies an `a ∈ N` with `q a e ≠ 0`). It is the form of +comparison needed to show a maximal orthogonal family of minimal projections exhausts the +identity. -/ +theorem IsMinimalProjection.mvNSub_of_isFactor {N : VonNeumannAlgebra H} + (hN : IsFactor N) {e : H →L[ℂ] H} (he : IsMinimalProjection N e) + {q : H →L[ℂ] H} (hq : IsStarProjection q) (hqN : q ∈ N) (hq0 : q ≠ 0) : + e ≼[N] q := by + haveI : Nontrivial H := he.nontrivial + obtain ⟨a, haN, hane⟩ := hN.exists_mul_ne he.2.1 he.2.2.1 hq0 + exact he.mvNSub_of_ne hq hqN haN hane + +end VonNeumannAlgebra diff --git a/QuantumSystem/Algebra/VonNeumannAlgebra/SpatialDecomposition.lean b/QuantumSystem/Algebra/VonNeumannAlgebra/SpatialDecomposition.lean new file mode 100644 index 0000000..e7495d9 --- /dev/null +++ b/QuantumSystem/Algebra/VonNeumannAlgebra/SpatialDecomposition.lean @@ -0,0 +1,668 @@ +module + +public import QuantumSystem.Algebra.VonNeumannAlgebra.Basic +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.TensorProductCompletion +public import Mathlib.Analysis.InnerProductSpace.Projection.Basic +public import Mathlib.Analysis.InnerProductSpace.Adjoint +public import Mathlib.Analysis.InnerProductSpace.l2Space + +/-! +# Spatial decomposition of a type I factor + +This file assembles the proof ingredients of the type I factor structure theorem +`N ≅ B(ℓ²(F)) ⊗̄ 1` up to the spatial decomposition `H ≅ ℓ²(F) ⊗̂ (eH)`: + +1. **Covering family.** For a factor `N` with minimal projection `e`, Zorn's lemma and the + comparison theorem `IsMinimalProjection.mvNSub_of_isFactor` produce a family of mutually + orthogonal projections, each Murray–von Neumann equivalent to `e`, whose ranges span densely — + the projection-theoretic backbone `Σ eᵢ = 1` of the structure theorem. +2. **Matrix units.** From the equivalence partial isometries `v_p : e ≅ p` the system of **matrix + units** `e_{pq} = v_p v_q⋆` is built, with the defining matrix-unit relations, and the + **multiplicity-one** property: for every `a ∈ N` the matrix entry `v_p⋆ a v_q` is a *scalar* + multiple of `e` (a direct consequence of the corner condition `e N e = ℂ e` defining a minimal + projection). Together these say that `N` is, algebraically, the `*`-algebra of `F × F` matrices + over `ℂ` — the algebraic heart of the structure theorem. +3. **Spatial ℓ² decomposition.** A partial isometry `v` with source projection `p = v⋆v` and range + projection `q = vv⋆` restricts to a linear isometric equivalence between the closed subspaces + `range p` and `range q`. These isometries identify every summand `range eᵢ` with the + multiplicity space `range e`, giving `H ≅ ℓ²(F; eH)` and, through the tensor bridge + `HilbertTensor.lpTensorEquiv`, the literal tensor form `H ≅ ℓ²(F) ⊗̂ (eH)`. + +The remaining steps — the strong-operator reconstruction `a = Σ_{pq} c_{pq}(a) e_{pq}` (in its +double-commutant form: the matrix units generate `N`) and the identification of `N` with +`B(ℓ²(F)) ⊗̄ 1` under the spatial isomorphism — are carried out in +`QuantumSystem.Algebra.VonNeumannAlgebra.StructureTheorem`. + +## Conventions + +The dense span of the ranges is expressed as +`(Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤`, i.e. the closed linear +span of the union of the ranges is the whole space; this is the operator-friendly form of +`⨆ᵢ ranges = 1` and matches the central-support construction `IsFactor.exists_mul_ne`. + +`OrthEquivFam` only records that each member is a nonzero star projection in `N` equivalent to +`e`; when `e` is minimal the members are minimal as a consequence +(`OrthEquivFam.isMinimalProjection_of_mem`). + +## Main definitions + +* `VonNeumannAlgebra.OrthEquivFam N e F` — `F` is a set of pairwise-orthogonal nonzero projections + in `N`, each Murray–von Neumann equivalent to `e`. +* `VonNeumannAlgebra.OrthEquivFam.pisom` — a choice of equivalence partial isometry `v_p : e ≅ p`. +* `VonNeumannAlgebra.OrthEquivFam.matrixUnit` — the matrix unit `e_{pq} = v_p v_q⋆`. +* `VonNeumannAlgebra.OrthEquivFam.multiplicityEquiv` — the spatial isomorphism `H ≅ ℓ²(F; eH)`. + +## Main results + +* `VonNeumannAlgebra.IsFactor.exists_orthEquivFam_top` — in a factor, there is a maximal + orthogonal family of `e`-equivalent projections whose ranges have dense span. +* `VonNeumannAlgebra.OrthEquivFam.matrixUnit_mul_of_eq` / `matrixUnit_mul_of_ne` — the matrix-unit + multiplication law `e_{pq} e_{rs} = δ_{qr} e_{ps}`. +* `VonNeumannAlgebra.OrthEquivFam.exists_matrixEntry` — multiplicity one: + `∃ c, v_p⋆ a v_q = c • e` for `a ∈ N`. +* `IsPartialIsometry.sourceRangeEquiv` — the isometric equivalence `range (v⋆v) ≃ₗᵢ range (vv⋆)` + induced by a partial isometry `v`. +* `VonNeumannAlgebra.IsFactor.exists_lp_decomposition` — the `ℓ²`-sum form `H ≅ ℓ²(F; eH)` of + the spatial decomposition of a type I factor. +* `VonNeumannAlgebra.IsFactor.exists_tmul_decomposition` — the literal tensor form + `H ≅ ℓ²(F) ⊗̂ (eH)`, obtained by composing with the tensor bridge + `HilbertTensor.lpTensorEquiv`. + +## Notation + +`⊗̄` in the prose above is documentation shorthand for the von Neumann (spatial) tensor product of +algebras; that convention is stated in full in `QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`, +where the algebras it names (`HilbertTensor.vnTensorLeft` / `vnTensorRight`) are defined. +-/ + +@[expose] public section + +open scoped ENNReal + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-! ### Covering families of `e`-equivalent projections -/ + +namespace VonNeumannAlgebra + +/-- The range projection of a Murray–von Neumann equivalence with nonzero source is nonzero. -/ +lemma MvNEquiv.ne_zero {N : VonNeumannAlgebra H} {p q : H →L[ℂ] H} + (h : p ∼[N] q) (hp : p ≠ 0) : q ≠ 0 := by + obtain ⟨v, _, hvpi, hvp, hvq⟩ := h + intro hq0 + apply hp + have hv0 : v = 0 := by + have hpi : v * star v * v = v := hvpi + rw [hvq, hq0, zero_mul] at hpi + exact hpi.symm + rw [← hvp, hv0]; simp + +/-- **Minimality transports along Murray–von Neumann equivalence.** If `e` is a minimal projection +and `e ∼[N] p`, then `p` is minimal: with `v⋆v = e` and `vv⋆ = p`, the corner computes as +`p a p = v (e (v⋆ a v) e) v⋆ = c • v e v⋆ = c • p`. -/ +lemma IsMinimalProjection.of_mvNEquiv {N : VonNeumannAlgebra H} {e p : H →L[ℂ] H} + (he : IsMinimalProjection N e) (h : e ∼[N] p) : IsMinimalProjection N p := by + have hpproj : IsStarProjection p := h.isStarProjection_right + have hp0 : p ≠ 0 := h.ne_zero he.2.2.1 + obtain ⟨v, hvN, hvpi, hvp, hvq⟩ := h + have hpN : p ∈ N := by rw [← hvq]; exact mul_mem hvN (star_mem hvN) + have hve : v * e = v := by rw [← hvp]; exact IsPartialIsometry.mul_source hvpi + have hev : e * star v = star v := by + have := congrArg star hve + rwa [star_mul, he.1.isSelfAdjoint.star_eq] at this + refine ⟨hpproj, hpN, hp0, fun a haN => ?_⟩ + obtain ⟨c, hc⟩ := he.2.2.2 (star v * a * v) (mul_mem (mul_mem (star_mem hvN) haN) hvN) + refine ⟨c, ?_⟩ + calc p * a * p + = (v * star v) * a * (v * star v) := by rw [hvq] + _ = (v * e) * (star v * a * v) * (e * star v) := by + rw [hve, hev]; simp only [mul_assoc] + _ = v * (e * (star v * a * v) * e) * star v := by simp only [mul_assoc] + _ = v * (c • e) * star v := by rw [hc] + _ = c • (v * e * star v) := by simp only [mul_smul_comm, smul_mul_assoc] + _ = c • p := by rw [hve, hvq] + +/-- A family of pairwise-orthogonal nonzero projections in `N`, each Murray–von Neumann equivalent +to `e`. -/ +def OrthEquivFam (N : VonNeumannAlgebra H) (e : H →L[ℂ] H) (F : Set (H →L[ℂ] H)) : Prop := + (∀ p ∈ F, IsStarProjection p ∧ p ∈ N ∧ p ≠ 0 ∧ e ∼[N] p) ∧ + F.Pairwise (fun p q => p * q = 0) + +/-- When `e` is minimal, every member of an `OrthEquivFam` for `e` is itself a minimal +projection, since minimality transports along `∼[N]` (`IsMinimalProjection.of_mvNEquiv`). -/ +lemma OrthEquivFam.isMinimalProjection_of_mem {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + {F : Set (H →L[ℂ] H)} (hF : OrthEquivFam N e F) (he : IsMinimalProjection N e) + {p : H →L[ℂ] H} (hp : p ∈ F) : IsMinimalProjection N p := + he.of_mvNEquiv (hF.1 p hp).2.2.2 + +/-- By Zorn's lemma, there is a maximal orthogonal family of `e`-equivalent projections. -/ +lemma exists_maximal_orthEquivFam (N : VonNeumannAlgebra H) (e : H →L[ℂ] H) : + ∃ F, OrthEquivFam N e F ∧ ∀ G, OrthEquivFam N e G → F ⊆ G → G ⊆ F := by + obtain ⟨F, hFmax⟩ := zorn_subset {F | OrthEquivFam N e F} (by + intro c hcsub hchain + refine ⟨⋃₀ c, ⟨?_, ?_⟩, fun s hs => Set.subset_sUnion_of_mem hs⟩ + · rintro p ⟨s, hsc, hps⟩; exact (hcsub hsc).1 p hps + · rintro p ⟨s, hsc, hps⟩ q ⟨t, htc, hqt⟩ hpq + rcases hchain.total hsc htc with h | h + · exact (hcsub htc).2 (h hps) hqt hpq + · exact (hcsub hsc).2 hps (h hqt) hpq) + exact ⟨F, hFmax.1, fun G hG hFG => hFmax.2 hG hFG⟩ + +/-- The orthogonal projection onto the closed span of the ranges of an `OrthEquivFam` lies in `N`, +because that subspace is invariant under the commutant `N'`: for `y ∈ N'` and `f ∈ F ⊆ N`, +`y (f x) = (y f) x = (f y) x = f (y x)` lies in the range of `f`. -/ +lemma OrthEquivFam.starProjection_mem {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + {F : Set (H →L[ℂ] H)} (hF : OrthEquivFam N e F) : + (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure.starProjection ∈ N := by + set S : Set H := {y | ∃ f ∈ F, ∃ x, f x = y} with hS + set M : Submodule ℂ H := (Submodule.span ℂ S).topologicalClosure with hM + set p : H →L[ℂ] H := M.starProjection with hp + have hpproj : IsStarProjection p := isStarProjection_starProjection + rw [IsStarProjection.mem_iff hpproj N] + intro y hyN' + rw [hp, Submodule.range_starProjection] + have hcl : IsClosed ((M.comap (y : H →ₗ[ℂ] H)) : Set H) := by + rw [Submodule.comap_coe] + exact ((Submodule.span ℂ S).isClosed_topologicalClosure).preimage y.continuous + have hle : M ≤ M.comap (y : H →ₗ[ℂ] H) := by + refine Submodule.topologicalClosure_minimal (Submodule.span ℂ S) ?_ hcl + rw [Submodule.span_le] + rintro s ⟨f, hf, x, rfl⟩ + simp only [Submodule.comap_coe, Set.mem_preimage, SetLike.mem_coe, ContinuousLinearMap.coe_coe] + have hfy : f * y = y * f := mem_commutant_iff.mp hyN' f (hF.1 f hf).2.1 + rw [show y (f x) = (y * f) x from rfl, ← hfy] + exact Submodule.le_topologicalClosure _ (Submodule.subset_span ⟨f, hf, y x, rfl⟩) + exact hle + +/-- **Covering family of minimal projections (factor case).** In a factor, there is a family `F` +of pairwise-orthogonal nonzero projections in `N`, each equivalent to the minimal projection `e`, +whose ranges span densely: the closed linear span of the union of their ranges is `⊤`. This is the +`Σ eᵢ = 1` input of the type I structure theorem. + +The proof takes a *maximal* such family `F` (Zorn) and lets `p` be the orthogonal projection onto +the closed span `M` of the ranges; `p ∈ N`. If `M ≠ ⊤` then `r = 1 - p` is a nonzero projection in +`N`, so by the comparison theorem some nonzero `q' ≼ r` is equivalent to `e`; `q'` is orthogonal to +every `f ∈ F`, contradicting maximality. -/ +theorem IsFactor.exists_orthEquivFam_top {N : VonNeumannAlgebra H} + (hN : IsFactor N) {e : H →L[ℂ] H} (he : IsMinimalProjection N e) : + ∃ F : Set (H →L[ℂ] H), OrthEquivFam N e F ∧ + (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤ := by + haveI : Nontrivial H := he.nontrivial + obtain ⟨F, hF, hFmax⟩ := exists_maximal_orthEquivFam N e + refine ⟨F, hF, ?_⟩ + set S : Set H := {y | ∃ f ∈ F, ∃ x, f x = y} with hS + set M : Submodule ℂ H := (Submodule.span ℂ S).topologicalClosure with hM + set p : H →L[ℂ] H := M.starProjection with hp + have hpproj : IsStarProjection p := isStarProjection_starProjection + have hpN : p ∈ N := hF.starProjection_mem + have hpf : ∀ f ∈ F, p * f = f := by + intro f hf + ext x + simp only [ContinuousLinearMap.mul_apply, hp] + rw [Submodule.starProjection_eq_self_iff] + exact Submodule.le_topologicalClosure _ (Submodule.subset_span ⟨f, hf, x, rfl⟩) + by_contra hMtop + set r : H →L[ℂ] H := 1 - p with hr + have hrproj : IsStarProjection r := by + refine ⟨?_, ?_⟩ + · change (1 - p) * (1 - p) = 1 - p + rw [mul_sub, sub_mul, sub_mul, one_mul, one_mul, mul_one, hpproj.isIdempotentElem, + sub_self, sub_zero] + · change star (1 - p) = 1 - p + rw [star_sub, star_one, hpproj.isSelfAdjoint.star_eq] + have hrN : r ∈ N := by rw [hr]; exact sub_mem (one_mem _) hpN + have hr0 : r ≠ 0 := by + intro h + apply hMtop + have hp1 : p = 1 := by rw [hr, sub_eq_zero] at h; exact h.symm + have hMrange : M = p.range := (Submodule.range_starProjection M).symm + rw [hMrange, hp1] + exact Submodule.eq_top_iff'.2 fun y => ⟨y, rfl⟩ + obtain ⟨q', hq'N, hrq', heq'⟩ := he.mvNSub_of_isFactor hN hrproj hrN hr0 + have hq'proj : IsStarProjection q' := heq'.isStarProjection_right + have hq'0 : q' ≠ 0 := heq'.ne_zero he.2.2.1 + have hpq' : p * q' = 0 := by + have h := hrq' + rw [hr, sub_mul, one_mul, sub_eq_self] at h + exact h + have horth : ∀ f ∈ F, q' * f = 0 ∧ f * q' = 0 := by + intro f hf + have hq'p : q' * p = 0 := by + have h := congrArg star hpq' + rwa [star_mul, hq'proj.isSelfAdjoint.star_eq, hpproj.isSelfAdjoint.star_eq, star_zero] at h + have h1 : q' * f = 0 := by + calc q' * f = q' * (p * f) := by rw [hpf f hf] + _ = (q' * p) * f := by rw [mul_assoc] + _ = 0 := by rw [hq'p, zero_mul] + refine ⟨h1, ?_⟩ + have h := congrArg star h1 + rwa [star_mul, (hF.1 f hf).1.isSelfAdjoint.star_eq, hq'proj.isSelfAdjoint.star_eq, + star_zero] at h + have hq'notF : q' ∉ F := fun hq'F => + hq'0 (by have := (horth q' hq'F).1; rwa [hq'proj.isIdempotentElem] at this) + have hbigger : OrthEquivFam N e (insert q' F) := by + refine ⟨?_, ?_⟩ + · rintro x (rfl | hx) + · exact ⟨hq'proj, hq'N, hq'0, heq'⟩ + · exact hF.1 x hx + · rintro x (rfl | hx) z (rfl | hz) hxz + · exact absurd rfl hxz + · exact (horth z hz).1 + · exact (horth x hx).2 + · exact hF.2 hx hz hxz + have hsub := hFmax (insert q' F) hbigger (Set.subset_insert _ _) + exact hq'notF (hsub (Set.mem_insert _ _)) + +/-! ### Matrix units and the multiplicity-one property -/ + +variable {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} {F : Set (H →L[ℂ] H)} + +/-- A choice of partial isometry `v_p` implementing the Murray–von Neumann equivalence `e ∼[N] p`, +oriented with source projection `v_p⋆ v_p = e` (`pisom_source`) and range projection +`v_p v_p⋆ = p` (`pisom_range`). -/ +noncomputable def OrthEquivFam.pisom (hF : OrthEquivFam N e F) (p : F) : H →L[ℂ] H := + (hF.1 p.1 p.2).2.2.2.choose + +/-- The chosen equivalence partial isometry `v_p` lies in `N`. -/ +lemma OrthEquivFam.pisom_mem (hF : OrthEquivFam N e F) (p : F) : hF.pisom p ∈ N := + (hF.1 p.1 p.2).2.2.2.choose_spec.1 + +/-- The chosen `v_p` is a partial isometry. -/ +lemma OrthEquivFam.pisom_isPI (hF : OrthEquivFam N e F) (p : F) : + IsPartialIsometry (hF.pisom p) := + (hF.1 p.1 p.2).2.2.2.choose_spec.2.1 + +/-- The source projection of `v_p` is `e`: `v_p⋆ v_p = e`. -/ +lemma OrthEquivFam.pisom_source (hF : OrthEquivFam N e F) (p : F) : + star (hF.pisom p) * hF.pisom p = e := + (hF.1 p.1 p.2).2.2.2.choose_spec.2.2.1 + +/-- The range projection of `v_p` is `p`: `v_p v_p⋆ = p`. -/ +lemma OrthEquivFam.pisom_range (hF : OrthEquivFam N e F) (p : F) : + hF.pisom p * star (hF.pisom p) = (p : H →L[ℂ] H) := + (hF.1 p.1 p.2).2.2.2.choose_spec.2.2.2 + +/-- `v_p e = v_p`: the source projection acts as a right unit on `v_p`. -/ +lemma OrthEquivFam.pisom_mul_source (hF : OrthEquivFam N e F) (p : F) : + hF.pisom p * e = hF.pisom p := by + have h : hF.pisom p * (star (hF.pisom p) * hF.pisom p) = hF.pisom p := by + rw [← mul_assoc]; exact hF.pisom_isPI p + rwa [hF.pisom_source p] at h + +/-- `e v_p⋆ = v_p⋆`: the source projection acts as a left unit on `v_p⋆`. -/ +lemma OrthEquivFam.e_mul_star_pisom (hF : OrthEquivFam N e F) (p : F) : + e * star (hF.pisom p) = star (hF.pisom p) := by + have h : star (hF.pisom p) * hF.pisom p * star (hF.pisom p) = star (hF.pisom p) := by + have h' : star (hF.pisom p) * star (star (hF.pisom p)) * star (hF.pisom p) + = star (hF.pisom p) := IsPartialIsometry.star (hF.pisom_isPI p) + rwa [star_star] at h' + rwa [hF.pisom_source p] at h + +/-- For distinct family members the partial isometries are orthogonal: `v_q⋆ v_r = 0`. -/ +lemma OrthEquivFam.star_pisom_mul_pisom_of_ne (hF : OrthEquivFam N e F) {q r : F} + (hqr : q ≠ r) : star (hF.pisom q) * hF.pisom r = 0 := by + have hq : (q : H →L[ℂ] H) * hF.pisom q = hF.pisom q := by + have h : hF.pisom q * star (hF.pisom q) * hF.pisom q = hF.pisom q := hF.pisom_isPI q + rwa [hF.pisom_range q] at h + have hr : (r : H →L[ℂ] H) * hF.pisom r = hF.pisom r := by + have h : hF.pisom r * star (hF.pisom r) * hF.pisom r = hF.pisom r := hF.pisom_isPI r + rwa [hF.pisom_range r] at h + have hsq : star (hF.pisom q) * (q : H →L[ℂ] H) = star (hF.pisom q) := by + have := congrArg star hq + rwa [star_mul, (hF.1 q.1 q.2).1.isSelfAdjoint.star_eq] at this + have h0 : (q : H →L[ℂ] H) * r = 0 := hF.2 q.2 r.2 (fun h => hqr (Subtype.ext h)) + calc star (hF.pisom q) * hF.pisom r + = (star (hF.pisom q) * (q : H →L[ℂ] H)) * ((r : H →L[ℂ] H) * hF.pisom r) := by + rw [hsq, hr] + _ = star (hF.pisom q) * ((q : H →L[ℂ] H) * r) * hF.pisom r := by simp only [mul_assoc] + _ = 0 := by rw [h0, mul_zero, zero_mul] + +/-- The **matrix unit** `e_{pq} = v_p v_q⋆`. -/ +noncomputable def OrthEquivFam.matrixUnit (hF : OrthEquivFam N e F) (p q : F) : H →L[ℂ] H := + hF.pisom p * star (hF.pisom q) + +/-- Definitional unfolding of the matrix unit: `e_{pq} = v_p v_q⋆`. -/ +lemma OrthEquivFam.matrixUnit_def (hF : OrthEquivFam N e F) (p q : F) : + hF.matrixUnit p q = hF.pisom p * star (hF.pisom q) := rfl + +/-- Matrix units lie in `N`. -/ +lemma OrthEquivFam.matrixUnit_mem (hF : OrthEquivFam N e F) (p q : F) : + hF.matrixUnit p q ∈ N := + mul_mem (hF.pisom_mem p) (star_mem (hF.pisom_mem q)) + +/-- Matrix-unit multiplication law `e_{pq} e_{rs} = δ_{qr} e_{ps}`, diagonal case `q = r`: +`e_{pq} e_{qs} = e_{ps}`. -/ +theorem OrthEquivFam.matrixUnit_mul_of_eq (hF : OrthEquivFam N e F) (p q s : F) : + hF.matrixUnit p q * hF.matrixUnit q s = hF.matrixUnit p s := by + rw [matrixUnit_def, matrixUnit_def, matrixUnit_def] + calc hF.pisom p * star (hF.pisom q) * (hF.pisom q * star (hF.pisom s)) + = hF.pisom p * (star (hF.pisom q) * hF.pisom q) * star (hF.pisom s) := by + simp only [mul_assoc] + _ = hF.pisom p * e * star (hF.pisom s) := by rw [hF.pisom_source q] + _ = hF.pisom p * star (hF.pisom s) := by rw [hF.pisom_mul_source p] + +/-- Matrix-unit multiplication law `e_{pq} e_{rs} = δ_{qr} e_{ps}`, off-diagonal case `q ≠ r`: +`e_{pq} e_{rs} = 0`. -/ +theorem OrthEquivFam.matrixUnit_mul_of_ne (hF : OrthEquivFam N e F) (p s : F) {q r : F} + (hqr : q ≠ r) : hF.matrixUnit p q * hF.matrixUnit r s = 0 := by + rw [matrixUnit_def, matrixUnit_def] + calc hF.pisom p * star (hF.pisom q) * (hF.pisom r * star (hF.pisom s)) + = hF.pisom p * (star (hF.pisom q) * hF.pisom r) * star (hF.pisom s) := by + simp only [mul_assoc] + _ = 0 := by rw [hF.star_pisom_mul_pisom_of_ne hqr, mul_zero, zero_mul] + +/-- **Multiplicity one.** For a minimal projection `e` and any `a ∈ N`, the matrix entry +`v_p⋆ a v_q` is a scalar multiple of `e`. This is the corner condition `e N e = ℂ e` transported +along the equivalences, and is the algebraic content of `N ≅ B(ℓ²(F)) ⊗̄ 1`. -/ +theorem OrthEquivFam.exists_matrixEntry (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) (p q : F) {a : H →L[ℂ] H} (ha : a ∈ N) : + ∃ c : ℂ, star (hF.pisom p) * a * hF.pisom q = c • e := by + have hbN : star (hF.pisom p) * a * hF.pisom q ∈ N := + mul_mem (mul_mem (star_mem (hF.pisom_mem p)) ha) (hF.pisom_mem q) + obtain ⟨c, hc⟩ := he.2.2.2 _ hbN + refine ⟨c, ?_⟩ + have key : e * (star (hF.pisom p) * a * hF.pisom q) * e + = star (hF.pisom p) * a * hF.pisom q := by + calc e * (star (hF.pisom p) * a * hF.pisom q) * e + = (e * star (hF.pisom p)) * a * (hF.pisom q * e) := by simp only [mul_assoc] + _ = star (hF.pisom p) * a * hF.pisom q := by + rw [hF.e_mul_star_pisom p, hF.pisom_mul_source q] + rw [← key, hc] + +end VonNeumannAlgebra + +/-! ### Partial-isometry isometries and the spatial ℓ² decomposition -/ + +section LpCongr + +variable {α : Type*} {𝕜 : Type*} [RCLike 𝕜] {G G' : α → Type*} + [∀ i, NormedAddCommGroup (G i)] [∀ i, NormedSpace 𝕜 (G i)] + [∀ i, NormedAddCommGroup (G' i)] [∀ i, NormedSpace 𝕜 (G' i)] + +/-- A family of isometries preserves `Memℓp`: norms are pointwise unchanged. -/ +lemma memℓp_congr_linearIsometryEquiv (e : ∀ i, G i ≃ₗᵢ[𝕜] G' i) {f : ∀ i, G i} + (hf : Memℓp f 2) : Memℓp (fun i => e i (f i)) 2 := by + apply Memℓp.of_norm + have hnorm : (fun i => ‖e i (f i)‖) = fun i => ‖f i‖ := funext fun i => (e i).norm_map (f i) + rw [hnorm] + exact hf.norm + +/-- A family of linear isometric equivalences `G i ≃ₗᵢ G' i` induces a linear isometric +equivalence between the `ℓ²` sums `lp G 2 ≃ₗᵢ lp G' 2`, applied componentwise. -/ +noncomputable def lpCongr (e : ∀ i, G i ≃ₗᵢ[𝕜] G' i) : lp G 2 ≃ₗᵢ[𝕜] lp G' 2 where + toFun f := ⟨fun i => e i (f i), memℓp_congr_linearIsometryEquiv e (lp.memℓp f)⟩ + invFun g := ⟨fun i => (e i).symm (g i), memℓp_congr_linearIsometryEquiv (fun i => (e i).symm) + (lp.memℓp g)⟩ + left_inv f := by + refine Subtype.ext (funext fun i => ?_) + change (e i).symm (e i (f i)) = f i + rw [LinearIsometryEquiv.symm_apply_apply] + right_inv g := by + refine Subtype.ext (funext fun i => ?_) + change e i ((e i).symm (g i)) = g i + rw [LinearIsometryEquiv.apply_symm_apply] + map_add' x y := by + refine Subtype.ext (funext fun i => ?_) + change e i ((x + y) i) = e i (x i) + e i (y i) + rw [lp.coeFn_add, Pi.add_apply, map_add] + map_smul' c f := by + refine Subtype.ext (funext fun i => ?_) + change e i ((c • f) i) = c • e i (f i) + rw [lp.coeFn_smul, Pi.smul_apply, map_smul] + norm_map' f := by + have hp : (0 : ℝ) < (2 : ℝ≥0∞).toReal := by norm_num + rw [lp.norm_eq_tsum_rpow hp, lp.norm_eq_tsum_rpow hp] + congr 1 + refine tsum_congr fun i => ?_ + congr 1 + exact (e i).norm_map (f i) + +end LpCongr + +namespace IsPartialIsometry + +/-- For `x` in the source subspace (`p x = x` where `p = v⋆v`), the map preserves the norm: +`‖v x‖ = ‖x‖`. -/ +lemma norm_apply {v : H →L[ℂ] H} {p : H →L[ℂ] H} + (hsource : star v * v = p) {x : H} (hx : (p : H →L[ℂ] H) x = x) : ‖v x‖ = ‖x‖ := by + have hinner : (inner ℂ (v x) (v x) : ℂ) = inner ℂ x x := by + rw [← ContinuousLinearMap.adjoint_inner_right, ← ContinuousLinearMap.star_eq_adjoint, + ← ContinuousLinearMap.mul_apply, hsource, hx] + have h2 : ‖v x‖ ^ 2 = ‖x‖ ^ 2 := by + rw [← inner_self_eq_norm_sq (𝕜 := ℂ), ← inner_self_eq_norm_sq (𝕜 := ℂ)] + exact congrArg RCLike.re hinner + have h3 := congrArg Real.sqrt h2 + rwa [Real.sqrt_sq (norm_nonneg _), Real.sqrt_sq (norm_nonneg _)] at h3 + +/-- The image of any vector under a partial isometry lands in the range subspace: if `q = v v⋆` +then `q (v x) = v x`. -/ +lemma apply_mem_range {v : H →L[ℂ] H} (hv : IsPartialIsometry v) {q : H →L[ℂ] H} + (hrange : v * star v = q) (x : H) : (q : H →L[ℂ] H) (v x) = v x := by + rw [← ContinuousLinearMap.mul_apply, ← hrange, hv] + +/-- A partial isometry `v` with source projection `star v * v = p` and range projection +`v * star v = q` restricts to a linear isometric equivalence from the source subspace +`range p` onto the range subspace `range q`. -/ +noncomputable def sourceRangeEquiv {v : H →L[ℂ] H} (hv : IsPartialIsometry v) + {p q : H →L[ℂ] H} (hsource : star v * v = p) (hrange : v * star v = q) : + LinearMap.range (p : H →ₗ[ℂ] H) ≃ₗᵢ[ℂ] LinearMap.range (q : H →ₗ[ℂ] H) := by + have hpidem : (p : H →L[ℂ] H) * p = p := by + have := hv.isStarProjection_star_mul_self.isIdempotentElem + rwa [hsource] at this + have hqidem : (q : H →L[ℂ] H) * q = q := by + have := hv.isStarProjection_mul_star_self.isIdempotentElem + rwa [hrange] at this + have hsvpi : star v * v * star v = star v := by + have h : star v * star (star v) * star v = star v := IsPartialIsometry.star hv + rwa [star_star] at h + have hfix : ∀ {x : H}, x ∈ LinearMap.range (p : H →ₗ[ℂ] H) → (p : H →L[ℂ] H) x = x := by + rintro x ⟨z, rfl⟩ + rw [ContinuousLinearMap.coe_coe, ← ContinuousLinearMap.mul_apply, hpidem] + refine LinearIsometryEquiv.ofSurjective + { toFun := fun ξ => ⟨v ξ.1, ⟨v ξ.1, by + rw [ContinuousLinearMap.coe_coe]; exact hv.apply_mem_range hrange ξ.1⟩⟩ + map_add' := fun a b => by apply Subtype.ext; simp + map_smul' := fun c a => by apply Subtype.ext; simp + norm_map' := fun ξ => norm_apply hsource (hfix ξ.2) } ?_ + rintro ⟨η, hη⟩ + have hqfix : (q : H →L[ℂ] H) η = η := by + obtain ⟨z, hz⟩ := hη + rw [← hz, ContinuousLinearMap.coe_coe, ← ContinuousLinearMap.mul_apply, hqidem] + have hmem : star v η ∈ LinearMap.range (p : H →ₗ[ℂ] H) := by + refine ⟨star v η, ?_⟩ + rw [ContinuousLinearMap.coe_coe, show (p : H →L[ℂ] H) (star v η) = (p * star v) η from rfl, + ← hsource, hsvpi] + refine ⟨⟨star v η, hmem⟩, Subtype.ext ?_⟩ + change v (star v η) = η + rw [← ContinuousLinearMap.mul_apply, hrange, hqfix] + +end IsPartialIsometry + +/-- A vector in the range of a star projection is fixed by it: `p x = x`. -/ +lemma IsStarProjection.apply_eq_self_of_mem_range {p : H →L[ℂ] H} (hp : IsStarProjection p) + {x : H} (hx : x ∈ LinearMap.range (p : H →ₗ[ℂ] H)) : (p : H →L[ℂ] H) x = x := by + obtain ⟨z, rfl⟩ := hx + rw [ContinuousLinearMap.coe_coe, ← ContinuousLinearMap.mul_apply, hp.isIdempotentElem] + +/-- The range of a star projection is closed: it equals the kernel of `1 - p`. -/ +lemma IsStarProjection.isClosed_range {p : H →L[ℂ] H} (hp : IsStarProjection p) : + IsClosed (LinearMap.range (p : H →ₗ[ℂ] H) : Set H) := by + have hker : LinearMap.range (p : H →ₗ[ℂ] H) + = LinearMap.ker ((1 - p : H →L[ℂ] H) : H →ₗ[ℂ] H) := by + ext x + simp only [LinearMap.mem_range, LinearMap.mem_ker, ContinuousLinearMap.coe_coe, + ContinuousLinearMap.sub_apply, ContinuousLinearMap.one_apply, sub_eq_zero] + constructor + · rintro ⟨z, rfl⟩ + rw [← ContinuousLinearMap.mul_apply, hp.isIdempotentElem] + · intro hx + exact ⟨x, hx.symm⟩ + rw [hker] + exact (1 - p).isClosed_ker + +/-- The range of a star projection, as a closed subspace, is complete. -/ +lemma IsStarProjection.completeSpace_range {p : H →L[ℂ] H} (hp : IsStarProjection p) : + CompleteSpace (LinearMap.range (p : H →ₗ[ℂ] H)) := + completeSpace_coe_iff_isComplete.mpr hp.isClosed_range.isComplete + +/-- The inverse of the partial-isometry-induced equivalence acts as `v⋆`: for `η` in the range +subspace, `(sourceRangeEquiv v).symm η = v⋆ η`. -/ +lemma IsPartialIsometry.coe_sourceRangeEquiv_symm {v : H →L[ℂ] H} (hv : IsPartialIsometry v) + {p q : H →L[ℂ] H} (hsource : star v * v = p) (hrange : v * star v = q) + (η : LinearMap.range (q : H →ₗ[ℂ] H)) : + ((hv.sourceRangeEquiv hsource hrange).symm η : H) = star v (η : H) := by + have hsvpi : star v * v * star v = star v := by + have h : star v * star (star v) * star v = star v := IsPartialIsometry.star hv + rwa [star_star] at h + have hq : IsStarProjection q := by rw [← hrange]; exact hv.isStarProjection_mul_star_self + have hqfix : (q : H →L[ℂ] H) (η : H) = (η : H) := hq.apply_eq_self_of_mem_range η.2 + have hmem : star v (η : H) ∈ LinearMap.range (p : H →ₗ[ℂ] H) := + ⟨star v (η : H), by + rw [ContinuousLinearMap.coe_coe, ← hsource, ← ContinuousLinearMap.mul_apply, hsvpi]⟩ + have hG : (hv.sourceRangeEquiv hsource hrange) ⟨star v (η : H), hmem⟩ = η := by + apply Subtype.ext + change v (star v (η : H)) = (η : H) + rw [← ContinuousLinearMap.mul_apply, hrange, hqfix] + have hsymm : (hv.sourceRangeEquiv hsource hrange).symm η = ⟨star v (η : H), hmem⟩ := + (hv.sourceRangeEquiv hsource hrange).injective (by + rw [LinearIsometryEquiv.apply_symm_apply]; exact hG.symm) + rw [hsymm] + +namespace VonNeumannAlgebra + +/-- A covering orthogonal family of star projections (each in `OrthEquivFam`) realises `H` as the +internal Hilbert sum of the ranges. -/ +lemma OrthEquivFam.isHilbertSum {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + {F : Set (H →L[ℂ] H)} (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) : + IsHilbertSum ℂ (fun i : F => LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)) + (fun i => (LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ) := by + haveI : ∀ i : F, CompleteSpace (LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)) := + fun i => (hF.1 i.1 i.2).1.completeSpace_range + refine IsHilbertSum.mkInternal _ ?_ ?_ + · rintro ⟨pi, hpi⟩ ⟨pj, hpj⟩ hij ⟨v, hv⟩ ⟨w, hw⟩ + have hne : pi ≠ pj := fun h => hij (Subtype.ext h) + have h0 : (pi : H →L[ℂ] H) * pj = 0 := hF.2 hpi hpj hne + have hvf : (pi : H →L[ℂ] H) v = v := (hF.1 pi hpi).1.apply_eq_self_of_mem_range hv + have hwf : (pj : H →L[ℂ] H) w = w := (hF.1 pj hpj).1.apply_eq_self_of_mem_range hw + have e1 : (inner ℂ v w : ℂ) = inner ℂ ((pi : H →L[ℂ] H) v) ((pj : H →L[ℂ] H) w) := by + rw [hvf, hwf] + change (inner ℂ v w : ℂ) = 0 + rw [e1, ← ContinuousLinearMap.adjoint_inner_right, ← ContinuousLinearMap.star_eq_adjoint, + (hF.1 pi hpi).1.isSelfAdjoint.star_eq, + show (pi : H →L[ℂ] H) ((pj : H →L[ℂ] H) w) = ((pi : H →L[ℂ] H) * pj) w from rfl, h0] + simp + · rw [← htop] + refine Submodule.topologicalClosure_mono (Submodule.span_le.mpr ?_) + rintro y ⟨f, hf, x, rfl⟩ + exact Submodule.mem_iSup_of_mem ⟨f, hf⟩ ⟨x, rfl⟩ + +/-- **Spatial Hilbert-sum isomorphism.** A covering orthogonal family of `e`-equivalent +projections gives a linear isometric equivalence of `H` with the `ℓ²` sum of the ranges. -/ +noncomputable def OrthEquivFam.hilbertSumEquiv {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + {F : Set (H →L[ℂ] H)} (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) : + H ≃ₗᵢ[ℂ] lp (fun i : F => LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)) 2 := + (hF.isHilbertSum htop).linearIsometryEquiv + +/-- **Multiplicity decomposition of the Hilbert space.** A covering orthogonal family of +projections each Murray–von Neumann equivalent to `e` identifies `H` isometrically with +`ℓ²(F; eH)` — the `ℓ²` sum, indexed by `F`, of copies of the fibre `range e` (which is the +*multiplicity space* when `e` is minimal, as in `exists_lp_decomposition`; minimality is not +assumed in this lemma). The codomain is that `ℓ²` sum and nothing else: no tensor product occurs +here. Composing with the tensor bridge turns it into the literal `H ≅ ℓ²(F) ⊗̂ (eH)` of the type I +factor structure theorem — that is `OrthEquivFam.spatialEquiv`, and the existence statement is +`exists_tmul_decomposition`. The equivalence +is built from the Hilbert-sum decomposition `H ≅ ⊕ᵢ range eᵢ` and the partial-isometry-induced +isometries `range eᵢ ≅ range e`. -/ +noncomputable def OrthEquivFam.multiplicityEquiv {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + {F : Set (H →L[ℂ] H)} (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) : + H ≃ₗᵢ[ℂ] lp (fun _ : F => LinearMap.range (e : H →ₗ[ℂ] H)) 2 := + (hF.hilbertSumEquiv htop).trans (lpCongr (fun i => + (IsPartialIsometry.sourceRangeEquiv + (hF.1 i.1 i.2).2.2.2.choose_spec.2.1 + (hF.1 i.1 i.2).2.2.2.choose_spec.2.2.1 + (hF.1 i.1 i.2).2.2.2.choose_spec.2.2.2).symm)) + +/-- The `i`-th Hilbert-sum coordinate of `y`, embedded back into `H`, is the orthogonal projection +`(↑i) y`. This identifies the abstract Hilbert-sum decomposition with the explicit family of range +projections. -/ +lemma OrthEquivFam.coe_hilbertSumEquiv_apply {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} + {F : Set (H →L[ℂ] H)} (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + (y : H) (i : F) : + ((hF.hilbertSumEquiv htop y i : LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)) : H) + = (i : H →L[ℂ] H) y := by + have hHS := hF.isHilbertSum htop + have hdecomp : HasSum + (fun j : F => (LinearMap.range ((j : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ + ((hHS.linearIsometryEquiv y) j)) y := by + have h := hHS.hasSum_linearIsometryEquiv_symm (hHS.linearIsometryEquiv y) + rwa [LinearIsometryEquiv.symm_apply_apply] at h + have happ := hdecomp.mapL (i : H →L[ℂ] H) + have hii : (i : H →L[ℂ] H) + ((LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ ((hHS.linearIsometryEquiv y) i)) + = (LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ ((hHS.linearIsometryEquiv y) i) := + (hF.1 i.1 i.2).1.apply_eq_self_of_mem_range (Submodule.coe_mem _) + have hsingle : HasSum + (fun j : F => (i : H →L[ℂ] H) + ((LinearMap.range ((j : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ ((hHS.linearIsometryEquiv y) j))) + ((i : H →L[ℂ] H) + ((LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ ((hHS.linearIsometryEquiv y) i))) := + hasSum_single i (fun j hj => by + have hjfix : (j : H →L[ℂ] H) + ((LinearMap.range ((j : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ ((hHS.linearIsometryEquiv y) j)) + = (LinearMap.range ((j : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ + ((hHS.linearIsometryEquiv y) j) := + (hF.1 j.1 j.2).1.apply_eq_self_of_mem_range (Submodule.coe_mem _) + have hij0 : (i : H →L[ℂ] H) * (j : H →L[ℂ] H) = 0 := + hF.2 i.2 j.2 (fun h => hj (Subtype.ext h).symm) + calc (i : H →L[ℂ] H) + ((LinearMap.range ((j : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ ((hHS.linearIsometryEquiv y) j)) + = (i : H →L[ℂ] H) ((j : H →L[ℂ] H) + ((LinearMap.range ((j : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ + ((hHS.linearIsometryEquiv y) j))) := by rw [hjfix] + _ = ((i : H →L[ℂ] H) * (j : H →L[ℂ] H)) + ((LinearMap.range ((j : H →L[ℂ] H) : H →ₗ[ℂ] H)).subtypeₗᵢ + ((hHS.linearIsometryEquiv y) j)) := rfl + _ = 0 := by rw [hij0]; rfl) + exact ((happ.unique hsingle).trans hii).symm + +/-- The minimal projection `e` of a type I factor is the multiplicity space: a type I factor with +minimal projection `e` acts on a Hilbert space isometric to the `ℓ²` sum `ℓ²(F; eH)` of copies of +`eH = range e`, indexed by a covering orthogonal family `F` (`OrthEquivFam`, with densely +spanning ranges) of minimal projections equivalent to `e`. Composing with the tensor bridge turns +this `ℓ²` sum into the literal tensor product `H ≅ ℓ²(F) ⊗̂ eH`; that is the statement of +`exists_tmul_decomposition`, and it — not this one — is the tensor-product form. -/ +theorem IsFactor.exists_lp_decomposition {N : VonNeumannAlgebra H} + (hN : IsFactor N) {e : H →L[ℂ] H} (he : IsMinimalProjection N e) : + ∃ F : Set (H →L[ℂ] H), OrthEquivFam N e F ∧ + (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤ ∧ + (∀ p ∈ F, IsMinimalProjection N p) ∧ + Nonempty (H ≃ₗᵢ[ℂ] lp (fun _ : F => LinearMap.range (e : H →ₗ[ℂ] H)) 2) := by + obtain ⟨F, hF, htop⟩ := hN.exists_orthEquivFam_top he + exact ⟨F, hF, htop, fun p hp => hF.isMinimalProjection_of_mem he hp, + ⟨hF.multiplicityEquiv htop⟩⟩ + +/-- **Tensor-product decomposition (literal form).** A type I factor `N ⊆ B(H)` with minimal +projection `e` acts on a Hilbert space isometric to the completed Hilbert tensor product +`ℓ²(F) ⊗̂ (eH)`, where `F` is a covering orthogonal family (`OrthEquivFam`, with densely spanning +ranges) of minimal projections equivalent to `e` and `eH = range e` is the multiplicity space. +This is the literal `H ≅ ℓ²(F) ⊗̂ eH` form of the type I structure theorem, obtained from +`exists_lp_decomposition` by composing with the tensor bridge `HilbertTensor.lpTensorEquiv`. -/ +theorem IsFactor.exists_tmul_decomposition {N : VonNeumannAlgebra H} + (hN : IsFactor N) {e : H →L[ℂ] H} (he : IsMinimalProjection N e) : + ∃ F : Set (H →L[ℂ] H), OrthEquivFam N e F ∧ + (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤ ∧ + (∀ p ∈ F, IsMinimalProjection N p) ∧ + Nonempty (H ≃ₗᵢ[ℂ] + HilbertTensor (lp (fun _ : F => ℂ) 2) (LinearMap.range (e : H →ₗ[ℂ] H))) := by + obtain ⟨F, hF, htop⟩ := hN.exists_orthEquivFam_top he + haveI : CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H)) := he.1.completeSpace_range + haveI : DecidableEq (↥F) := Classical.decEq _ + exact ⟨F, hF, htop, fun p hp => hF.isMinimalProjection_of_mem he hp, + ⟨(hF.multiplicityEquiv htop).trans + (HilbertTensor.lpTensorEquiv (ι := F) (K := LinearMap.range (e : H →ₗ[ℂ] H)))⟩⟩ + +end VonNeumannAlgebra diff --git a/QuantumSystem/Algebra/VonNeumannAlgebra/SplitInclusion.lean b/QuantumSystem/Algebra/VonNeumannAlgebra/SplitInclusion.lean new file mode 100644 index 0000000..61bca10 --- /dev/null +++ b/QuantumSystem/Algebra/VonNeumannAlgebra/SplitInclusion.lean @@ -0,0 +1,213 @@ +module + +public import QuantumSystem.Algebra.VonNeumannAlgebra.TypeI +public import Mathlib.Analysis.InnerProductSpace.Adjoint +public import Mathlib.Analysis.InnerProductSpace.PiL2 + +/-! +# Split inclusions of von Neumann algebras + +An inclusion `A ≤ B` of von Neumann algebras on a Hilbert space is **split** when some type I +factor `M` interpolates: `A ≤ M ≤ B`. This is the definition of Doplicher–Longo, *Standard and +split inclusions of von Neumann algebras* (Invent. Math. 75, 1984) §1; the notion originates in +the analysis of local algebras by Buchholz, *Product states for local algebras* (Comm. Math. +Phys. 36, 1974), where the interpolating type I factor is what produces normal product states +across a commuting pair. The AQFT *split property* — this predicate applied to the inclusions +`𝓡(O₁) ≤ 𝓡(O₂)` of local von Neumann algebras of properly contained regions — lives at the net +level in `QuantumSystem.Algebra.LocalNet.SplitProperty`. + +The structural content of a split inclusion is the tensor decomposition +`IsSplitInclusion.exists_tensor_decomposition`: a spatial isomorphism `U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)` +carrying `A` into the tensor factor `𝓑(ℓ²(F)) ⊗̄ 1` and the commutant `B′` into `1 ⊗̄ 𝓑(eH)`. It +is inherited from the type I structure theorem +(`QuantumSystem.Algebra.VonNeumannAlgebra.StructureTheorem`). + +## Main definitions and results + +* `VonNeumannAlgebra.IsSplitInclusion A B` — some type I factor `M` satisfies `A ≤ M ≤ B`. +* `VonNeumannAlgebra.IsSplitInclusion.le` / `mono` — a split inclusion is an inclusion, and + splitness survives shrinking `A` and enlarging `B`. +* `VonNeumannAlgebra.IsTypeIFactor.isSplitInclusion_of_le_of_le` — any inclusion sandwiching a + type I factor is split. +* `VonNeumannAlgebra.isSplitInclusion_self_iff` — the identity inclusion `M ≤ M` is split exactly + when `M` is a type I factor; the two directions are + `IsTypeIFactor.isSplitInclusion_self` and `IsSplitInclusion.isTypeIFactor_of_self`. +* `VonNeumannAlgebra.IsSplitInclusion.exists_tensor_decomposition` — the split tensor + decomposition of the inclusion. +* `VonNeumannAlgebra.diagonalAlgebra` and `VonNeumannAlgebra.not_isSplitInclusion_diagonalAlgebra` + — the negative control: the diagonal algebra `ℂ ⊕ ℂ` on `ℂ²` is not a factor + (`not_isFactor_diagonalAlgebra`), so its identity inclusion is *not* split. This is what keeps + the predicate distinguishable from plain inclusion. + +## Notation + +`⊗̄` in the prose above is documentation shorthand for the von Neumann (spatial) tensor product of +algebras; that convention is stated in full in `QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`, +where the algebras it names (`HilbertTensor.vnTensorLeft` / `vnTensorRight`) are defined. +-/ + +@[expose] public section + +namespace VonNeumannAlgebra + +open HilbertTensor + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-- **Split inclusion of von Neumann algebras** (Doplicher–Longo). An inclusion `A ≤ B` is +*split* when some type I factor `M` interpolates: `A ≤ M ≤ B`. The containment `A ≤ B` is not +part of the definition, since it follows (`IsSplitInclusion.le`). -/ +def IsSplitInclusion (A B : VonNeumannAlgebra H) : Prop := + ∃ M : VonNeumannAlgebra H, IsTypeIFactor M ∧ A ≤ M ∧ M ≤ B + +/-- A split inclusion is in particular an inclusion. -/ +lemma IsSplitInclusion.le {A B : VonNeumannAlgebra H} (h : IsSplitInclusion A B) : A ≤ B := + let ⟨_, _, h₁, h₂⟩ := h + h₁.trans h₂ + +/-- Splitness is preserved by shrinking the smaller algebra and enlarging the larger one. -/ +lemma IsSplitInclusion.mono {A A' B B' : VonNeumannAlgebra H} (hA : A' ≤ A) (hB : B ≤ B') + (h : IsSplitInclusion A B) : IsSplitInclusion A' B' := + let ⟨M, hM, h₁, h₂⟩ := h + ⟨M, hM, hA.trans h₁, h₂.trans hB⟩ + +/-- Any inclusion sandwiching a type I factor is split. -/ +lemma IsTypeIFactor.isSplitInclusion_of_le_of_le {M A B : VonNeumannAlgebra H} + (hM : IsTypeIFactor M) (h₁ : A ≤ M) (h₂ : M ≤ B) : IsSplitInclusion A B := + ⟨M, hM, h₁, h₂⟩ + +/-- A type I factor splits in itself: `IsSplitInclusion M M`. -/ +lemma IsTypeIFactor.isSplitInclusion_self {M : VonNeumannAlgebra H} (hM : IsTypeIFactor M) : + IsSplitInclusion M M := + hM.isSplitInclusion_of_le_of_le le_rfl le_rfl + +/-- **The identity inclusion splits only for type I factors.** An interpolating factor squeezed +between `M` and itself *is* `M`, by antisymmetry. This is why a reflexive proper-containment +relation would make the AQFT split property demand type I local algebras — the reason +`ProperContainment` is axiomatised to be irreflexive. -/ +lemma IsSplitInclusion.isTypeIFactor_of_self {M : VonNeumannAlgebra H} + (h : IsSplitInclusion M M) : IsTypeIFactor M := + let ⟨_, hN, h₁, h₂⟩ := h + le_antisymm h₂ h₁ ▸ hN + +/-- The identity inclusion `M ≤ M` is split exactly when `M` is a type I factor. -/ +lemma isSplitInclusion_self_iff {M : VonNeumannAlgebra H} : + IsSplitInclusion M M ↔ IsTypeIFactor M := + ⟨IsSplitInclusion.isTypeIFactor_of_self, IsTypeIFactor.isSplitInclusion_self⟩ + +/-- **Split tensor decomposition.** A split inclusion `A ≤ M ≤ B` is spatially tensor-split: +there is a linear isometric equivalence `U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)` (with `e` a minimal projection +of the interpolating type I factor `M`) under which `M` becomes exactly the tensor factor +`𝓑(ℓ²(F)) ⊗̄ 1` and `M′` the factor `1 ⊗̄ 𝓑(eH)`, so that `A` lands in the left factor and the +commutant `B′` in the right factor. This is the inclusion-form structural consequence of the +split property (Doplicher–Longo; Buchholz), inherited from +`IsFactor.exists_split_tensor_decomposition`. + +The interpolating factor is returned with its full type I factoriality and its minimal projection, +and the index set `F` with its nonemptiness, so that a consumer needs no reconstruction. -/ +theorem IsSplitInclusion.exists_tensor_decomposition {A B : VonNeumannAlgebra H} + (h : IsSplitInclusion A B) : + ∃ (M : VonNeumannAlgebra H) (e : H →L[ℂ] H) (F : Set (H →L[ℂ] H)) + (U : H ≃ₗᵢ[ℂ] lp (fun _ : F => ℂ) 2 ⊗̂ LinearMap.range (e : H →ₗ[ℂ] H)), + IsTypeIFactor M ∧ IsMinimalProjection M e ∧ Nonempty F ∧ A ≤ M ∧ M ≤ B ∧ + VonNeumannAlgebra.conj U M = vnTensorLeft ∧ + VonNeumannAlgebra.conj U M′ = vnTensorRight ∧ + VonNeumannAlgebra.conj U A ≤ vnTensorLeft ∧ + VonNeumannAlgebra.conj U B′ ≤ vnTensorRight := by + obtain ⟨M, hMt, h₁, h₂⟩ := h + obtain ⟨hMf, e, he⟩ := hMt + obtain ⟨F, U, hF, hM, hM', hA, hB⟩ := hMf.exists_split_tensor_decomposition he h₁ h₂ + exact ⟨M, e, F, U, ⟨hMf, e, he⟩, he, hF, h₁, h₂, hM, hM', hA, hB⟩ + +/-! ### A non-split inclusion: the diagonal algebra on `ℂ²` + +The negative control for `IsSplitInclusion`. Everything above inhabits the *positive* side of the +predicate; this section keeps the negations of `IsFactor`, `IsTypeIFactor` and `IsSplitInclusion` +inhabited, realizing the minimal non-split witness `ℂ⊕ℂ ⊆ ℂ⊕ℂ` of the extraction note +`docs/math/split-inclusion.md` (degeneracy table: a commutative algebra is split in itself only +when it is `ℂ·1`). Without it nothing built in this repository would distinguish `IsSplitInclusion` +from plain inclusion, nor `IsFactor` from `True`. + +It lives here, next to the predicate it refutes, rather than among the local-net witnesses: it +mentions no net, no index set and no representation. +-/ + +section Diagonal + +open InnerProductSpace + +/-- The rank-one projection `|e₀⟩⟨e₀|` onto the first coordinate of `ℂ²`: self-adjoint +(`star_diagonalProjection`) and not a scalar (`diagonalProjection_ne_smul_one`), it generates +the diagonal algebra below and witnesses its nontrivial centre. -/ +noncomputable def diagonalProjection : + EuclideanSpace ℂ (Fin 2) →L[ℂ] EuclideanSpace ℂ (Fin 2) := + rankOne ℂ (EuclideanSpace.single 0 1) (EuclideanSpace.single 0 1) + +/-- The **diagonal algebra** `ℂ ⊕ ℂ` on `ℂ²`: the commutant of the rank-one projection onto the +first coordinate — concretely, the operators diagonal in the standard basis. The smallest +von Neumann algebra in the repository that is a counterexample rather than a witness. -/ +noncomputable def diagonalAlgebra : VonNeumannAlgebra (EuclideanSpace ℂ (Fin 2)) := + commutantSet {diagonalProjection} + +/-- The rank-one projection onto a coordinate is self-adjoint. -/ +lemma star_diagonalProjection : star diagonalProjection = diagonalProjection := by + rw [diagonalProjection, ContinuousLinearMap.star_eq_adjoint, adjoint_rankOne] + +/-- The generating projection acts as `v ↦ v₀ • e₀`. -/ +lemma diagonalProjection_apply (v : EuclideanSpace ℂ (Fin 2)) : + diagonalProjection v = v 0 • EuclideanSpace.single 0 1 := by + rw [diagonalProjection, rankOne_apply, EuclideanSpace.inner_single_left, map_one, one_mul] + +/-- The generating projection lies in the diagonal algebra: it commutes with itself and, being +self-adjoint, with its own adjoint. -/ +lemma diagonalProjection_mem : diagonalProjection ∈ diagonalAlgebra := by + rw [diagonalAlgebra, mem_commutantSet_iff] + rintro g rfl + exact ⟨rfl, by rw [star_diagonalProjection]⟩ + +/-- The generating projection lies in the commutant of the diagonal algebra: every member of the +commutant of `{diagonalProjection}` commutes with it by definition. -/ +lemma diagonalProjection_mem_commutant : diagonalProjection ∈ diagonalAlgebra.commutant := by + rw [mem_commutant_iff] + intro g hg + rw [diagonalAlgebra, mem_commutantSet_iff] at hg + exact (hg diagonalProjection rfl).1.symm + +/-- The generating projection is not a scalar: it fixes `e₀` and kills `e₁`, so `c • 1` would +force `c = 1` and `c = 0` at once. This is the nontrivial centre of the diagonal algebra. -/ +lemma diagonalProjection_ne_smul_one (c : ℂ) : diagonalProjection ≠ c • 1 := by + intro h + have h0 : diagonalProjection (EuclideanSpace.single 0 1) 0 = c := by + rw [h] + simp + have h1 : diagonalProjection (EuclideanSpace.single 1 1) 1 = c := by + rw [h] + simp + rw [diagonalProjection_apply] at h0 h1 + simp at h0 h1 + exact one_ne_zero (h0.trans h1.symm) + +/-- **The diagonal algebra is not a factor**: its generating projection lies in its centre and is +not a scalar. The first refuted `IsFactor` in the repository — without it nothing built here +distinguishes `IsFactor` from `True`. -/ +theorem not_isFactor_diagonalAlgebra : ¬ IsFactor diagonalAlgebra := fun h => + let ⟨c, hc⟩ := h diagonalProjection diagonalProjection_mem diagonalProjection_mem_commutant + diagonalProjection_ne_smul_one c hc + +/-- The diagonal algebra is not a type I factor, not being a factor at all. -/ +theorem not_isTypeIFactor_diagonalAlgebra : ¬ IsTypeIFactor diagonalAlgebra := fun h => + not_isFactor_diagonalAlgebra h.1 + +/-- **A non-split inclusion** — the extraction note's minimal witness `ℂ⊕ℂ ⊆ ℂ⊕ℂ`: the identity +inclusion of the diagonal algebra on `ℂ²` is not split. An interpolating type I factor squeezed +between `diagonalAlgebra` and itself would *be* `diagonalAlgebra` +(`IsSplitInclusion.isTypeIFactor_of_self`), which is not a factor. This keeps the negation of +`IsSplitInclusion` inhabited: without it nothing built in the repository distinguishes the +predicate from plain inclusion. -/ +theorem not_isSplitInclusion_diagonalAlgebra : + ¬ IsSplitInclusion diagonalAlgebra diagonalAlgebra := fun h => + not_isTypeIFactor_diagonalAlgebra h.isTypeIFactor_of_self + +end Diagonal + +end VonNeumannAlgebra diff --git a/QuantumSystem/Algebra/VonNeumannAlgebra/StructureTheorem.lean b/QuantumSystem/Algebra/VonNeumannAlgebra/StructureTheorem.lean new file mode 100644 index 0000000..9f13e75 --- /dev/null +++ b/QuantumSystem/Algebra/VonNeumannAlgebra/StructureTheorem.lean @@ -0,0 +1,603 @@ +module + +public import QuantumSystem.Algebra.VonNeumannAlgebra.SpatialDecomposition +public import QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor +public import QuantumSystem.Algebra.Star.DoubleCommutant.SOTClosedSubAlgebra +public import QuantumSystem.ForMathlib.Analysis.VonNeumannAlgebra.Commutant +public import Mathlib.Algebra.Star.Subalgebra + +/-! +# The structure theorem for type I factors + +This file proves the spatial structure theorem for a type I factor `N ⊆ B(H)` with minimal +projection `e`: there is a linear isometric equivalence `U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)` under which `N` +becomes exactly the tensor factor `B(ℓ²(F)) ⊗̄ 1` and `N'` becomes the right factor `1 ⊗̄ B(eH)`. +The ingredients — the covering orthogonal family `F` (`OrthEquivFam`), the matrix units +`e_{pq} = v_p v_q⋆`, and the spatial isomorphism `multiplicityEquiv` — are built in +`QuantumSystem.Algebra.VonNeumannAlgebra.SpatialDecomposition`. The proof here has two halves. + +**Generation half.** The system of matrix units generates `N` as a von Neumann algebra: + +> `VonNeumannAlgebra.generated S = N`, where `S = { e_{pq} | p q : F }`. + +This is the von-Neumann-algebraic (double-commutant) reformulation of the strong-operator +reconstruction `a = Σ_{pq} c_{pq}(a) e_{pq}`. The reformulation avoids any strong/weak-operator +infinite-sum API (which Mathlib lacks) by working through the commutant: the inclusion `S'' ⊆ N` +is monotonicity, and the content `N ⊆ S''` is the statement that every operator commuting with all +matrix units commutes with all of `N`. The proof of that content +(`commutes_of_mem_centralizer`) is genuinely analytic: it inserts the resolution of the identity +`Σ_r p_r = 1` (here `p_r = e_{rr} = v_r v_r⋆`) as a `HasSum`, and uses that each `↑p · a · ↑r` is +a *scalar* multiple of the matrix unit `e_{pr}` (multiplicity one, `exists_matrixEntry`), hence +commutes with `b`. The sum over the possibly-infinite index set `F` is handled throughout by +`HasSum`/`HasSum.mapL`/`HasSum.unique`; no operator-level infinite series is formed. + +**Identification half.** The identification glues the generation theorem `N = ⟨matrix units⟩''` +to the tensor commutation theorem (`QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`) through +the spatial isomorphism `U = multiplicityEquiv ∘ lpTensorEquiv`. The key computation is that `U` +carries the matrix unit `e_{pq}` to the amplified rank-one operator `|δ_p⟩⟨δ_q| ⊗̂ 1`, where +`δ_p = lp.single 2 p 1` is the `p`-th standard basis vector of `ℓ²(F)`. + +## Main results + +* `VonNeumannAlgebra.OrthEquivFam.hasSum_resolutionOfIdentity` — the resolution of the identity + `HasSum (fun i : F => (↑i) y) y`, the analytic core. +* `VonNeumannAlgebra.OrthEquivFam.commutes_of_mem_centralizer` — an operator commuting with every + matrix unit commutes with every element of `N`. +* `VonNeumannAlgebra.OrthEquivFam.generated_matrixUnits_eq` — **the matrix units generate `N`**: + `generated S = N`. +* `VonNeumannAlgebra.OrthEquivFam.mem_sotClosure_adjoin` — the literal SOT-reconstruction corollary: + every `a ∈ N` lies in the SOT-closure of the `*`-subalgebra generated by the matrix units. +* `VonNeumannAlgebra.OrthEquivFam.coe_multiplicityEquiv_apply` — the explicit `i`-th coordinate of + `U y`: `(multiplicityEquiv y) i = v_i⋆ y`. +* `VonNeumannAlgebra.OrthEquivFam.hasSum_tmul_spatialEquiv` — the resulting expansion + `U y = ∑ᵢ δᵢ ⊗̂ (v_i⋆ y)`. +* `VonNeumannAlgebra.OrthEquivFam.conjStarAlgEquiv_matrixUnit` — `U e_{pq} U⋆ = |δ_p⟩⟨δ_q| ⊗̂ 1`. +* `VonNeumannAlgebra.OrthEquivFam.conj_spatialEquiv_eq_vnTensorLeft` — the identification + `U N U⋆ = B(ℓ²(F)) ⊗̄ 1`, with commutant companion `conj_spatialEquiv_commutant_eq_vnTensorRight`. +* `VonNeumannAlgebra.IsFactor.exists_spatial_tensor_decomposition` — the existence headline. +* `VonNeumannAlgebra.IsFactor.exists_split_tensor_decomposition` — the split tensor decomposition: + for an inclusion `A ≤ B` of von Neumann algebras with intermediate type I factor `A ≤ N ≤ B` — + a *split inclusion* in the sense of Doplicher–Longo (see + `VonNeumannAlgebra.IsSplitInclusion`) — the same `U` sends `A` into `B(ℓ²(F)) ⊗̄ 1` and the + commutant `B′` into `1 ⊗̄ B(eH)`. + +## References + +* [Yngvason] J. Yngvason, *Localization and Entanglement in Relativistic Quantum Physics*, in + *The Message of Quantum Science*, Lecture Notes in Physics 899, Springer 2015, pp. 325–348; + arXiv:1401.2652. Its §5.1 ("Causal Independence and Split Property") displays the two + equations cited below as `(38)`, `𝒜₁ ⊂ 𝒩 ⊂ 𝒜₂′`, and `(39)`, `𝒜₁ ⊂ ℬ(ℋ₁) ⊗ 𝟏` and + `𝒜₂ ⊂ 𝟏 ⊗ ℬ(ℋ₂)`. +* [DL84] S. Doplicher, R. Longo, *Standard and split inclusions of von Neumann algebras*, + Invent. Math. 75 (1984) 493–536. +* [dB74] D. Buchholz, *Product states for local algebras*, Comm. Math. Phys. 36 (1974) 287–304. + +## Notation + +`⊗̄` in the prose above is documentation shorthand for the von Neumann (spatial) tensor product of +algebras; that convention is stated in full in `QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`, +where the algebras it names (`HilbertTensor.vnTensorLeft` / `vnTensorRight`) are defined. +-/ + +@[expose] public section + +open scoped TensorProduct + +namespace VonNeumannAlgebra + +open StrongOperatorTopology HilbertTensor + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] +variable {N : VonNeumannAlgebra H} {e : H →L[ℂ] H} {F : Set (H →L[ℂ] H)} + +/-! ### The matrix units generate the factor -/ + +/-- The set of matrix units is contained in `N`. -/ +lemma OrthEquivFam.matrixUnit_subset (hF : OrthEquivFam N e F) : + Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2) ⊆ (N : Set (H →L[ℂ] H)) := by + rintro x ⟨pq, rfl⟩ + exact hF.matrixUnit_mem pq.1 pq.2 + +/-- **Resolution of the identity.** For a covering orthogonal family, the projections `↑i` sum to +the identity in the strong operator sense: `HasSum (fun i : F => (↑i) y) y` for every `y`. This is +the analytic heart of the generation theorem; it is the Hilbert-sum reconstruction of `y` from its +coordinates, with the `i`-th coordinate recognised as the orthogonal projection `(↑i) y` by +`coe_hilbertSumEquiv_apply`. -/ +theorem OrthEquivFam.hasSum_resolutionOfIdentity (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) (y : H) : + HasSum (fun i : F => (i : H →L[ℂ] H) y) y := by + have hdecomp : HasSum + (fun i : F => ((hF.hilbertSumEquiv htop y i : + LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)) : H)) y := by + have h := (hF.isHilbertSum htop).hasSum_linearIsometryEquiv_symm (hF.hilbertSumEquiv htop y) + have hy : (hF.isHilbertSum htop).linearIsometryEquiv.symm (hF.hilbertSumEquiv htop y) = y := + (hF.hilbertSumEquiv htop).symm_apply_apply y + rwa [hy] at h + simpa only [hF.coe_hilbertSumEquiv_apply htop y] using hdecomp + +/-- **The content of the generation theorem.** Any operator `b` commuting with every matrix unit +commutes with every `a ∈ N`. Equivalently `Set.centralizer S ⊆ Set.centralizer N`, which is the +nontrivial inclusion `N ⊆ S''`. + +The proof tests `a * b = b * a` against arbitrary inner products. Inserting the resolution of the +identity `Σ_r p_r = 1` between and around `a`, the commutator reduces to the family of operators +`↑p · a · ↑r`, each of which equals a *scalar* multiple of the matrix unit `e_{pr}` +(`exists_matrixEntry`) and therefore commutes with `b`. -/ +theorem OrthEquivFam.commutes_of_mem_centralizer (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + {a : H →L[ℂ] H} (ha : a ∈ N) {b : H →L[ℂ] H} + (hb : b ∈ Set.centralizer (Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2))) : + a * b = b * a := by + have hbcomm : ∀ p q : F, hF.matrixUnit p q * b = b * hF.matrixUnit p q := + fun p q => (Set.mem_centralizer_iff.mp hb) _ ⟨(p, q), rfl⟩ + -- `↑p · a · ↑r` is a scalar multiple of `e_{pr}`, hence commutes with `b`. + have hcomm_T : ∀ p r : F, ((p : H →L[ℂ] H) * a * (r : H →L[ℂ] H)) * b + = b * ((p : H →L[ℂ] H) * a * (r : H →L[ℂ] H)) := by + intro p r + obtain ⟨c, hc⟩ := hF.exists_matrixEntry he p r ha + have hT : (p : H →L[ℂ] H) * a * (r : H →L[ℂ] H) = c • hF.matrixUnit p r := by + rw [← hF.pisom_range p, ← hF.pisom_range r] + calc hF.pisom p * star (hF.pisom p) * a * (hF.pisom r * star (hF.pisom r)) + = hF.pisom p * (star (hF.pisom p) * a * hF.pisom r) * star (hF.pisom r) := by + simp only [mul_assoc] + _ = hF.pisom p * (c • e) * star (hF.pisom r) := by rw [hc] + _ = c • (hF.pisom p * e * star (hF.pisom r)) := by rw [mul_smul_comm, smul_mul_assoc] + _ = c • (hF.pisom p * star (hF.pisom r)) := by rw [hF.pisom_mul_source p] + _ = c • hF.matrixUnit p r := rfl + rw [hT, smul_mul_assoc, hbcomm p r, mul_smul_comm] + -- Test `a * b = b * a` against inner products. + apply ContinuousLinearMap.ext + intro y + apply ext_inner_left ℂ + intro x + -- For each `r`, the `r`-summands agree (insert `Σ_p p_p` to the left of `a`). + have step : ∀ r : F, inner ℂ x ((a * (r : H →L[ℂ] H) * b) y) + = inner ℂ x ((b * a * (r : H →L[ℂ] H)) y) := by + intro r + have hLp : HasSum + (fun p : F => inner ℂ x (((p : H →L[ℂ] H) * a * (r : H →L[ℂ] H) * b) y)) + (inner ℂ x ((a * (r : H →L[ℂ] H) * b) y)) := by + have h := (hF.hasSum_resolutionOfIdentity htop ((a * (r : H →L[ℂ] H) * b) y)).mapL (innerSL ℂ x) + simpa only [innerSL_apply_apply, ContinuousLinearMap.mul_apply] using h + have hRp : HasSum + (fun p : F => inner ℂ x ((b * (p : H →L[ℂ] H) * a * (r : H →L[ℂ] H)) y)) + (inner ℂ x ((b * a * (r : H →L[ℂ] H)) y)) := by + have h := (((hF.hasSum_resolutionOfIdentity htop ((a * (r : H →L[ℂ] H)) y)).mapL b).mapL (innerSL ℂ x)) + simpa only [innerSL_apply_apply, ContinuousLinearMap.mul_apply] using h + have hfun : (fun p : F => inner ℂ x (((p : H →L[ℂ] H) * a * (r : H →L[ℂ] H) * b) y)) + = (fun p : F => inner ℂ x ((b * (p : H →L[ℂ] H) * a * (r : H →L[ℂ] H)) y)) := by + funext p + have hkey : (p : H →L[ℂ] H) * a * (r : H →L[ℂ] H) * b + = b * (p : H →L[ℂ] H) * a * (r : H →L[ℂ] H) := by + rw [hcomm_T p r, ← mul_assoc, ← mul_assoc] + rw [hkey] + rw [hfun] at hLp + exact hLp.unique hRp + have hL : HasSum (fun r : F => inner ℂ x ((a * (r : H →L[ℂ] H) * b) y)) + (inner ℂ x ((a * b) y)) := by + have h := ((hF.hasSum_resolutionOfIdentity htop (b y)).mapL a).mapL (innerSL ℂ x) + simpa only [innerSL_apply_apply, ContinuousLinearMap.mul_apply] using h + have hR : HasSum (fun r : F => inner ℂ x ((b * a * (r : H →L[ℂ] H)) y)) + (inner ℂ x ((b * a) y)) := by + have h := ((hF.hasSum_resolutionOfIdentity htop y).mapL (b * a)).mapL (innerSL ℂ x) + simpa only [innerSL_apply_apply, ContinuousLinearMap.mul_apply] using h + rw [funext step] at hL + exact hL.unique hR + +/-- The centralizer of the matrix units is contained in the commutant of `N`. -/ +lemma OrthEquivFam.centralizer_subset_commutant (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) : + Set.centralizer (Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2)) + ⊆ Set.centralizer (N : Set (H →L[ℂ] H)) := by + intro b hb + rw [Set.mem_centralizer_iff] + intro a ha + exact hF.commutes_of_mem_centralizer he htop ha hb + +/-- **Matrix units generate the type I factor.** The von Neumann algebra generated by the system of +matrix units `e_{pq} = v_p v_q⋆` is `N`. This is the von-Neumann-algebraic form of the structure +theorem `N ≅ B(ℓ²(F)) ⊗̄ 1`. -/ +theorem OrthEquivFam.generated_matrixUnits_eq (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) : + VonNeumannAlgebra.generated (Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2)) = N := by + set S := Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2) with hSdef + apply le_antisymm + · exact VonNeumannAlgebra.generated_le hF.matrixUnit_subset + · intro a ha + rw [← SetLike.mem_coe] + change a ∈ ((VonNeumannAlgebra.commutantSet S).commutant : Set (H →L[ℂ] H)) + rw [VonNeumannAlgebra.coe_commutant, VonNeumannAlgebra.coe_commutantSet] + have hsub : (S ∪ star S).centralizer ⊆ (N : Set (H →L[ℂ] H)).centralizer := + (Set.centralizer_subset Set.subset_union_left).trans (hF.centralizer_subset_commutant he htop) + have h2 : ((N : Set (H →L[ℂ] H)).centralizer).centralizer + ⊆ ((S ∪ star S).centralizer).centralizer := Set.centralizer_subset hsub + exact h2 (Set.subset_centralizer_centralizer (SetLike.mem_coe.mpr ha)) + +/-- **SOT reconstruction (corollary).** Every `a ∈ N` lies in the strong-operator closure of the +`*`-subalgebra generated by the matrix units. This is the literal `a = Σ_{pq} c_{pq}(a) e_{pq}` +content of the structure theorem, in closure form, obtained from the generation theorem and the SOT +double commutant theorem. -/ +theorem OrthEquivFam.mem_sotClosure_adjoin (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + {a : H →L[ℂ] H} (ha : a ∈ N) : + (StrongOperatorTopology.toSOTEquiv a : H →SLₚₜ[RingHom.id ℂ] H) ∈ closure (Set.toSOT + (StarAlgebra.adjoin ℂ (Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2)) : + Set (H →L[ℂ] H))) := by + set S := Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2) with hSdef + apply SOTClosedSubalgebra.mem_sotClosure_of_mem_doubleCommutant_starSubalgebra (StarAlgebra.adjoin ℂ S) a + have hunion : S ∪ star S ⊆ (StarAlgebra.adjoin ℂ S : Set (H →L[ℂ] H)) := by + refine Set.union_subset (StarAlgebra.subset_adjoin ℂ S) ?_ + intro y hy + rw [Set.mem_star] at hy + have hya : star y ∈ (StarAlgebra.adjoin ℂ S : Set (H →L[ℂ] H)) := + StarAlgebra.subset_adjoin ℂ S hy + have := star_mem (s := StarAlgebra.adjoin ℂ S) hya + rwa [star_star] at this + have hgen : (VonNeumannAlgebra.generated S : Set (H →L[ℂ] H)) + = Set.centralizer (Set.centralizer (S ∪ star S)) := by + change ((VonNeumannAlgebra.commutantSet S).commutant : Set (H →L[ℂ] H)) = _ + rw [VonNeumannAlgebra.coe_commutant, VonNeumannAlgebra.coe_commutantSet] + have hmono : Set.centralizer (Set.centralizer (S ∪ star S)) + ⊆ Set.centralizer (Set.centralizer (StarAlgebra.adjoin ℂ S : Set (H →L[ℂ] H))) := + Set.centralizer_subset (Set.centralizer_subset hunion) + apply hmono + have hmem : a ∈ (VonNeumannAlgebra.generated S : Set (H →L[ℂ] H)) := by + rw [hF.generated_matrixUnits_eq he htop]; exact SetLike.mem_coe.mpr ha + rwa [hgen] at hmem + +/-! ### Spatial identification with the tensor factor `B(ℓ²(F)) ⊗̄ 1` -/ + +/-- The `i`-th standard basis vector `δ_i = lp.single 2 i 1` of `ℓ²(F) = lp (fun _ : F => ℂ) 2`. +This is a thin wrapper whose only purpose is to pin the index family `fun _ : F => ℂ`, so that the +basis vectors are unambiguous in scalar positions (such as inner products). -/ +noncomputable def lpDelta [DecidableEq F] (i : F) : lp (fun _ : F => ℂ) 2 := + lp.single (E := fun _ : F => ℂ) 2 i (1 : ℂ) + +/-- `δ i` abbreviates the standard basis vector `lpDelta i` of `ℓ²(F)`, matching the `δ_i` of the +informal text. (A Dirac bra-ket `|x⟩⟨y|` notation for the rank-one operator is deliberately not +introduced here: a leading `|` token collides with the set-builder `{y | … }` used in the `htop` +hypotheses throughout this file.) -/ +local notation "δ" => lpDelta + +omit [CompleteSpace H] in +/-- `δ_i` unfolds to the `ℓ²` standard basis vector `lp.single 2 i 1`. -/ +lemma lpDelta_apply [DecidableEq F] (i : F) : + lpDelta i = lp.single (E := fun _ : F => ℂ) 2 i (1 : ℂ) := rfl + +omit [CompleteSpace H] in +/-- The basis vectors `δ_i` are unit vectors. -/ +lemma lpDelta_norm [DecidableEq F] (i : F) : ‖lpDelta i‖ = 1 := by + rw [lpDelta_apply, lp.norm_single (by norm_num), norm_one] + +omit [CompleteSpace H] in +/-- The standard basis vectors `δ_i` span a dense subspace of `ℓ²(F)`. -/ +lemma dense_span_lpDelta [DecidableEq F] : + Dense (Submodule.span ℂ (Set.range (lpDelta : F → lp (fun _ : F => ℂ) 2)) : + Set (lp (fun _ : F => ℂ) 2)) := by + intro g + have hsum : HasSum (fun i : F => lp.single (E := fun _ : F => ℂ) 2 i (g i)) g := + lp.hasSum_single (by norm_num) g + refine mem_closure_of_tendsto hsum (Filter.Eventually.of_forall (fun s => ?_)) + refine Submodule.sum_mem _ (fun i _ => ?_) + rw [show lp.single (E := fun _ : F => ℂ) 2 i (g i) = (g i) • lpDelta i by + rw [lpDelta_apply, ← lp.single_smul, smul_eq_mul, mul_one]] + exact Submodule.smul_mem _ _ (Submodule.subset_span ⟨i, rfl⟩) + +/-- **Explicit multiplicity coordinate.** Under the spatial decomposition the `i`-th coordinate of +`y` in `ℓ²(F; eH)` is `v_i⋆ y`, where `v_i` is the equivalence partial isometry `e ≅ i`. -/ +theorem OrthEquivFam.coe_multiplicityEquiv_apply (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + (y : H) (i : F) : + ((hF.multiplicityEquiv htop y i : LinearMap.range (e : H →ₗ[ℂ] H)) : H) + = star (hF.pisom i) y := by + have hop : star (hF.pisom i) * (i : H →L[ℂ] H) = star (hF.pisom i) := by + rw [← hF.pisom_range i, ← mul_assoc, hF.pisom_source i, hF.e_mul_star_pisom i] + have h1 : ((hF.multiplicityEquiv htop y i : LinearMap.range (e : H →ₗ[ℂ] H)) : H) + = star (hF.pisom i) + ((hF.hilbertSumEquiv htop y i : LinearMap.range ((i : H →L[ℂ] H) : H →ₗ[ℂ] H)) : H) := + (hF.pisom_isPI i).coe_sourceRangeEquiv_symm (hF.pisom_source i) (hF.pisom_range i) _ + rw [h1, hF.coe_hilbertSumEquiv_apply htop y i, ← ContinuousLinearMap.mul_apply, hop] + +/-- **The spatial isomorphism.** A covering orthogonal family of `e`-equivalent projections gives a +linear isometric equivalence `U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)` of `H` with the completed Hilbert tensor +product, identifying `H` with `ℓ²(F) ⊗̂ (eH)` via `multiplicityEquiv` and the tensor bridge. This is +the isomorphism implementing the type I structure theorem `N ≅ B(ℓ²(F)) ⊗̄ 1`. -/ +noncomputable def OrthEquivFam.spatialEquiv (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] [DecidableEq F] : + H ≃ₗᵢ[ℂ] HilbertTensor (lp (fun _ : F => ℂ) 2) (LinearMap.range (e : H →ₗ[ℂ] H)) := + (hF.multiplicityEquiv htop).trans (lpTensorEquiv (ι := F) (K := LinearMap.range (e : H →ₗ[ℂ] H))) + +/-- **Spatial expansion of `U`.** The spatial isomorphism `U = multiplicityEquiv ∘ lpTensorEquiv` +expands a vector as `U y = ∑ᵢ δᵢ ⊗̂ (i-th multiplicity coordinate of y)`. -/ +theorem OrthEquivFam.hasSum_tmul_spatialEquiv (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] [DecidableEq F] (y : H) : + HasSum (fun i : F => tmul (δ i) (hF.multiplicityEquiv htop y i)) + (hF.spatialEquiv htop y) := by + have h := (lp.hasSum_single (E := fun _ : F => LinearMap.range (e : H →ₗ[ℂ] H)) (p := 2) + (by norm_num) (hF.multiplicityEquiv htop y)).mapL + (lpTensorEquiv (ι := F) + (K := LinearMap.range (e : H →ₗ[ℂ] H))).toLinearIsometry.toContinuousLinearMap + simpa only [OrthEquivFam.spatialEquiv, lpDelta, LinearIsometryEquiv.coe_toLinearIsometry, + LinearIsometry.coe_toContinuousLinearMap, lpTensorEquiv_single, + LinearIsometryEquiv.trans_apply] using h + +attribute [local irreducible] OrthEquivFam.spatialEquiv OrthEquivFam.multiplicityEquiv + +/-- The `p`-th coordinate of `U (e_{pq} y)` collapses to the single term `δ_p ⊗̂ (v_q⋆ y)`: every +other coordinate vanishes because `v_i⋆ e_{pq} = 0` for `i ≠ p`. -/ +lemma OrthEquivFam.spatialEquiv_matrixUnit_apply (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] [DecidableEq F] (p q : F) (y : H) : + hF.spatialEquiv htop (hF.matrixUnit p q y) + = tmul (δ p) (hF.multiplicityEquiv htop (hF.matrixUnit p q y) p) := by + refine (hF.hasSum_tmul_spatialEquiv htop (hF.matrixUnit p q y)).unique ?_ + refine hasSum_single p (fun i hi => ?_) + have hcoe : ((hF.multiplicityEquiv htop (hF.matrixUnit p q y) i : + LinearMap.range (e : H →ₗ[ℂ] H)) : H) = 0 := by + rw [hF.coe_multiplicityEquiv_apply htop] + have h0 : star (hF.pisom i) * hF.matrixUnit p q = 0 := by + rw [OrthEquivFam.matrixUnit_def, ← mul_assoc, + hF.star_pisom_mul_pisom_of_ne hi, zero_mul] + rw [← ContinuousLinearMap.mul_apply, h0, ContinuousLinearMap.zero_apply] + rw [show hF.multiplicityEquiv htop (hF.matrixUnit p q y) i = 0 from + Subtype.ext (by rw [hcoe]; rfl), ← tmulRightL_apply, map_zero] + +/-- The surviving coordinates agree: `v_p⋆ (e_{pq} y) = v_q⋆ y`. -/ +lemma OrthEquivFam.multiplicityEquiv_matrixUnit_coord (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + (p q : F) (y : H) : + hF.multiplicityEquiv htop (hF.matrixUnit p q y) p = hF.multiplicityEquiv htop y q := by + apply Subtype.ext + rw [hF.coe_multiplicityEquiv_apply htop, hF.coe_multiplicityEquiv_apply htop] + have hpp : star (hF.pisom p) * hF.matrixUnit p q = star (hF.pisom q) := by + rw [OrthEquivFam.matrixUnit_def, ← mul_assoc, hF.pisom_source p, hF.e_mul_star_pisom q] + rw [← ContinuousLinearMap.mul_apply, hpp] + +/-- The amplified rank-one operator on `U y` collapses to the single term `δ_p ⊗̂ (v_q⋆ y)`: the +rank-one operator picks out the `q`-th coordinate. -/ +lemma OrthEquivFam.amplifyLeft_rankOne_spatialEquiv (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] [DecidableEq F] (p q : F) (y : H) : + amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q)) (hF.spatialEquiv htop y) + = tmul (δ p) (hF.multiplicityEquiv htop y q) := by + have key : ∀ i : F, amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q)) + (tmul (δ i) (hF.multiplicityEquiv htop y i)) + = (inner ℂ (δ q) (δ i) : ℂ) • tmul (δ p) (hF.multiplicityEquiv htop y i) := + fun i => by rw [amplifyLeft_tmul, InnerProductSpace.rankOne_apply, tmul_smul_left] + have hqq : (inner ℂ (δ q) (δ q) : ℂ) = 1 := by + rw [lpDelta_apply, lp.inner_single_left, lp.coeFn_single, Pi.single_eq_same, + RCLike.inner_apply, map_one, mul_one] + have hz : ∀ i : F, i ≠ q → amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q)) + (tmul (δ i) (hF.multiplicityEquiv htop y i)) = 0 := by + intro i hi + have hzero : (inner ℂ (δ q) (δ i) : ℂ) = 0 := by + rw [lpDelta_apply, lpDelta_apply, lp.inner_single_left, lp.coeFn_single, + Pi.single_eq_of_ne (Ne.symm hi), inner_zero_right] + rw [key i, hzero, zero_smul] + have hval : amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q)) + (tmul (δ q) (hF.multiplicityEquiv htop y q)) + = tmul (δ p) (hF.multiplicityEquiv htop y q) := by + rw [key q, hqq, one_smul] + exact (((hF.hasSum_tmul_spatialEquiv htop y).mapL + (amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q)))).unique + (hasSum_single q hz)).trans hval + +/-- **Intertwining relation.** The spatial isomorphism intertwines the matrix unit `e_{pq}` with +the amplified rank-one operator: `U (e_{pq} y) = (|δ_p⟩⟨δ_q| ⊗̂ 1) (U y)`. -/ +lemma OrthEquivFam.spatialEquiv_intertwine (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] [DecidableEq F] (p q : F) (y : H) : + hF.spatialEquiv htop (hF.matrixUnit p q y) + = amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q)) (hF.spatialEquiv htop y) := + (hF.spatialEquiv_matrixUnit_apply htop p q y).trans + ((congrArg (tmul (δ p)) (hF.multiplicityEquiv_matrixUnit_coord htop p q y)).trans + (hF.amplifyLeft_rankOne_spatialEquiv htop p q y).symm) + +/-- **Matrix unit identifies with an amplified rank-one operator.** Under the spatial isomorphism +`U`, the matrix unit `e_{pq}` is carried to the amplification of the rank-one operator +`|δ_p⟩⟨δ_q|` on `ℓ²(F)`: `U e_{pq} U⋆ = |δ_p⟩⟨δ_q| ⊗̂ 1`. This is the algebraic heart of the +identification `N ≅ B(ℓ²(F)) ⊗̄ 1`. -/ +theorem OrthEquivFam.conjStarAlgEquiv_matrixUnit (hF : OrthEquivFam N e F) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] [DecidableEq F] (p q : F) : + (hF.spatialEquiv htop).conjStarAlgEquiv (hF.matrixUnit p q) + = amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q)) := by + refine ContinuousLinearMap.ext fun w => ?_ + rw [LinearIsometryEquiv.conjStarAlgEquiv_apply_apply] + exact (hF.spatialEquiv_intertwine htop p q ((hF.spatialEquiv htop).symm w)).trans + (congrArg (amplifyLeft (InnerProductSpace.rankOne ℂ (δ p) (δ q))) + (LinearIsometryEquiv.apply_symm_apply (hF.spatialEquiv htop) w)) + +omit [CompleteSpace H] in +/-- **The amplified basis rank-one operators generate `B(ℓ²(F)) ⊗̄ 1`.** The von Neumann algebra +generated by `{|δ_p⟩⟨δ_q| ⊗̂ 1 : p q : F}` is the tensor factor `vnTensorLeft = B(ℓ²(F)) ⊗̄ 1`. The +proof computes the commutant: an operator commuting with all the amplified rank-one operators lies +in `1 ⊗̄ B(eH)` (the dense slice lemma, using that `{δ_p}` spans densely), and conversely +`1 ⊗̄ B(eH)` commutes with them; so the commutant is `vnTensorRight`, whose commutant is +`vnTensorLeft`. -/ +lemma generated_amplifyLeft_rankOne_eq [Nonempty F] [DecidableEq F] + [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] : + VonNeumannAlgebra.generated (Set.range (fun pq : F × F => + amplifyLeft (H₂ := LinearMap.range (e : H →ₗ[ℂ] H)) + (InnerProductSpace.rankOne ℂ (δ pq.1) (δ pq.2)))) + = vnTensorLeft := by + set T := Set.range (fun pq : F × F => amplifyLeft (H₂ := LinearMap.range (e : H →ₗ[ℂ] H)) + (InnerProductSpace.rankOne ℂ (δ pq.1) (δ pq.2))) with hTdef + have hcomm : VonNeumannAlgebra.commutantSet T = vnTensorRight := by + refine le_antisymm (fun x hx => ?_) ?_ + · refine mem_vnTensorRight_of_commutes_dense (δ (Classical.arbitrary F)) + (lpDelta_norm _) (Set.range lpDelta) dense_span_lpDelta x (fun f hf => ?_) + obtain ⟨p, rfl⟩ := hf + have hg : amplifyLeft (H₂ := LinearMap.range (e : H →ₗ[ℂ] H)) + (InnerProductSpace.rankOne ℂ (δ p) (δ (Classical.arbitrary F))) ∈ T := + ⟨(p, Classical.arbitrary F), rfl⟩ + have hcx := (VonNeumannAlgebra.mem_commutantSet_iff.mp hx _ hg).1 + exact hcx.symm + · refine VonNeumannAlgebra.generated_le ?_ + rintro g ⟨B, rfl⟩ + rw [SetLike.mem_coe, VonNeumannAlgebra.mem_commutantSet_iff] + rintro h ⟨pq, rfl⟩ + refine ⟨amplifyLeft_comp_amplifyRight + (InnerProductSpace.rankOne ℂ (δ pq.1) (δ pq.2)) B, ?_⟩ + rw [amplifyLeft_star] + exact amplifyLeft_comp_amplifyRight + (star (InnerProductSpace.rankOne ℂ (δ pq.1) (δ pq.2))) B + haveI : Nontrivial (lp (fun _ : F => ℂ) 2) := + ⟨lpDelta (Classical.arbitrary F), 0, by + rw [← norm_ne_zero_iff, lpDelta_norm]; norm_num⟩ + exact (congrArg VonNeumannAlgebra.commutant hcomm).trans + (vnTensorRight_commutant (H₁ := lp (fun _ : F => ℂ) 2) + (H₂ := LinearMap.range (e : H →ₗ[ℂ] H))) + +/-- **Type I factor structure theorem (explicit identification).** Under the spatial isomorphism +`U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)`, the type I factor `N` is carried exactly onto the tensor factor +`B(ℓ²(F)) ⊗̄ 1`: `U N U⋆ = B(ℓ²(F)) ⊗̄ 1`. This glues the matrix-unit generation theorem +`N = ⟨e_{pq}⟩''` to the identification `U e_{pq} U⋆ = |δ_p⟩⟨δ_q| ⊗̂ 1` through the fact that the +spatial conjugation commutes with the generated-algebra construction. -/ +theorem OrthEquivFam.conj_spatialEquiv_eq_vnTensorLeft (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [Nonempty F] [DecidableEq F] [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] : + VonNeumannAlgebra.conj (hF.spatialEquiv htop) N = vnTensorLeft := by + have himg : ⇑(hF.spatialEquiv htop).conjStarAlgEquiv '' + (Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2)) + = Set.range (fun pq : F × F => + amplifyLeft (H₂ := LinearMap.range (e : H →ₗ[ℂ] H)) + (InnerProductSpace.rankOne ℂ (δ pq.1) (δ pq.2))) := by + rw [← Set.range_comp] + exact congrArg Set.range (funext fun pq => hF.conjStarAlgEquiv_matrixUnit htop pq.1 pq.2) + calc VonNeumannAlgebra.conj (hF.spatialEquiv htop) N + = VonNeumannAlgebra.conj (hF.spatialEquiv htop) + (VonNeumannAlgebra.generated (Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2))) := + congrArg (VonNeumannAlgebra.conj (hF.spatialEquiv htop)) + (hF.generated_matrixUnits_eq he htop).symm + _ = VonNeumannAlgebra.generated (⇑(hF.spatialEquiv htop).conjStarAlgEquiv '' + Set.range (fun pq : F × F => hF.matrixUnit pq.1 pq.2)) := + VonNeumannAlgebra.conj_generated _ _ + _ = VonNeumannAlgebra.generated (Set.range (fun pq : F × F => + amplifyLeft (H₂ := LinearMap.range (e : H →ₗ[ℂ] H)) + (InnerProductSpace.rankOne ℂ (δ pq.1) (δ pq.2)))) := + congrArg VonNeumannAlgebra.generated himg + _ = vnTensorLeft := generated_amplifyLeft_rankOne_eq + +/-- **Type I factor structure theorem (commutant).** The same spatial isomorphism carries the +commutant `N'` onto `1 ⊗̄ B(eH)`: `U N' U⋆ = 1 ⊗̄ B(eH)`. This is the companion of +`conj_spatialEquiv_eq_vnTensorLeft`, obtained from it because conjugation commutes with taking +commutants and `(B(ℓ²(F)) ⊗̄ 1)' = 1 ⊗̄ B(eH)`. -/ +theorem OrthEquivFam.conj_spatialEquiv_commutant_eq_vnTensorRight (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [Nonempty F] [DecidableEq F] [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] : + VonNeumannAlgebra.conj (hF.spatialEquiv htop) N′ = vnTensorRight := by + haveI : Nontrivial (lp (fun _ : F => ℂ) 2) := + ⟨lpDelta (Classical.arbitrary F), 0, by + rw [← norm_ne_zero_iff, lpDelta_norm]; norm_num⟩ + rw [← VonNeumannAlgebra.conj_commutant, hF.conj_spatialEquiv_eq_vnTensorLeft he htop, + vnTensorLeft_commutant] + +/-- **Left factor absorption.** Any von Neumann subalgebra `A ≤ N` of the type I factor `N` +is carried by the spatial isomorphism into the left tensor factor: `U A U⋆ ≤ B(ℓ²(F)) ⊗̄ 1`. This +is monotonicity of spatial conjugation composed with `U N U⋆ = vnTensorLeft`. -/ +lemma OrthEquivFam.conj_spatialEquiv_le_vnTensorLeft (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [Nonempty F] [DecidableEq F] [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] + {A : VonNeumannAlgebra H} (h₁ : A ≤ N) : + VonNeumannAlgebra.conj (hF.spatialEquiv htop) A ≤ vnTensorLeft := + (VonNeumannAlgebra.conj_mono _ h₁).trans_eq (hF.conj_spatialEquiv_eq_vnTensorLeft he htop) + +/-- **Right factor absorption.** For any von Neumann algebra `B` containing `N`, the commutant +`B′` is carried by the spatial isomorphism into the right tensor factor: `U B′ U⋆ ≤ 1 ⊗̄ B(eH)`. +Taking commutants in `N ≤ B` gives `B′ ≤ N′`, and monotonicity composed with +`U N′ U⋆ = vnTensorRight` finishes. -/ +lemma OrthEquivFam.conj_spatialEquiv_le_vnTensorRight (hF : OrthEquivFam N e F) + (he : IsMinimalProjection N e) + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) + [Nonempty F] [DecidableEq F] [CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H))] + {B : VonNeumannAlgebra H} (h₂ : N ≤ B) : + VonNeumannAlgebra.conj (hF.spatialEquiv htop) B′ ≤ vnTensorRight := + (VonNeumannAlgebra.conj_mono _ (VonNeumannAlgebra.commutant_le h₂)).trans_eq + (hF.conj_spatialEquiv_commutant_eq_vnTensorRight he htop) + +omit [CompleteSpace H] in +/-- A covering orthogonal family of a nonzero Hilbert space is nonempty: its ranges span a dense +subspace, which would be `{0}` were the family empty. -/ +lemma OrthEquivFam.nonempty_of_top [Nontrivial H] {F : Set (H →L[ℂ] H)} + (htop : (Submodule.span ℂ {y | ∃ f ∈ F, ∃ x, f x = y}).topologicalClosure = ⊤) : + Nonempty F := by + rw [← not_isEmpty_iff] + intro hempty + have hset : {y : H | ∃ f ∈ F, ∃ x, f x = y} = (∅ : Set H) := by + ext y + simp only [Set.mem_setOf_eq, Set.mem_empty_iff_false, iff_false] + rintro ⟨f, hf, x, rfl⟩ + exact hempty.false ⟨f, hf⟩ + obtain ⟨z, hz⟩ := exists_ne (0 : H) + have hmem : z ∈ (⊤ : Submodule ℂ H) := Submodule.mem_top + rw [← htop, hset, Submodule.span_empty, ← SetLike.mem_coe, + Submodule.topologicalClosure_coe, Submodule.bot_coe, closure_singleton, + Set.mem_singleton_iff] at hmem + exact hz hmem + +/-- **Type I factor structure theorem (existence form).** A type I factor `N ⊆ B(H)` with minimal +projection `e` (acting on a nonzero Hilbert space) is, up to a spatial isomorphism +`U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)`, exactly the tensor factor `B(ℓ²(F)) ⊗̄ 1`, with commutant `1 ⊗̄ B(eH)`: +conjugation by `U` carries `N` onto `vnTensorLeft` and `N'` onto `vnTensorRight`. This is +[Yngvason] §5.1 (38)→(39) in its model-independent von-Neumann-algebraic form (see the module +doc's reference list). -/ +theorem IsFactor.exists_spatial_tensor_decomposition {N : VonNeumannAlgebra H} + (hN : IsFactor N) {e : H →L[ℂ] H} (he : IsMinimalProjection N e) : + ∃ (F : Set (H →L[ℂ] H)) + (U : H ≃ₗᵢ[ℂ] lp (fun _ : F => ℂ) 2 ⊗̂ LinearMap.range (e : H →ₗ[ℂ] H)), + VonNeumannAlgebra.conj U N = vnTensorLeft ∧ + VonNeumannAlgebra.conj U N′ = vnTensorRight := by + haveI := he.nontrivial + obtain ⟨F, hF, htop⟩ := hN.exists_orthEquivFam_top he + haveI : CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H)) := he.1.completeSpace_range + haveI : DecidableEq F := Classical.decEq _ + haveI : Nonempty F := OrthEquivFam.nonempty_of_top htop + exact ⟨F, hF.spatialEquiv htop, hF.conj_spatialEquiv_eq_vnTensorLeft he htop, + hF.conj_spatialEquiv_commutant_eq_vnTensorRight he htop⟩ + +/-- **Split tensor decomposition ([Yngvason] §5.1 (38)→(39); see the module doc's reference +list).** Suppose an inclusion `A ≤ B` of von +Neumann algebras admits an intermediate factor `N ⊆ B(H)` with minimal projection `e` — that is, +`A ≤ N ≤ B` with `N` type I. The existence of such an `N` is precisely what makes `A ≤ B` a +*split inclusion* in the sense of Doplicher–Longo (*Standard and split inclusions of von Neumann +algebras*, Invent. Math. 75, 1984); the named form of that definition, `IsSplitInclusion`, and its +consequences live in `QuantumSystem.Algebra.VonNeumannAlgebra.SplitInclusion`. In AQFT the split +inclusions of interest are `𝓡(O₁) ≤ 𝓡(O₂)` for properly contained regions — the *split property* +of a local net (Buchholz, *Product states for local algebras*, Comm. Math. Phys. 36, 1974). + +The conclusion: there is a spatial isomorphism `U : H ≃ₗᵢ ℓ²(F) ⊗̂ (eH)` simultaneously +tensor-splitting the inclusion — `A` lands in the left factor `B(ℓ²(F)) ⊗̄ 1` and the commutant +`B′` lands in the right factor `1 ⊗̄ B(eH)`, with `N` and its commutant identified exactly. The +commuting-pair form of this statement — `A₁ ≤ N ≤ A₂′` for a pair `(A₁, A₂)` — is recovered by +instantiating `B := A₂′`. -/ +theorem IsFactor.exists_split_tensor_decomposition {N : VonNeumannAlgebra H} + (hN : IsFactor N) {e : H →L[ℂ] H} (he : IsMinimalProjection N e) + {A B : VonNeumannAlgebra H} (h₁ : A ≤ N) (h₂ : N ≤ B) : + ∃ (F : Set (H →L[ℂ] H)) + (U : H ≃ₗᵢ[ℂ] lp (fun _ : F => ℂ) 2 ⊗̂ LinearMap.range (e : H →ₗ[ℂ] H)), + Nonempty F ∧ + VonNeumannAlgebra.conj U N = vnTensorLeft ∧ + VonNeumannAlgebra.conj U N′ = vnTensorRight ∧ + VonNeumannAlgebra.conj U A ≤ vnTensorLeft ∧ + VonNeumannAlgebra.conj U B′ ≤ vnTensorRight := by + haveI := he.nontrivial + obtain ⟨F, hF, htop⟩ := hN.exists_orthEquivFam_top he + haveI : CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H)) := he.1.completeSpace_range + haveI : DecidableEq F := Classical.decEq _ + haveI : Nonempty F := OrthEquivFam.nonempty_of_top htop + exact ⟨F, hF.spatialEquiv htop, ‹Nonempty F›, hF.conj_spatialEquiv_eq_vnTensorLeft he htop, + hF.conj_spatialEquiv_commutant_eq_vnTensorRight he htop, + hF.conj_spatialEquiv_le_vnTensorLeft he htop h₁, + hF.conj_spatialEquiv_le_vnTensorRight he htop h₂⟩ + +end VonNeumannAlgebra diff --git a/QuantumSystem/Algebra/VonNeumannAlgebra/TensorFactor.lean b/QuantumSystem/Algebra/VonNeumannAlgebra/TensorFactor.lean new file mode 100644 index 0000000..b070c00 --- /dev/null +++ b/QuantumSystem/Algebra/VonNeumannAlgebra/TensorFactor.lean @@ -0,0 +1,429 @@ +module + +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.RankOne +public import QuantumSystem.ForMathlib.Analysis.InnerProductSpace.TensorProductCompletion +public import QuantumSystem.ForMathlib.Analysis.VonNeumannAlgebra.Commutant +public import QuantumSystem.Algebra.VonNeumannAlgebra.Basic +public import Mathlib.Analysis.InnerProductSpace.Adjoint + +/-! +# The tensor von Neumann factors `B(H₁) ⊗̄ 1` and `1 ⊗̄ B(H₂)` + +Using the operator amplifications `amplifyLeft : B(H₁) → B(H₁ ⊗̂ H₂)` and +`amplifyRight : B(H₂) → B(H₁ ⊗̂ H₂)` (from `TensorProductCompletion`), this file defines the two +von Neumann factors on the completed Hilbert tensor product: + +* `HilbertTensor.vnTensorLeft = B(H₁) ⊗̄ 1` — the von Neumann algebra generated by the image of + `amplifyLeft`, +* `HilbertTensor.vnTensorRight = 1 ⊗̄ B(H₂)` — the von Neumann algebra generated by the image of + `amplifyRight`. + +Here and in the files downstream, `⊗̄` is documentation shorthand for the von Neumann (spatial) +tensor product of algebras: it has no Lean declaration, and the algebras it names are the two +definitions above. The tensor notations that *are* declared live in `TensorProductCompletion`: +`⊗̂` for the completed Hilbert tensor product and `⊗ₕ` for elementary tensors. + +These are the objects in the split-property tensor decomposition `A₁ ⊆ B(H₁)⊗̄1`, +`A₂ ⊆ 1⊗̄B(H₂)` — equation (39) of J. Yngvason, *Localization and Entanglement in Relativistic +Quantum Physics*, in *The Message of Quantum Science*, Lecture Notes in Physics 899, Springer 2015, +pp. 325–348 (arXiv:1401.2652), §5.1. The file culminates in the **tensor commutation theorem** +`vnTensorLeft.commutant = vnTensorRight` (von Neumann's commutation theorem for a full factor on +the first leg): the commutant of `B(H₁)⊗̄1` is exactly `1⊗̄B(H₂)`. + +## Main definitions + +* `HilbertTensor.vnTensorLeft`, `HilbertTensor.vnTensorRight`. +* `HilbertTensor.tmulRightL f` — the inclusion `H₂ → H₁ ⊗̂ H₂`, `z ↦ f ⊗ z`. + +## Main results + +* `HilbertTensor.amplifyLeft_mem_vnTensorLeft` / `amplifyRight_mem_vnTensorRight` — the + amplified operators lie in the corresponding factor. +* `HilbertTensor.amplifyLeft_comp_amplifyRight` — the two amplifications commute. +* `HilbertTensor.mem_vnTensorRight_of_commutes` — the slice lemma: an operator commuting with all + `A ⊗̂ 1` is of the form `1 ⊗̂ S`. +* `HilbertTensor.vnTensorLeft_commutant` / `vnTensorRight_commutant` — the tensor commutation + theorem `(B(H₁)⊗̄1)' = 1⊗̄B(H₂)` and its bicommutant companion. +* `HilbertTensor.isFactor_vnTensorLeft` / `isFactor_vnTensorRight` — both tensor algebras are + factors: their common centre is trivial. +-/ + +@[expose] public section + +open scoped TensorProduct + +namespace HilbertTensor + +variable {H₁ H₂ : Type*} + [NormedAddCommGroup H₁] [InnerProductSpace ℂ H₁] + [NormedAddCommGroup H₂] [InnerProductSpace ℂ H₂] + +/-- The von Neumann factor `B(H₁) ⊗̄ 1` acting on `HilbertTensor H₁ H₂`: the von Neumann algebra +generated by the image of the left amplification `A ↦ A ⊗̂ 1`. Factor-ness is proved in +`isFactor_vnTensorLeft` (for complete legs and nontrivial `H₁`). -/ +noncomputable def vnTensorLeft : VonNeumannAlgebra (HilbertTensor H₁ H₂) := + VonNeumannAlgebra.generated (Set.range (amplifyLeft (H₂ := H₂))) + +/-- The von Neumann factor `1 ⊗̄ B(H₂)` acting on `HilbertTensor H₁ H₂`: the von Neumann algebra +generated by the image of the right amplification `B ↦ 1 ⊗̂ B`. Factor-ness is proved in +`isFactor_vnTensorRight` (for complete legs and nontrivial `H₁`). -/ +noncomputable def vnTensorRight : VonNeumannAlgebra (HilbertTensor H₁ H₂) := + VonNeumannAlgebra.generated (Set.range (amplifyRight (H₁ := H₁))) + +/-- Every left amplification `A ⊗̂ 1` lies in the factor `B(H₁) ⊗̄ 1`. -/ +theorem amplifyLeft_mem_vnTensorLeft (A : H₁ →L[ℂ] H₁) : + amplifyLeft (H₂ := H₂) A ∈ vnTensorLeft := + VonNeumannAlgebra.mem_generated_of_mem ⟨A, rfl⟩ + +/-- Every right amplification `1 ⊗̂ B` lies in the factor `1 ⊗̄ B(H₂)`. -/ +theorem amplifyRight_mem_vnTensorRight (B : H₂ →L[ℂ] H₂) : + amplifyRight (H₁ := H₁) B ∈ vnTensorRight := + VonNeumannAlgebra.mem_generated_of_mem ⟨B, rfl⟩ + +/-- The commutant of `B(H₁) ⊗̄ 1` is the commutant of the range of `amplifyLeft` (bicommutant +collapse). This reduces the tensor commutation theorem `vnTensorLeft.commutant = vnTensorRight` +to the identity `(range amplifyLeft)' = 1 ⊗̄ B(H₂)`. -/ +lemma vnTensorLeft_commutant_eq_commutantSet : + (vnTensorLeft (H₁ := H₁) (H₂ := H₂)).commutant + = VonNeumannAlgebra.commutantSet (Set.range (amplifyLeft (H₂ := H₂))) := + VonNeumannAlgebra.commutant_generated _ + +/-! ### The tensor commutation theorem + +The commutant of `B(H₁) ⊗̄ 1` is exactly `1 ⊗̄ B(H₂)` — von Neumann's commutation theorem for the +special case of a full factor on the first leg. The hard inclusion +`(B(H₁)⊗̄1)' ⊆ 1⊗̄B(H₂)` is the slice argument: an operator `T` commuting with every `A ⊗̂ 1` +must be of the form `1 ⊗̂ S`, where `S = ι_e* ∘ T ∘ ι_e` for a fixed unit vector `e ∈ H₁` and +`ι_e : H₂ → H₁ ⊗̂ H₂`, `y ↦ e ⊗ y`. The reduction to a pure-tensor computation uses the rank-one +operators `|f⟩⟨e|` on `H₁`. -/ + +/-- The bounded inclusion `H₂ → H₁ ⊗̂ H₂`, `z ↦ f ⊗ z`, for a fixed `f ∈ H₁`. Its operator norm +is `‖f‖` (`‖tmulRightL f z‖ = ‖f‖ * ‖z‖`), so it is an isometry exactly when `‖f‖ = 1`. -/ +noncomputable def tmulRightL (f : H₁) : H₂ →L[ℂ] HilbertTensor H₁ H₂ := + UniformSpace.Completion.toComplL.comp + (LinearMap.mkContinuous (TensorProduct.mk ℂ H₁ H₂ f) ‖f‖ fun z => by + rw [TensorProduct.mk_apply, TensorProduct.norm_tmul]) + +/-- The inclusion `tmulRightL f` sends `z` to the elementary tensor `f ⊗ z`. -/ +@[simp] lemma tmulRightL_apply (f : H₁) (z : H₂) : tmulRightL f z = tmul f z := rfl + +/-- The left and right amplifications commute: `(A ⊗̂ 1)(1 ⊗̂ B) = A ⊗̂ B = (1 ⊗̂ B)(A ⊗̂ 1)`. -/ +theorem amplifyLeft_comp_amplifyRight (A : H₁ →L[ℂ] H₁) (B : H₂ →L[ℂ] H₂) : + (amplifyLeft (H₂ := H₂) A).comp (amplifyRight (H₁ := H₁) B) + = (amplifyRight B).comp (amplifyLeft A) := by + refine ContinuousLinearMap.ext fun w => ?_ + refine UniformSpace.Completion.induction_on w + (isClosed_eq (by fun_prop) (by fun_prop)) (fun a => ?_) + induction a using TensorProduct.induction_on with + | zero => simp only [UniformSpace.Completion.coe_zero, map_zero] + | tmul f y => + change amplifyLeft A (amplifyRight B (tmul f y)) = amplifyRight B (amplifyLeft A (tmul f y)) + rw [amplifyRight_tmul, amplifyLeft_tmul, amplifyLeft_tmul, amplifyRight_tmul] + | add p q hp hq => rw [UniformSpace.Completion.coe_add, map_add, map_add, hp, hq] + +section CompleteRight + +variable [CompleteSpace H₂] + +/-- The adjoint of the inclusion `ι_e` acts on pure tensors as the `e`-slice +`g ⊗ z ↦ ⟪e, g⟫ • z`. -/ +lemma adjoint_tmulRightL_tmul (e g : H₁) (z : H₂) : + ContinuousLinearMap.adjoint (tmulRightL (H₂ := H₂) e) (tmul g z) = (inner ℂ e g) • z := by + refine ext_inner_right ℂ fun y => ?_ + rw [ContinuousLinearMap.adjoint_inner_left, tmulRightL_apply, inner_tmul, inner_smul_left, + inner_conj_symm] + +/-- The amplification of a rank-one operator `|f⟩⟨e|` factors through the `e`-slice and the +inclusion `ι_f`: `(|f⟩⟨e|) ⊗̂ 1 = ι_f ∘ ι_e*`. This is the algebraic heart of the slice +argument. -/ +lemma amplifyLeft_rankOne_eq (e f : H₁) : + amplifyLeft (H₂ := H₂) (InnerProductSpace.rankOne ℂ f e) + = (tmulRightL f).comp (ContinuousLinearMap.adjoint (tmulRightL e)) := by + refine ContinuousLinearMap.ext fun w => ?_ + refine UniformSpace.Completion.induction_on w + (isClosed_eq (by fun_prop) (by fun_prop)) (fun a => ?_) + induction a using TensorProduct.induction_on with + | zero => simp only [UniformSpace.Completion.coe_zero, map_zero] + | tmul g z => + change amplifyLeft _ (tmul g z) + = (tmulRightL f).comp (ContinuousLinearMap.adjoint (tmulRightL e)) (tmul g z) + rw [amplifyLeft_tmul, ContinuousLinearMap.comp_apply, adjoint_tmulRightL_tmul, + tmulRightL_apply, InnerProductSpace.rankOne_apply, + tmul_smul_left, smul_tmul_right] + | add p q hp hq => rw [UniformSpace.Completion.coe_add, map_add, map_add, hp, hq] + +/-- **Slice lemma (dense form, explicit representation).** If `T` commutes with the rank-one +amplifications `(|f⟩⟨e|) ⊗̂ 1` for `f` ranging over a set `D` whose span is dense in `H₁` (and a +fixed unit vector `e ∈ H₁`), then `T = 1 ⊗̂ S` for `S = ι_e* ∘ T ∘ ι_e`. The relation +`T (f ⊗̂ y) = f ⊗̂ (S y)` is first established for `f ∈ D` and then extended to all `f` by density, +working at the level of the bounded inclusions `ι_y : f ↦ f ⊗̂ y`. -/ +lemma exists_amplifyRight_of_commutes_dense (e : H₁) (he : ‖e‖ = 1) (D : Set H₁) + (hD : Dense (Submodule.span ℂ D : Set H₁)) + (T : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) + (hT : ∀ f ∈ D, T.comp (amplifyLeft (InnerProductSpace.rankOne ℂ f e)) + = (amplifyLeft (InnerProductSpace.rankOne ℂ f e)).comp T) : + ∃ S : H₂ →L[ℂ] H₂, T = amplifyRight S := by + set S := (ContinuousLinearMap.adjoint (tmulRightL e)).comp (T.comp (tmulRightL e)) with hS + have hee : inner ℂ e e = (1 : ℂ) := by rw [inner_self_eq_norm_sq_to_K, he]; norm_num + have key : ∀ f ∈ D, ∀ y : H₂, T (tmul f y) = tmul f (S y) := by + intro f hf y + have e1 : amplifyLeft (H₂ := H₂) (InnerProductSpace.rankOne ℂ f e) (tmul e y) = tmul f y := by + rw [amplifyLeft_tmul, InnerProductSpace.rankOne_apply, hee, one_smul] + calc T (tmul f y) + = T (amplifyLeft (InnerProductSpace.rankOne ℂ f e) (tmul e y)) := by rw [e1] + _ = amplifyLeft (InnerProductSpace.rankOne ℂ f e) (T (tmul e y)) := by + rw [← ContinuousLinearMap.comp_apply, hT f hf, ContinuousLinearMap.comp_apply] + _ = (tmulRightL f).comp (ContinuousLinearMap.adjoint (tmulRightL e)) (T (tmul e y)) := by + rw [amplifyLeft_rankOne_eq] + _ = tmul f (S y) := by + rw [ContinuousLinearMap.comp_apply, tmulRightL_apply, hS] + simp only [ContinuousLinearMap.comp_apply, tmulRightL_apply] + have key_all : ∀ (f : H₁) (y : H₂), T (tmul f y) = tmul f (S y) := by + intro f y + have hcl : T.comp (tmulLeftL y) = (amplifyRight S).comp (tmulLeftL y) := by + refine ContinuousLinearMap.ext_on hD (fun g hg => ?_) + rw [ContinuousLinearMap.comp_apply, ContinuousLinearMap.comp_apply, tmulLeftL_apply, + amplifyRight_tmul, key g hg y] + have h := congrArg (fun L : H₁ →L[ℂ] HilbertTensor H₁ H₂ => L f) hcl + simpa only [ContinuousLinearMap.comp_apply, tmulLeftL_apply, amplifyRight_tmul] using h + have hTS : T = amplifyRight S := by + refine ContinuousLinearMap.ext fun w => ?_ + refine UniformSpace.Completion.induction_on w + (isClosed_eq T.continuous (amplifyRight S).continuous) (fun a => ?_) + induction a using TensorProduct.induction_on with + | zero => simp only [UniformSpace.Completion.coe_zero, map_zero] + | tmul f y => + change T (tmul f y) = amplifyRight S (tmul f y) + rw [key_all, amplifyRight_tmul] + | add p q hp hq => rw [UniformSpace.Completion.coe_add, map_add, map_add, hp, hq] + exact ⟨S, hTS⟩ + +/-- **Slice lemma (dense form).** If `T` commutes with the rank-one amplifications +`(|f⟩⟨e|) ⊗̂ 1` for `f` ranging over a set `D` whose span is dense in `H₁` (and a fixed unit +vector `e ∈ H₁`), then `T = 1 ⊗̂ S`, hence `T ∈ 1 ⊗̄ B(H₂)`. -/ +lemma mem_vnTensorRight_of_commutes_dense (e : H₁) (he : ‖e‖ = 1) (D : Set H₁) + (hD : Dense (Submodule.span ℂ D : Set H₁)) + (T : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) + (hT : ∀ f ∈ D, T.comp (amplifyLeft (InnerProductSpace.rankOne ℂ f e)) + = (amplifyLeft (InnerProductSpace.rankOne ℂ f e)).comp T) : + T ∈ vnTensorRight := by + obtain ⟨S, rfl⟩ := exists_amplifyRight_of_commutes_dense e he D hD T hT + exact amplifyRight_mem_vnTensorRight S + +/-- **Slice lemma (explicit representation).** If `T` commutes with every left amplification +`A ⊗̂ 1`, then `T = 1 ⊗̂ S` for `S = ι_e* ∘ T ∘ ι_e` (any unit vector `e ∈ H₁`). -/ +lemma exists_amplifyRight_of_commutes (e : H₁) (he : ‖e‖ = 1) + (T : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) + (hT : ∀ A : H₁ →L[ℂ] H₁, T.comp (amplifyLeft A) = (amplifyLeft A).comp T) : + ∃ S : H₂ →L[ℂ] H₂, T = amplifyRight S := + exists_amplifyRight_of_commutes_dense e he Set.univ + (by rw [Submodule.span_univ, Submodule.top_coe]; exact dense_univ) T + (fun f _ => hT (InnerProductSpace.rankOne ℂ f e)) + +/-- **Slice lemma (hard inclusion of the commutation theorem).** If `T` commutes with every left +amplification `A ⊗̂ 1`, then `T = 1 ⊗̂ S` for `S = ι_e* ∘ T ∘ ι_e` (any unit vector `e ∈ H₁`), +hence `T ∈ 1 ⊗̄ B(H₂)`. -/ +theorem mem_vnTensorRight_of_commutes (e : H₁) (he : ‖e‖ = 1) + (T : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) + (hT : ∀ A : H₁ →L[ℂ] H₁, T.comp (amplifyLeft A) = (amplifyLeft A).comp T) : + T ∈ vnTensorRight := by + obtain ⟨S, rfl⟩ := exists_amplifyRight_of_commutes e he T hT + exact amplifyRight_mem_vnTensorRight S + +end CompleteRight + +/-- **Tensor commutation theorem.** The commutant of the von Neumann factor `B(H₁) ⊗̄ 1` is exactly +`1 ⊗̄ B(H₂)`. This is von Neumann's commutation theorem `(M ⊗ 1)' = M' ⊗ B(H₂)` for `M = B(H₁)` +(whose commutant `M'` is `ℂ`), and it is the analytic core of the split-property tensor +decomposition: combined with `vnTensorLeft_commutant_eq_commutantSet` and a unit vector in `H₁`, +it identifies `A₁ ⊆ B(H₁)⊗̄1` with `A₂ ⊆ 1⊗̄B(H₂)` for commuting `A₁, A₂`. -/ +theorem vnTensorLeft_commutant [CompleteSpace H₁] [CompleteSpace H₂] [Nontrivial H₁] : + (vnTensorLeft (H₁ := H₁) (H₂ := H₂)).commutant = vnTensorRight := by + rw [vnTensorLeft_commutant_eq_commutantSet] + refine le_antisymm ?_ ?_ + · -- hard inclusion: every `T` commuting with `B(H₁)⊗̄1` lies in `1⊗̄B(H₂)` + obtain ⟨x, hx⟩ := exists_ne (0 : H₁) + have he : ‖(‖x‖⁻¹ : ℂ) • x‖ = 1 := by + rw [norm_smul, norm_inv, Complex.norm_real, norm_norm, + inv_mul_cancel₀ (norm_ne_zero_iff.mpr hx)] + intro T hT + refine mem_vnTensorRight_of_commutes _ he T fun A => ?_ + have hc := (VonNeumannAlgebra.mem_commutantSet_iff.mp hT (amplifyLeft A) ⟨A, rfl⟩).1 + rw [← ContinuousLinearMap.mul_def, ← ContinuousLinearMap.mul_def] + exact hc.symm + · -- easy inclusion: `1⊗̄B(H₂)` commutes with `B(H₁)⊗̄1` + change VonNeumannAlgebra.generated (Set.range (amplifyRight (H₁ := H₁))) + ≤ VonNeumannAlgebra.commutantSet (Set.range (amplifyLeft (H₂ := H₂))) + refine VonNeumannAlgebra.generated_le ?_ + rintro g ⟨B, rfl⟩ + rw [SetLike.mem_coe, VonNeumannAlgebra.mem_commutantSet_iff] + rintro h ⟨A, rfl⟩ + refine ⟨?_, ?_⟩ + · rw [ContinuousLinearMap.mul_def, ContinuousLinearMap.mul_def] + exact amplifyLeft_comp_amplifyRight A B + · rw [amplifyLeft_star, ContinuousLinearMap.mul_def, ContinuousLinearMap.mul_def] + exact amplifyLeft_comp_amplifyRight (star A) B + +/-- The commutant of `1 ⊗̄ B(H₂)` is exactly `B(H₁) ⊗̄ 1`: the bicommutant form of the tensor +commutation theorem `vnTensorLeft_commutant`. -/ +theorem vnTensorRight_commutant [CompleteSpace H₁] [CompleteSpace H₂] [Nontrivial H₁] : + (vnTensorRight (H₁ := H₁) (H₂ := H₂)).commutant = vnTensorLeft := by + rw [← vnTensorLeft_commutant, VonNeumannAlgebra.commutant_commutant] + +/-! ### The tensor factors are factors -/ + +/-- The right amplification `B ↦ 1 ⊗̂ B` is injective (for nontrivial `H₁`): evaluating on pure +tensors `f ⊗̂ y` with `f ≠ 0` recovers `B y` up to the norm factor `‖f‖`. -/ +lemma amplifyRight_injective [Nontrivial H₁] : + Function.Injective (amplifyRight (H₁ := H₁) (H₂ := H₂)) := by + intro B₁ B₂ h + obtain ⟨f, hf⟩ := exists_ne (0 : H₁) + ext y + have h1 := congrArg + (fun L : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂ => L (tmul f y)) h + simp only [amplifyRight_tmul] at h1 + have h2 : tmul f (B₁ y - B₂ y) = 0 := by + rw [sub_eq_add_neg, tmul_add, ← neg_one_smul ℂ (B₂ y), smul_tmul_right, h1, neg_one_smul, + add_neg_cancel] + have h3 : ‖f‖ * ‖B₁ y - B₂ y‖ = 0 := by rw [← norm_tmul, h2, norm_zero] + rcases mul_eq_zero.mp h3 with h4 | h4 + · exact absurd (norm_eq_zero.mp h4) hf + · exact sub_eq_zero.mp (norm_eq_zero.mp h4) + +/-- **The centre of the tensor decomposition is trivial.** An operator lying in both +`B(H₁) ⊗̄ 1` and `1 ⊗̄ B(H₂)` is a scalar: by the slice lemma it is `1 ⊗̂ S`, membership in +`B(H₁) ⊗̄ 1 = (1 ⊗̄ B(H₂))'` makes `S` commute with all of `B(H₂)` — in particular with every +rank-one operator — so `S` is scalar. -/ +lemma eq_smul_one_of_mem_vnTensorLeft_of_mem_vnTensorRight + [CompleteSpace H₁] [CompleteSpace H₂] [Nontrivial H₁] + {x : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂} + (hL : x ∈ vnTensorLeft (H₁ := H₁) (H₂ := H₂)) (hR : x ∈ vnTensorRight) : + ∃ c : ℂ, x = c • 1 := by + obtain ⟨u, hu⟩ := exists_ne (0 : H₁) + have he : ‖(‖u‖⁻¹ : ℂ) • u‖ = 1 := by + rw [norm_smul, norm_inv, Complex.norm_real, norm_norm, + inv_mul_cancel₀ (norm_ne_zero_iff.mpr hu)] + have hx' : x ∈ VonNeumannAlgebra.commutantSet (Set.range (amplifyLeft (H₂ := H₂))) := by + rw [← vnTensorLeft_commutant_eq_commutantSet, vnTensorLeft_commutant] + exact hR + obtain ⟨S, rfl⟩ := exists_amplifyRight_of_commutes _ he x fun A => by + have hc := (VonNeumannAlgebra.mem_commutantSet_iff.mp hx' (amplifyLeft A) ⟨A, rfl⟩).1 + rw [← ContinuousLinearMap.mul_def, ← ContinuousLinearMap.mul_def] + exact hc.symm + have hS : ∀ B : H₂ →L[ℂ] H₂, S * B = B * S := by + intro B + have hmem : amplifyRight (H₁ := H₁) S ∈ vnTensorRight.commutant := by + rw [vnTensorRight_commutant]; exact hL + have h1 := VonNeumannAlgebra.mem_commutant_iff.mp hmem (amplifyRight B) + (amplifyRight_mem_vnTensorRight B) + apply amplifyRight_injective (H₁ := H₁) + rw [amplifyRight_mul, amplifyRight_mul] + exact h1.symm + obtain ⟨c, hc⟩ := + ContinuousLinearMap.exists_eq_smul_one_of_forall_rankOne_comm (S := S) fun a b => by + rw [← ContinuousLinearMap.mul_def, ← ContinuousLinearMap.mul_def] + exact hS (InnerProductSpace.rankOne ℂ a b) + exact ⟨c, by rw [hc, amplifyRight_smul, amplifyRight_one]⟩ + +/-- **`B(H₁) ⊗̄ 1` is a factor**: its centre is trivial, since by the commutation theorem the +centre is `(B(H₁) ⊗̄ 1) ⊓ (1 ⊗̄ B(H₂))`. -/ +theorem isFactor_vnTensorLeft [CompleteSpace H₁] [CompleteSpace H₂] [Nontrivial H₁] : + VonNeumannAlgebra.IsFactor (vnTensorLeft (H₁ := H₁) (H₂ := H₂)) := fun _ hx hx' => + eq_smul_one_of_mem_vnTensorLeft_of_mem_vnTensorRight hx (by rwa [← vnTensorLeft_commutant]) + +/-- **`1 ⊗̄ B(H₂)` is a factor**: its centre is trivial, since by the commutation theorem the +centre is `(B(H₁) ⊗̄ 1) ⊓ (1 ⊗̄ B(H₂))`. (Nontriviality of `H₁` is what the commutation theorem +consumes; `H₂` may be arbitrary.) -/ +theorem isFactor_vnTensorRight [CompleteSpace H₁] [CompleteSpace H₂] [Nontrivial H₁] : + VonNeumannAlgebra.IsFactor (vnTensorRight (H₁ := H₁) (H₂ := H₂)) := fun _ hx hx' => + eq_smul_one_of_mem_vnTensorLeft_of_mem_vnTensorRight (by rwa [← vnTensorRight_commutant]) hx + +/-! ### `B(H₁) ⊗̄ 1` is `⋆`-isomorphic to `B(H₁)` + +The left amplification `amplifyLeft : B(H₁) → B(H₁)⊗̄1` is a `⋆`-algebra homomorphism that is +injective (`amplifyLeft_injective`) and, by the slice lemma transported through the swap +equivalence, surjective onto `vnTensorLeft` (`exists_amplifyLeft_of_mem_vnTensorLeft`). Hence it +is a `⋆`-isomorphism `B(H₁) ≃⋆ₐ B(H₁) ⊗̄ 1` (`amplifyLeftStarAlgEquiv`). -/ + +/-- The left amplification `A ↦ A ⊗̂ 1` is injective (for nontrivial `H₂`): evaluating on pure +tensors `x ⊗̂ g` with `g ≠ 0` recovers `A x` up to the norm factor `‖g‖`. -/ +lemma amplifyLeft_injective [Nontrivial H₂] : + Function.Injective (amplifyLeft (H₁ := H₁) (H₂ := H₂)) := by + intro A₁ A₂ h + obtain ⟨g, hg⟩ := exists_ne (0 : H₂) + ext x + have h1 := congrArg + (fun L : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂ => L (tmul x g)) h + simp only [amplifyLeft_tmul] at h1 + have h2 : tmul (A₁ x - A₂ x) g = 0 := by + rw [sub_eq_add_neg, add_tmul, ← neg_one_smul ℂ (A₂ x), tmul_smul_left, h1, neg_one_smul, + add_neg_cancel] + have h3 : ‖A₁ x - A₂ x‖ * ‖g‖ = 0 := by rw [← norm_tmul, h2, norm_zero] + rcases mul_eq_zero.mp h3 with h4 | h4 + · exact sub_eq_zero.mp (norm_eq_zero.mp h4) + · exact absurd (norm_eq_zero.mp h4) hg + +/-- Every right amplification `1 ⊗̂ B` commutes with the factor `B(H₁) ⊗̄ 1`, i.e. lies in its +commutant. This is the easy inclusion of the commutation theorem and needs no completeness or +nontriviality hypotheses. -/ +lemma amplifyRight_mem_commutant_vnTensorLeft [CompleteSpace H₁] (B : H₂ →L[ℂ] H₂) : + amplifyRight (H₁ := H₁) B ∈ (vnTensorLeft (H₁ := H₁) (H₂ := H₂)).commutant := by + rw [vnTensorLeft_commutant_eq_commutantSet, VonNeumannAlgebra.mem_commutantSet_iff] + rintro h ⟨A, rfl⟩ + refine ⟨?_, ?_⟩ + · rw [ContinuousLinearMap.mul_def, ContinuousLinearMap.mul_def] + exact amplifyLeft_comp_amplifyRight A B + · rw [amplifyLeft_star, ContinuousLinearMap.mul_def, ContinuousLinearMap.mul_def] + exact amplifyLeft_comp_amplifyRight (star A) B + +/-- **Surjectivity of the left amplification onto `B(H₁) ⊗̄ 1`.** Every operator in the factor +`vnTensorLeft` is a left amplification `A ⊗̂ 1`. An element `T ∈ vnTensorLeft` commutes with every +right amplification (`amplifyRight_mem_commutant_vnTensorLeft`); transporting through the swap +equivalence `commEquiv` turns this into commutation with every left amplification on the swapped +space, where the slice lemma `exists_amplifyRight_of_commutes` produces the operator. -/ +lemma exists_amplifyLeft_of_mem_vnTensorLeft [CompleteSpace H₁] [Nontrivial H₂] + {T : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂} + (hT : T ∈ vnTensorLeft (H₁ := H₁) (H₂ := H₂)) : + ∃ A : H₁ →L[ℂ] H₁, T = amplifyLeft A := by + have hcomm : ∀ B : H₂ →L[ℂ] H₂, T * amplifyRight B = amplifyRight B * T := fun B => + VonNeumannAlgebra.mem_commutant_iff.mp (amplifyRight_mem_commutant_vnTensorLeft B) T hT + obtain ⟨u, hu⟩ := exists_ne (0 : H₂) + have he : ‖(‖u‖⁻¹ : ℂ) • u‖ = 1 := by + rw [norm_smul, norm_inv, Complex.norm_real, norm_norm, + inv_mul_cancel₀ (norm_ne_zero_iff.mpr hu)] + have hhyp : ∀ A : H₂ →L[ℂ] H₂, + (commEquiv.conjStarAlgEquiv T).comp (amplifyLeft A) + = (amplifyLeft A).comp (commEquiv.conjStarAlgEquiv T) := fun A => by + rw [← ContinuousLinearMap.mul_def, ← ContinuousLinearMap.mul_def, + ← conjStarAlgEquiv_commEquiv_amplifyRight A, ← map_mul, ← map_mul, hcomm A] + obtain ⟨S, hS⟩ := exists_amplifyRight_of_commutes (H₁ := H₂) (H₂ := H₁) + ((‖u‖⁻¹ : ℂ) • u) he (commEquiv.conjStarAlgEquiv T) hhyp + refine ⟨S, ?_⟩ + have hsymm := congrArg (⇑commEquiv.conjStarAlgEquiv.symm) hS + rw [StarAlgEquiv.symm_apply_apply, conjStarAlgEquiv_symm_commEquiv_amplifyRight] at hsymm + exact hsymm + +/-- The left amplification as a unital `⋆`-algebra homomorphism `B(H₁) →⋆ₐ[ℂ] B(H₁) ⊗̄ 1` onto the +factor, i.e. with codomain restricted to `vnTensorLeft`. -/ +noncomputable def amplifyLeftVnₐ [CompleteSpace H₁] : + (H₁ →L[ℂ] H₁) →⋆ₐ[ℂ] (vnTensorLeft (H₁ := H₁) (H₂ := H₂)) where + toFun A := ⟨amplifyLeft A, amplifyLeft_mem_vnTensorLeft A⟩ + map_one' := Subtype.ext amplifyLeft_one + map_mul' A B := Subtype.ext (amplifyLeft_mul A B) + map_zero' := Subtype.ext amplifyLeft_zero + map_add' A B := Subtype.ext (amplifyLeft_add A B) + commutes' r := Subtype.ext <| by + simp [Algebra.algebraMap_eq_smul_one, amplifyLeft_smul, amplifyLeft_one] + map_star' A := Subtype.ext (amplifyLeft_star A).symm + +/-- **`B(H₁) ⊗̄ 1` is `⋆`-isomorphic to `B(H₁)`.** The left amplification is a `⋆`-isomorphism of +`B(H₁)` onto the factor `vnTensorLeft`, for nontrivial complete `H₁` and nontrivial `H₂`. -/ +noncomputable def amplifyLeftStarAlgEquiv [CompleteSpace H₁] [Nontrivial H₂] : + (H₁ →L[ℂ] H₁) ≃⋆ₐ[ℂ] (vnTensorLeft (H₁ := H₁) (H₂ := H₂)) := + StarAlgEquiv.ofBijective amplifyLeftVnₐ + ⟨fun A B hAB => amplifyLeft_injective (H₂ := H₂) (Subtype.ext_iff.mp hAB), + fun ⟨T, hT⟩ => by + obtain ⟨A, rfl⟩ := exists_amplifyLeft_of_mem_vnTensorLeft hT + exact ⟨A, rfl⟩⟩ + +end HilbertTensor diff --git a/QuantumSystem/Algebra/VonNeumannAlgebra/TypeI.lean b/QuantumSystem/Algebra/VonNeumannAlgebra/TypeI.lean new file mode 100644 index 0000000..248b02b --- /dev/null +++ b/QuantumSystem/Algebra/VonNeumannAlgebra/TypeI.lean @@ -0,0 +1,828 @@ +module + +public import QuantumSystem.Algebra.VonNeumannAlgebra.Basic +public import QuantumSystem.Algebra.VonNeumannAlgebra.StructureTheorem +public import QuantumSystem.ForMathlib.Analysis.VonNeumannAlgebra.Commutant +public import Mathlib.Analysis.InnerProductSpace.Projection.Basic +public import Mathlib.Analysis.InnerProductSpace.StarOrder +public import Mathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.PosPart.Basic +public import Mathlib.Analysis.CStarAlgebra.ContinuousFunctionalCalculus.Range +public import Mathlib.LinearAlgebra.Complex.Module + +/-! +# Type I von Neumann algebras + +The general **type I** property, phrased as in the literature (Takesaki V.1, Blackadar III.1.5): +every nonzero central projection dominates a nonzero abelian projection. For a *factor* this is +equivalent to the existence of a minimal projection, i.e. to `IsTypeIFactor`. The equivalence is +proved in full: the easy direction is minimal ⇒ abelian ⇒ type I; the converse first shows that +an abelian projection of a factor is order-minimal (a corner-commutation argument through the +central-support lemma `IsFactor.exists_mul_ne`), then that order-minimality forces the trivial +corner `p N p = ℂ p` — the positive/negative parts of a self-adjoint corner element (non-unital +continuous functional calculus inside the norm-closed corner subalgebra) yield an order +dichotomy, and a Dedekind-cut argument on `{c : ℝ | 0 ≤ x - c • p}` pins each self-adjoint +corner element to a real multiple of `p`. + +This file is also the home of the factor-level type I predicates `IsTypeIFactor` and +`IsTypeIInfinite`, and of the abstract structure theorem `IsTypeIFactor.exists_starAlgEquiv` +identifying a type I factor with `B(K)` for some Hilbert space `K` (whose spatial content lives in +`QuantumSystem.Algebra.VonNeumannAlgebra.StructureTheorem`). + +Finally, the file develops the **fundamental example** `B(H)`: the algebra of *all* bounded +operators, `𝓑(H) : VonNeumannAlgebra H` (supplied in +`QuantumSystem.Algebra.VonNeumannAlgebra.Basic`), is a factor — its centre is the scalars, by the +elementary fact that an operator commuting with every rank-one operator is scalar. It possesses a +minimal projection, namely any rank-one orthogonal projection `|u⟩⟨u|` with `‖u‖ = 1`. Hence +`B(H)` is a **type I factor**, and by the abstract structure theorem it is `⋆`-isomorphic to +`B(K)` for some Hilbert space `K` (which is `ℓ²` of an orthonormal basis of `H`); when `H` is +infinite-dimensional this exhibits `B(H)` as a **type I_∞ factor**. + +## Main definitions + +* `VonNeumannAlgebra.IsTypeI N` — every nonzero central projection of `N` dominates a nonzero + abelian projection. +* `VonNeumannAlgebra.IsTypeIFactor N` — a factor possessing a minimal projection. +* `VonNeumannAlgebra.IsTypeIInfinite N` — a type I factor carrying an infinite orthogonal family + of minimal projections (infinite multiplicity). +* `VonNeumannAlgebra.cornerNonUnitalStarSubalgebra N hp` — the norm-closed corner `{y ∈ N | p y + = y = y p}`. + +## Main results + +* `VonNeumannAlgebra.IsFactor.subprojection_eq_of_isAbelianProjection` — in a factor, an abelian + projection has no proper nonzero subprojection. +* `VonNeumannAlgebra.isMinimalProjection_of_forall_subprojection` — order-minimality implies the + trivial corner. +* `VonNeumannAlgebra.IsFactor.isMinimalProjection_of_isAbelianProjection` — in a factor, a + nonzero abelian projection is minimal. +* `VonNeumannAlgebra.IsFactor.isTypeI_iff_exists_isMinimalProjection` — a factor is type I iff + it has a minimal projection. +* `VonNeumannAlgebra.isTypeIFactor_iff_isFactor_and_isTypeI` — `IsTypeIFactor N ↔ IsFactor N ∧ + IsTypeI N`. +* `VonNeumannAlgebra.IsTypeIInfinite.exists_injective` — the witnessing minimal projections of a + type I_∞ factor may be chosen injectively. +* `VonNeumannAlgebra.IsTypeIFactor.exists_starAlgEquiv` — a type I factor is `⋆`-isomorphic to + `B(K)` for some complex Hilbert space `K`. +* `VonNeumannAlgebra.isFactor_boundedLinearOperators` — `B(H)` is a factor. +* `VonNeumannAlgebra.exists_isMinimalProjection_boundedLinearOperators` — `B(H)` has a minimal + projection (rank-one). +* `VonNeumannAlgebra.isTypeIFactor_boundedLinearOperators` — `B(H)` is a type I factor. +* `VonNeumannAlgebra.exists_starAlgEquiv_boundedLinearOperators` — `B(H) ≃⋆ₐ B(ℓ²(ι))` with the + implementing isometry `H ≃ₗᵢ ℓ²(ι)`, for `ι` the index set of a Hilbert basis of `H`. +* `VonNeumannAlgebra.isTypeIInfinite_boundedLinearOperators` — for infinite-dimensional `H`, `B(H)` + is a type I_∞ factor, packaged as the intrinsic predicate `IsTypeIInfinite 𝓑(H)`. +* `VonNeumannAlgebra.exists_starAlgEquiv_infiniteDimensional_boundedLinearOperators` — for + infinite-dimensional `H`, the same with `ι` infinite, hence `ℓ²(ι)` infinite-dimensional. + +## Notation + +In the prose above `B(H)` names the mathematical object — the algebra of all bounded operators — +while `𝓑(H)` is the Lean notation for it. The two are used deliberately, not interchangeably: +`𝓑(H)` resolves to `VonNeumannAlgebra.boundedLinearOperators H` (bundled von Neumann algebra) or +to `H →L[ℂ] H` (operator type) according to the expected type, an overload tabled in +`QuantumSystem.Algebra.VonNeumannAlgebra.Basic` and bridged by +`boundedLinearOperators.starAlgEquiv`. + +`⊗̄` is documentation shorthand for the von Neumann (spatial) tensor product of algebras; that +convention is stated in full in `QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`, where the +algebras it names (`HilbertTensor.vnTensorLeft` / `vnTensorRight`) are defined. +-/ + +@[expose] public section + +namespace VonNeumannAlgebra + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-- **Type I von Neumann algebra**: every nonzero central projection dominates a nonzero abelian +projection. The subprojection relation `p ≤ z` is written algebraically as `z * p = p`, as +everywhere in this development. -/ +def IsTypeI (N : VonNeumannAlgebra H) : Prop := + ∀ z : H →L[ℂ] H, IsCentralProjection N z → z ≠ 0 → + ∃ p : H →L[ℂ] H, IsAbelianProjection N p ∧ p ≠ 0 ∧ z * p = p + +/-- A **type I factor**: a factor possessing a minimal projection. This is the mathematically +conventional, intrinsic definition; the spatial decomposition `N ≅ B(H₁) ⊗̄ 1` is then a theorem, +not part of the definition. The equivalence with the general abelian-projection definition +`IsTypeI` is `isTypeIFactor_iff_isFactor_and_isTypeI`. -/ +def IsTypeIFactor (N : VonNeumannAlgebra H) : Prop := + IsFactor N ∧ ∃ e : H →L[ℂ] H, IsMinimalProjection N e + +/-- A factor with a minimal projection is type I: the only nonzero central projection of a factor +is `1` (`central_projection_eq`), and it dominates the minimal projection, which is abelian and +nonzero. -/ +lemma IsFactor.isTypeI_of_exists_isMinimalProjection {N : VonNeumannAlgebra H} + (hN : IsFactor N) (h : ∃ e : H →L[ℂ] H, IsMinimalProjection N e) : IsTypeI N := by + obtain ⟨e, he⟩ := h + haveI : Nontrivial H := he.nontrivial + intro z hz hz0 + rcases hN.central_projection_eq hz with h0 | h1 + · exact absurd h0 hz0 + · exact ⟨e, he.isAbelianProjection, he.2.2.1, by rw [h1, one_mul]⟩ + +/-- **In a factor, an abelian projection is order-minimal**: a projection `q ∈ N` with `q ≤ p` +(written `p * q = q`) is `0` or `p`. + +The proof avoids corner-commutant theory and polar decomposition: if `0 ≠ q ≠ p`, then +`r := p - q` is a nonzero projection under `p` orthogonal to `q`, and the central-support lemma +`IsFactor.exists_mul_ne` produces `a ∈ N` with `z := r a q ≠ 0`. Both `z` and `z⋆` are corner +elements of `p`, so they commute by abelianness; but `q z = 0` and `q z⋆ = z⋆` force +`z⋆ z = q (z z⋆) = (q z) z⋆ = 0`, and the C⋆-identity gives `z = 0` — a contradiction. -/ +theorem IsFactor.subprojection_eq_of_isAbelianProjection {N : VonNeumannAlgebra H} + (hN : IsFactor N) {p : H →L[ℂ] H} (hp : IsAbelianProjection N p) + {q : H →L[ℂ] H} (hq : IsStarProjection q) (hqN : q ∈ N) (hsub : p * q = q) : + q = 0 ∨ q = p := by + by_cases hq0 : q = 0 + · exact Or.inl hq0 + haveI : Nontrivial H := nontrivial_of_ne_zero hq0 + by_cases hqp : q = p + · exact Or.inr hqp + exfalso + have hqmul : q * p = q := isStarProjection_subproj_comm hp.1 hq hsub + set r : H →L[ℂ] H := p - q with hr + have hpr : p * r = r := by + rw [hr, mul_sub, hp.1.isIdempotentElem, hsub] + have hqr : q * r = 0 := by + rw [hr, mul_sub, hqmul, hq.isIdempotentElem, sub_self] + have hrsa : star r = r := by + rw [hr, star_sub, hp.1.isSelfAdjoint.star_eq, hq.isSelfAdjoint.star_eq] + have hrN : r ∈ N := sub_mem hp.2.1 hqN + have hr0 : r ≠ 0 := fun h0 => hqp (by rw [hr, sub_eq_zero] at h0; exact h0.symm) + obtain ⟨a, haN, hz0⟩ := hN.exists_mul_ne hqN hq0 hr0 + set z : H →L[ℂ] H := r * a * q with hz + have hzN : z ∈ N := mul_mem (mul_mem hrN haN) hqN + have hzcorner : p * z * p = z := by + rw [hz] + calc p * (r * a * q) * p + = (p * r) * a * (q * p) := by simp only [mul_assoc] + _ = r * a * q := by rw [hpr, hqmul] + have hzstar : star z = q * (star a * r) := by + rw [hz, star_mul, star_mul, hrsa, hq.isSelfAdjoint.star_eq] + have hcomm : z * star z = star z * z := by + have hzc : p * star z * p = star z := by + have h2 := congrArg star hzcorner + rw [star_mul, star_mul, hp.1.isSelfAdjoint.star_eq] at h2 + rw [mul_assoc] + exact h2 + have h1 := hp.2.2 z hzN (star z) (star_mem hzN) + rwa [hzcorner, hzc] at h1 + have hqz : q * z = 0 := by + rw [hz, ← mul_assoc, ← mul_assoc, hqr, zero_mul, zero_mul] + have hqstarz : q * star z = star z := by + rw [hzstar, ← mul_assoc, hq.isIdempotentElem] + have hzz0 : star z * z = 0 := by + have h6 : q * (z * star z) = q * (star z * z) := by rw [hcomm] + rw [← mul_assoc, hqz, zero_mul] at h6 + rw [← mul_assoc, hqstarz] at h6 + exact h6.symm + have hznorm : ‖z‖ = 0 := by + have hmul := CStarRing.norm_star_mul_self (x := z) + rw [hzz0, norm_zero] at hmul + exact mul_self_eq_zero.mp hmul.symm + exact hz0 (norm_eq_zero.mp hznorm) + +/-! ### The corner subalgebra + +The elements of `N` supported on a star projection `p` on both sides form a norm-closed +non-unital star subalgebra. Norm-closedness is what lets the non-unital continuous functional +calculus (`cfcₙ_mem`) operate inside the corner: the positive and negative parts of a +self-adjoint corner element stay in the corner. -/ + +/-- The **corner** of `N` at a star projection `p`: the elements of `N` supported on `p` on +both sides, as a non-unital star subalgebra of `B(H)`. -/ +def cornerNonUnitalStarSubalgebra (N : VonNeumannAlgebra H) {p : H →L[ℂ] H} + (hp : IsStarProjection p) : NonUnitalStarSubalgebra ℂ (H →L[ℂ] H) where + carrier := {y | y ∈ N ∧ p * y = y ∧ y * p = y} + add_mem' := by + rintro y z ⟨hyN, hpy, hyp⟩ ⟨hzN, hpz, hzp⟩ + exact ⟨add_mem hyN hzN, by rw [mul_add, hpy, hpz], by rw [add_mul, hyp, hzp]⟩ + zero_mem' := ⟨zero_mem _, mul_zero p, zero_mul p⟩ + mul_mem' := by + rintro y z ⟨hyN, hpy, hyp⟩ ⟨hzN, hpz, hzp⟩ + exact ⟨mul_mem hyN hzN, by rw [← mul_assoc, hpy], by rw [mul_assoc, hzp]⟩ + smul_mem' := by + rintro c y ⟨hyN, hpy, hyp⟩ + exact ⟨smul_mem c hyN, by rw [mul_smul_comm, hpy], by rw [smul_mul_assoc, hyp]⟩ + star_mem' := by + rintro y ⟨hyN, hpy, hyp⟩ + refine ⟨star_mem hyN, ?_, ?_⟩ + · have h := congrArg star hyp + rwa [star_mul, hp.isSelfAdjoint.star_eq] at h + · have h := congrArg star hpy + rwa [star_mul, hp.isSelfAdjoint.star_eq] at h + +/-- Membership in the corner subalgebra, unfolded. -/ +lemma mem_cornerNonUnitalStarSubalgebra_iff {N : VonNeumannAlgebra H} {p : H →L[ℂ] H} + {hp : IsStarProjection p} {y : H →L[ℂ] H} : + y ∈ cornerNonUnitalStarSubalgebra N hp ↔ y ∈ N ∧ p * y = y ∧ y * p = y := + Iff.rfl + +/-- The corner subalgebra is norm-closed: it is the intersection of the (double-centralizer, +hence closed) carrier of `N` with the closed support conditions `p * y = y` and `y * p = y`. -/ +lemma isClosed_cornerNonUnitalStarSubalgebra (N : VonNeumannAlgebra H) {p : H →L[ℂ] H} + (hp : IsStarProjection p) : + IsClosed ((cornerNonUnitalStarSubalgebra N hp : Set (H →L[ℂ] H))) := by + have hset : (cornerNonUnitalStarSubalgebra N hp : Set (H →L[ℂ] H)) + = (N : Set (H →L[ℂ] H)) ∩ ({y | p * y = y} ∩ {y | y * p = y}) := by + ext y + exact ⟨fun ⟨h1, h2, h3⟩ => ⟨h1, h2, h3⟩, fun ⟨h1, h2, h3⟩ => ⟨h1, h2, h3⟩⟩ + rw [hset] + refine N.isClosed_coe.inter (IsClosed.inter ?_ ?_) + · exact isClosed_eq (continuous_const.mul continuous_id) continuous_id + · exact isClosed_eq (continuous_id.mul continuous_const) continuous_id + +/-- The positive part of a corner element stays in the corner. -/ +lemma posPart_mem_cornerNonUnitalStarSubalgebra {N : VonNeumannAlgebra H} {p : H →L[ℂ] H} + (hp : IsStarProjection p) {d : H →L[ℂ] H} + (hd : d ∈ cornerNonUnitalStarSubalgebra N hp) : + d⁺ ∈ cornerNonUnitalStarSubalgebra N hp := by + haveI : IsClosed ((cornerNonUnitalStarSubalgebra N hp : Set (H →L[ℂ] H))) := + isClosed_cornerNonUnitalStarSubalgebra N hp + haveI : IsScalarTower ℝ ℂ (H →L[ℂ] H) := IsScalarTower.complexToReal + rw [CFC.posPart_def] + exact cfcₙ_mem _ hd + +/-- The negative part of a corner element stays in the corner. -/ +lemma negPart_mem_cornerNonUnitalStarSubalgebra {N : VonNeumannAlgebra H} {p : H →L[ℂ] H} + (hp : IsStarProjection p) {d : H →L[ℂ] H} + (hd : d ∈ cornerNonUnitalStarSubalgebra N hp) : + d⁻ ∈ cornerNonUnitalStarSubalgebra N hp := by + haveI : IsClosed ((cornerNonUnitalStarSubalgebra N hp : Set (H →L[ℂ] H))) := + isClosed_cornerNonUnitalStarSubalgebra N hp + haveI : IsScalarTower ℝ ℂ (H →L[ℂ] H) := IsScalarTower.complexToReal + rw [CFC.negPart_def] + exact cfcₙ_mem _ hd + +/-! ### Range projections + +For `x ∈ N`, the orthogonal projection onto the closure of `range x` lies in `N`; it is nonzero +when `x` is, it is dominated by any projection acting as the identity on the left of `x`, and the +range projections of two operators with `x₁ x₂ = 0`, `x₁` self-adjoint, are orthogonal. -/ + +/-- The orthogonal projection onto the closure of the range of `x ∈ N` lies in `N`, because that +subspace is invariant under the commutant. -/ +lemma starProjection_range_mem {N : VonNeumannAlgebra H} {x : H →L[ℂ] H} (hx : x ∈ N) : + (LinearMap.range (x : H →ₗ[ℂ] H)).topologicalClosure.starProjection ∈ N := by + set M : Submodule ℂ H := (LinearMap.range (x : H →ₗ[ℂ] H)).topologicalClosure with hM + have hpproj : IsStarProjection M.starProjection := isStarProjection_starProjection + rw [IsStarProjection.mem_iff hpproj N] + intro y hyN' + rw [Submodule.range_starProjection] + have hcl : IsClosed ((M.comap (y : H →ₗ[ℂ] H)) : Set H) := by + rw [Submodule.comap_coe] + exact ((LinearMap.range (x : H →ₗ[ℂ] H)).isClosed_topologicalClosure).preimage y.continuous + have hle : M ≤ M.comap (y : H →ₗ[ℂ] H) := by + refine Submodule.topologicalClosure_minimal _ ?_ hcl + rintro z ⟨v, rfl⟩ + simp only [Submodule.mem_comap, ContinuousLinearMap.coe_coe] + have hxy : x * y = y * x := mem_commutant_iff.mp hyN' x hx + rw [show y (x v) = (y * x) v from rfl, ← hxy] + exact Submodule.le_topologicalClosure _ ⟨y v, rfl⟩ + exact hle + +/-- The range projection of a nonzero operator is nonzero. -/ +lemma starProjection_range_ne_zero {x : H →L[ℂ] H} (hx0 : x ≠ 0) : + (LinearMap.range (x : H →ₗ[ℂ] H)).topologicalClosure.starProjection ≠ 0 := by + intro h0 + apply hx0 + ext v + have hmem : x v ∈ (LinearMap.range (x : H →ₗ[ℂ] H)).topologicalClosure := + Submodule.le_topologicalClosure _ ⟨v, rfl⟩ + have hfix := Submodule.starProjection_eq_self_iff.mpr hmem + rw [h0] at hfix + simpa using hfix.symm + +/-- If `p * x = x`, then the range projection of `x` is a subprojection of `p`. -/ +lemma starProjection_range_subproj {p x : H →L[ℂ] H} (hpx : p * x = x) : + p * (LinearMap.range (x : H →ₗ[ℂ] H)).topologicalClosure.starProjection + = (LinearMap.range (x : H →ₗ[ℂ] H)).topologicalClosure.starProjection := by + set M : Submodule ℂ H := (LinearMap.range (x : H →ₗ[ℂ] H)).topologicalClosure with hM + have hle : M ≤ LinearMap.ker ((p - 1 : H →L[ℂ] H) : H →ₗ[ℂ] H) := by + refine Submodule.topologicalClosure_minimal _ ?_ (p - 1).isClosed_ker + rintro z ⟨v, rfl⟩ + simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe, ContinuousLinearMap.sub_apply, + ContinuousLinearMap.one_apply] + rw [show p (x v) = (p * x) v from rfl, hpx] + exact sub_self _ + ext w + have hker := hle (M.starProjection_apply_mem w) + simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe, ContinuousLinearMap.sub_apply, + ContinuousLinearMap.one_apply, sub_eq_zero] at hker + exact hker + +/-- The range projections of `x₁` and `x₂` with `x₁` self-adjoint and `x₁ * x₂ = 0` are +orthogonal. -/ +lemma starProjection_range_mul_eq_zero {x₁ x₂ : H →L[ℂ] H} (hsa : star x₁ = x₁) + (h12 : x₁ * x₂ = 0) : + (LinearMap.range (x₁ : H →ₗ[ℂ] H)).topologicalClosure.starProjection + * (LinearMap.range (x₂ : H →ₗ[ℂ] H)).topologicalClosure.starProjection = 0 := by + set M₁ : Submodule ℂ H := (LinearMap.range (x₁ : H →ₗ[ℂ] H)).topologicalClosure with hM₁ + set M₂ : Submodule ℂ H := (LinearMap.range (x₂ : H →ₗ[ℂ] H)).topologicalClosure with hM₂ + have hadj : ContinuousLinearMap.adjoint x₁ = x₁ := by + rw [← ContinuousLinearMap.star_eq_adjoint, hsa] + have hortho : M₂ ⟂ M₁ := by + rw [Submodule.isOrtho_iff_le] + refine Submodule.topologicalClosure_minimal _ ?_ M₁.isClosed_orthogonal + rintro z ⟨u, rfl⟩ + rw [Submodule.mem_orthogonal] + intro m hm + have hker : M₁ ≤ LinearMap.ker ((innerSL ℂ (x₂ u)) : H →ₗ[ℂ] ℂ) := by + refine Submodule.topologicalClosure_minimal _ ?_ (innerSL ℂ _).isClosed_ker + rintro w ⟨v, rfl⟩ + simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe, innerSL_apply_apply] + rw [← ContinuousLinearMap.adjoint_inner_left x₁, hadj, + show x₁ (x₂ u) = (x₁ * x₂) u from rfl, h12] + simp + have h0 := hker hm + simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe, innerSL_apply_apply] at h0 + exact inner_eq_zero_symm.mp h0 + rw [ContinuousLinearMap.mul_def] + exact Submodule.starProjection_comp_starProjection_eq_zero_iff.mpr hortho.symm + +/-! ### Order-minimality implies corner triviality + +The dichotomy: a self-adjoint corner element of an order-minimal projection is comparable to `0` +in the Loewner order, because its positive and negative parts would otherwise produce two +orthogonal nonzero subprojections of `p`. A Dedekind-cut argument on +`{c : ℝ | 0 ≤ x - c • p}` then pins every self-adjoint corner element to `x = c₀ • p`. -/ + +/-- **Dichotomy.** If `p` has no proper nonzero subprojection in `N`, every self-adjoint corner +element `d` satisfies `0 ≤ d` or `d ≤ 0`. -/ +lemma nonneg_or_nonpos_of_forall_subprojection {N : VonNeumannAlgebra H} + {p : H →L[ℂ] H} (hp : IsStarProjection p) (hp0 : p ≠ 0) + (hmin : ∀ q, IsStarProjection q → q ∈ N → p * q = q → q = 0 ∨ q = p) + {d : H →L[ℂ] H} (hdsa : IsSelfAdjoint d) + (hd : d ∈ cornerNonUnitalStarSubalgebra N hp) : + 0 ≤ d ∨ d ≤ 0 := by + have hdplus := posPart_mem_cornerNonUnitalStarSubalgebra hp hd + have hdminus := negPart_mem_cornerNonUnitalStarSubalgebra hp hd + have hsub : d⁺ - d⁻ = d := CFC.posPart_sub_negPart d hdsa + by_cases hplus0 : d⁺ = 0 + · right + rw [← hsub, hplus0, zero_sub] + exact neg_nonpos.mpr (CFC.negPart_nonneg d) + by_cases hminus0 : d⁻ = 0 + · left + rw [← hsub, hminus0, sub_zero] + exact CFC.posPart_nonneg d + exfalso + have hPplus := hmin _ isStarProjection_starProjection (starProjection_range_mem hdplus.1) + (starProjection_range_subproj hdplus.2.1) + have hPminus := hmin _ isStarProjection_starProjection (starProjection_range_mem hdminus.1) + (starProjection_range_subproj hdminus.2.1) + rcases hPplus with h | hPp + · exact starProjection_range_ne_zero hplus0 h + rcases hPminus with h | hPm + · exact starProjection_range_ne_zero hminus0 h + have horth := starProjection_range_mul_eq_zero + (CFC.posPart_nonneg d).isSelfAdjoint.star_eq (CFC.posPart_mul_negPart d) + rw [hPp, hPm, hp.isIdempotentElem] at horth + exact hp0 horth + +/-- **Cut.** If `p` has no proper nonzero subprojection in `N`, every self-adjoint corner +element is a real multiple of `p`: the supremum `c₀` of `{c : ℝ | 0 ≤ x - c • p}` (nonempty, +bounded, closed) satisfies `x = c₀ • p`, since by the dichotomy `x - c • p ≤ 0` for every +`c > c₀`. -/ +lemma exists_real_smul_eq_of_forall_subprojection {N : VonNeumannAlgebra H} + {p : H →L[ℂ] H} (hp : IsStarProjection p) (hpN : p ∈ N) (hp0 : p ≠ 0) + (hmin : ∀ q, IsStarProjection q → q ∈ N → p * q = q → q = 0 ∨ q = p) + {x : H →L[ℂ] H} (hxsa : IsSelfAdjoint x) + (hx : x ∈ cornerNonUnitalStarSubalgebra N hp) : + ∃ c : ℝ, x = (c : ℂ) • p := by + obtain ⟨hxN, hpx, hxp⟩ := hx + -- positivity of a self-adjoint operator through diagonal inner products + have hpos_iff : ∀ T : H →L[ℂ] H, IsSelfAdjoint T → + (0 ≤ T ↔ ∀ v, 0 ≤ RCLike.re (inner ℂ (T v) v)) := by + intro T hT + rw [ContinuousLinearMap.nonneg_iff_isPositive, ContinuousLinearMap.isPositive_def'] + simp only [ContinuousLinearMap.reApplyInnerSelf_apply] + exact ⟨fun h => h.2, fun h => ⟨hT, h⟩⟩ + have hsa_c : ∀ c : ℝ, IsSelfAdjoint (x - (c : ℂ) • p) := by + intro c + rw [IsSelfAdjoint, star_sub, star_smul, hxsa.star_eq, hp.isSelfAdjoint.star_eq, + Complex.star_def, Complex.conj_ofReal] + have hcalc : ∀ (c : ℝ) (v : H), RCLike.re (inner ℂ ((x - (c : ℂ) • p) v) v) + = RCLike.re (inner ℂ (x v) v) - c * RCLike.re (inner ℂ (p v) v) := by + intro c v + rw [ContinuousLinearMap.sub_apply, ContinuousLinearMap.smul_apply, inner_sub_left, + inner_smul_left, Complex.conj_ofReal, map_sub, + show RCLike.re ((c : ℂ) * inner ℂ (p v) v) = c * RCLike.re (inner ℂ (p v) v) from + RCLike.re_ofReal_mul c _] + have hpvv : ∀ v, RCLike.re (inner ℂ (p v) v) = ‖p v‖ ^ 2 := by + intro v + have h1 : inner ℂ (p v) v = inner ℂ (p v) (p v) := by + calc inner ℂ (p v) v = inner ℂ (p (p v)) v := by + rw [show p (p v) = (p * p) v from rfl, hp.isIdempotentElem] + _ = inner ℂ (ContinuousLinearMap.adjoint p (p v)) v := by + rw [hp.isSelfAdjoint.adjoint_eq] + _ = inner ℂ (p v) (p v) := ContinuousLinearMap.adjoint_inner_left p v (p v) + rw [h1, inner_self_eq_norm_sq] + -- the cut set + set A : Set ℝ := {c : ℝ | 0 ≤ x - (c : ℂ) • p} with hA + have hmemA : ∀ c : ℝ, c ∈ A ↔ + ∀ v, c * ‖p v‖ ^ 2 ≤ RCLike.re (inner ℂ (x v) v) := by + intro c + rw [hA, Set.mem_setOf_eq, hpos_iff _ (hsa_c c)] + constructor + · intro h v + have := h v + rw [hcalc, hpvv, sub_nonneg] at this + exact this + · intro h v + rw [hcalc, hpvv, sub_nonneg] + exact h v + -- `x` is supported on the corner: `re ⟪x v, v⟫ = re ⟪x (p v), p v⟫` + have hxvv : ∀ v, RCLike.re (inner ℂ (x v) v) = RCLike.re (inner ℂ (x (p v)) (p v)) := by + intro v + have h1 : x v = x (p v) := by rw [← ContinuousLinearMap.mul_apply, hxp] + have h2 : x (p v) = p (x (p v)) := by + rw [show p (x (p v)) = (p * x) (p v) from rfl, hpx] + calc RCLike.re (inner ℂ (x v) v) = RCLike.re (inner ℂ (p (x (p v))) v) := by + conv_lhs => rw [h1, h2] + _ = RCLike.re (inner ℂ (x (p v)) (p v)) := by + have h3 : inner ℂ (p (x (p v))) v = inner ℂ (x (p v)) (p v) := by + conv_lhs => rw [show p (x (p v)) = ContinuousLinearMap.adjoint p (x (p v)) by + rw [hp.isSelfAdjoint.adjoint_eq]] + exact ContinuousLinearMap.adjoint_inner_left p v (x (p v)) + rw [h3] + -- nonempty: `-‖x‖ ∈ A` + have hAne : A.Nonempty := by + refine ⟨-‖x‖, (hmemA _).mpr fun v => ?_⟩ + rw [hxvv] + have hbound : |RCLike.re (inner ℂ (x (p v)) (p v))| ≤ ‖x‖ * ‖p v‖ ^ 2 := by + calc |RCLike.re (inner ℂ (x (p v)) (p v))| ≤ ‖inner ℂ (x (p v)) (p v)‖ := + RCLike.abs_re_le_norm _ + _ ≤ ‖x (p v)‖ * ‖p v‖ := norm_inner_le_norm _ _ + _ ≤ (‖x‖ * ‖p v‖) * ‖p v‖ := + mul_le_mul_of_nonneg_right (x.le_opNorm _) (norm_nonneg _) + _ = ‖x‖ * ‖p v‖ ^ 2 := by ring + calc -‖x‖ * ‖p v‖ ^ 2 = -(‖x‖ * ‖p v‖ ^ 2) := by ring + _ ≤ RCLike.re (inner ℂ (x (p v)) (p v)) := neg_le_of_abs_le hbound + -- bounded above + have hAbdd : BddAbove A := by + obtain ⟨w, hw⟩ : ∃ w, p w ≠ 0 := by + by_contra hcon + push Not at hcon + exact hp0 (ContinuousLinearMap.ext fun w => by simp [hcon w]) + refine ⟨RCLike.re (inner ℂ (x w) w) / ‖p w‖ ^ 2, fun c hc => ?_⟩ + have h1 := (hmemA c).mp hc w + have h2 : (0 : ℝ) < ‖p w‖ ^ 2 := by positivity + exact (le_div_iff₀ h2).mpr h1 + -- closed + have hAclosed : IsClosed A := by + have hAeq : A = ⋂ v : H, + {c : ℝ | c * ‖p v‖ ^ 2 ≤ RCLike.re (inner ℂ (x v) v)} := by + ext c + simp only [Set.mem_iInter, Set.mem_setOf_eq, ← hmemA c] + rw [hAeq] + exact isClosed_iInter fun v => + isClosed_le (continuous_id.mul continuous_const) continuous_const + set c₀ : ℝ := sSup A with hc₀ + have hc₀A : c₀ ∈ A := hAclosed.csSup_mem hAne hAbdd + refine ⟨c₀, ?_⟩ + -- for every `c > c₀`, the dichotomy forces `x - c • p ≤ 0` + have hpmem : p ∈ cornerNonUnitalStarSubalgebra N hp := + ⟨hpN, hp.isIdempotentElem, hp.isIdempotentElem⟩ + have hupper : ∀ ε : ℝ, 0 < ε → x - ((c₀ + ε : ℝ) : ℂ) • p ≤ 0 := by + intro ε hε + have hnotA : (c₀ + ε) ∉ A := fun hmem => + absurd (le_csSup hAbdd hmem) (by rw [← hc₀]; linarith) + have hdmem : x - ((c₀ + ε : ℝ) : ℂ) • p ∈ cornerNonUnitalStarSubalgebra N hp := + sub_mem ⟨hxN, hpx, hxp⟩ (SMulMemClass.smul_mem _ hpmem) + rcases nonneg_or_nonpos_of_forall_subprojection hp hp0 hmin (hsa_c _) hdmem with h | h + · exact absurd h hnotA + · exact h + -- `{c | x - c • p ≤ 0}` is pointwise-characterised, hence closed; it contains `(c₀, ∞)`, + -- hence its closure point `c₀` + have hnegpos_iff : ∀ c : ℝ, (x - (c : ℂ) • p ≤ 0) ↔ + ∀ v, RCLike.re (inner ℂ (x v) v) ≤ c * ‖p v‖ ^ 2 := by + intro c + have hsa' : IsSelfAdjoint ((c : ℂ) • p - x) := by + rw [IsSelfAdjoint, star_sub, star_smul, hxsa.star_eq, hp.isSelfAdjoint.star_eq, + Complex.star_def, Complex.conj_ofReal] + rw [ContinuousLinearMap.le_def, zero_sub, neg_sub, + ← ContinuousLinearMap.nonneg_iff_isPositive, hpos_iff _ hsa'] + have hstep : ∀ v, RCLike.re (inner ℂ (((c : ℂ) • p - x) v) v) + = c * ‖p v‖ ^ 2 - RCLike.re (inner ℂ (x v) v) := by + intro v + rw [ContinuousLinearMap.sub_apply, ContinuousLinearMap.smul_apply, inner_sub_left, + inner_smul_left, Complex.conj_ofReal, map_sub, + show RCLike.re ((c : ℂ) * inner ℂ (p v) v) = c * RCLike.re (inner ℂ (p v) v) from + RCLike.re_ofReal_mul c _, hpvv] + constructor + · intro h v + have := h v + rw [hstep, sub_nonneg] at this + exact this + · intro h v + rw [hstep, sub_nonneg] + exact h v + have hBclosed : IsClosed {c : ℝ | x - (c : ℂ) • p ≤ 0} := by + have hBeq : {c : ℝ | x - (c : ℂ) • p ≤ 0} + = ⋂ v : H, {c : ℝ | RCLike.re (inner ℂ (x v) v) ≤ c * ‖p v‖ ^ 2} := by + ext c + simp only [Set.mem_setOf_eq, Set.mem_iInter, hnegpos_iff c] + rw [hBeq] + exact isClosed_iInter fun v => + isClosed_le continuous_const (continuous_id.mul continuous_const) + have hIoi : Set.Ioi c₀ ⊆ {c : ℝ | x - (c : ℂ) • p ≤ 0} := by + intro c hc + have h := hupper (c - c₀) (sub_pos.mpr hc) + rw [show c₀ + (c - c₀) = c by ring] at h + exact h + have hc₀B : x - (c₀ : ℂ) • p ≤ 0 := by + have h1 : Set.Ici c₀ ⊆ {c : ℝ | x - (c : ℂ) • p ≤ 0} := by + rw [← closure_Ioi] + exact closure_minimal hIoi hBclosed + exact h1 Set.self_mem_Ici + have hge : (0 : H →L[ℂ] H) ≤ x - (c₀ : ℂ) • p := hc₀A + exact sub_eq_zero.mp (le_antisymm hc₀B hge) + +/-- **Order-minimality implies corner triviality**: a nonzero star projection `p ∈ N` with no +proper nonzero subprojection in `N` is a minimal projection, i.e. `p N p = ℂ p`. Self-adjoint +corner elements are real multiples of `p` by the cut lemma +(`exists_real_smul_eq_of_forall_subprojection`); a general corner element decomposes into real +and imaginary self-adjoint parts. -/ +theorem isMinimalProjection_of_forall_subprojection {N : VonNeumannAlgebra H} + {p : H →L[ℂ] H} (hp : IsStarProjection p) (hpN : p ∈ N) (hp0 : p ≠ 0) + (hmin : ∀ q, IsStarProjection q → q ∈ N → p * q = q → q = 0 ∨ q = p) : + IsMinimalProjection N p := by + refine ⟨hp, hpN, hp0, fun a haN => ?_⟩ + have hymem : p * a * p ∈ cornerNonUnitalStarSubalgebra N hp := by + refine ⟨mul_mem (mul_mem hpN haN) hpN, ?_, ?_⟩ + · calc p * (p * a * p) = (p * p) * a * p := by simp only [mul_assoc] + _ = p * a * p := by rw [hp.isIdempotentElem] + · rw [mul_assoc (p * a) p p, hp.isIdempotentElem] + set s : H →L[ℂ] H := star (p * a * p) with hs + have hsmem : s ∈ cornerNonUnitalStarSubalgebra N hp := star_mem hymem + have hy₁mem : (2⁻¹ : ℂ) • (p * a * p + s) ∈ cornerNonUnitalStarSubalgebra N hp := + SMulMemClass.smul_mem _ (add_mem hymem hsmem) + have hy₂mem : (-(Complex.I) * 2⁻¹ : ℂ) • (p * a * p - s) + ∈ cornerNonUnitalStarSubalgebra N hp := + SMulMemClass.smul_mem _ (sub_mem hymem hsmem) + have hy₁sa : IsSelfAdjoint ((2⁻¹ : ℂ) • (p * a * p + s)) := by + rw [IsSelfAdjoint, star_smul, star_add, hs, star_star, + show star (2⁻¹ : ℂ) = (2⁻¹ : ℂ) by simp, add_comm] + have hy₂sa : IsSelfAdjoint ((-(Complex.I) * 2⁻¹ : ℂ) • (p * a * p - s)) := by + rw [IsSelfAdjoint, star_smul, star_sub, hs, star_star, + show star (-(Complex.I) * 2⁻¹ : ℂ) = (Complex.I * 2⁻¹ : ℂ) by simp, + ← neg_sub (p * a * p) (star (p * a * p)), smul_neg, neg_mul, neg_smul] + obtain ⟨c₁, hc₁⟩ := exists_real_smul_eq_of_forall_subprojection hp hpN hp0 hmin hy₁sa hy₁mem + obtain ⟨c₂, hc₂⟩ := exists_real_smul_eq_of_forall_subprojection hp hpN hp0 hmin hy₂sa hy₂mem + refine ⟨(c₁ : ℂ) + Complex.I * (c₂ : ℂ), ?_⟩ + have hrec : (2⁻¹ : ℂ) • (p * a * p + s) + + Complex.I • ((-(Complex.I) * 2⁻¹ : ℂ) • (p * a * p - s)) = p * a * p := by + rw [smul_smul, show Complex.I * (-(Complex.I) * 2⁻¹) = (2⁻¹ : ℂ) by + rw [← mul_assoc, mul_neg, Complex.I_mul_I, neg_neg, one_mul], + smul_add, smul_sub] + calc (2⁻¹ : ℂ) • (p * a * p) + (2⁻¹ : ℂ) • s + + ((2⁻¹ : ℂ) • (p * a * p) - (2⁻¹ : ℂ) • s) + = (2⁻¹ : ℂ) • (p * a * p) + (2⁻¹ : ℂ) • (p * a * p) := by abel + _ = ((2⁻¹ : ℂ) + 2⁻¹) • (p * a * p) := (add_smul _ _ _).symm + _ = p * a * p := by norm_num + calc p * a * p + = (2⁻¹ : ℂ) • (p * a * p + s) + + Complex.I • ((-(Complex.I) * 2⁻¹ : ℂ) • (p * a * p - s)) := hrec.symm + _ = (c₁ : ℂ) • p + Complex.I • ((c₂ : ℂ) • p) := by rw [hc₁, hc₂] + _ = ((c₁ : ℂ) + Complex.I * (c₂ : ℂ)) • p := by rw [smul_smul, ← add_smul] + +/-- **In a factor, a nonzero abelian projection is minimal**: it is order-minimal +(`subprojection_eq_of_isAbelianProjection`), and order-minimality forces the trivial corner +(`isMinimalProjection_of_forall_subprojection`). -/ +theorem IsFactor.isMinimalProjection_of_isAbelianProjection + {N : VonNeumannAlgebra H} (hN : IsFactor N) {p : H →L[ℂ] H} + (hp : IsAbelianProjection N p) (hp0 : p ≠ 0) : IsMinimalProjection N p := + isMinimalProjection_of_forall_subprojection hp.1 hp.2.1 hp0 fun _ hq hqN hsub => + hN.subprojection_eq_of_isAbelianProjection hp hq hqN hsub + +/-- **The abelian-projection characterisation of type I coincides with the minimal-projection one +on factors**: a factor is type I iff it has a minimal projection. Nontriviality of `H` is +essential for the forward direction only: on a subsingleton `H` the type I condition is vacuously +satisfied while no nonzero projection exists, so `IsTypeI N` cannot produce one. The converse +carries no such hypothesis (`isTypeI_of_exists_isMinimalProjection`) — the minimal projection it +is handed is nonzero and so supplies the nontriviality itself. -/ +theorem IsFactor.isTypeI_iff_exists_isMinimalProjection [Nontrivial H] {N : VonNeumannAlgebra H} + (hN : IsFactor N) : IsTypeI N ↔ ∃ e : H →L[ℂ] H, IsMinimalProjection N e := by + constructor + · intro h + have hone : (1 : H →L[ℂ] H) ≠ 0 := by + obtain ⟨v, hv⟩ := exists_ne (0 : H) + intro h1 + apply hv + have h2 := congrArg (fun T : H →L[ℂ] H => T v) h1 + simpa using h2 + obtain ⟨q, hab, hq0, -⟩ := h 1 (isCentralProjection_one N) hone + exact ⟨q, hN.isMinimalProjection_of_isAbelianProjection hab hq0⟩ + · exact hN.isTypeI_of_exists_isMinimalProjection + +/-- The factor-specialised definition `IsTypeIFactor` agrees with the conjunction of the general +abelian-projection type I property and factor-ness. `[Nontrivial H]` is load-bearing here, not +decoration: on a subsingleton `H` *every* von Neumann algebra satisfies `IsFactor N ∧ IsTypeI N` +while *none* satisfies `IsTypeIFactor N`, since a minimal projection must be nonzero. -/ +theorem isTypeIFactor_iff_isFactor_and_isTypeI [Nontrivial H] {N : VonNeumannAlgebra H} : + IsTypeIFactor N ↔ IsFactor N ∧ IsTypeI N := by + constructor + · rintro ⟨hf, he⟩ + exact ⟨hf, hf.isTypeI_of_exists_isMinimalProjection he⟩ + · rintro ⟨hf, ht⟩ + exact ⟨hf, hf.isTypeI_iff_exists_isMinimalProjection.mp ht⟩ + +/-! ### Type I_∞ factors + +A **type I_∞ factor** is a type I factor of infinite multiplicity, recorded intrinsically as the +existence of an infinite orthogonal family of minimal projections. -/ + +/-- A **type I_∞ factor**: a type I factor carrying an infinite sequence of pairwise orthogonal +minimal projections. This is the intrinsic form of *infinite multiplicity*: through the structure +theorem `N ≃⋆ₐ B(K)` the minimal projections are the rank-one projections, and an infinite +orthogonal family of them exists exactly when `K` is infinite-dimensional — a type `I_n` factor +`B(ℂⁿ)` has at most `n` pairwise orthogonal nonzero projections. As with `IsTypeIFactor`, the +spatial identification with an infinite-dimensional `B(K)` is then a theorem, not part of the +definition. -/ +def IsTypeIInfinite (N : VonNeumannAlgebra H) : Prop := + IsTypeIFactor N ∧ + ∃ e : ℕ → (H →L[ℂ] H), + (∀ n, IsMinimalProjection N (e n)) ∧ + (∀ m n, m ≠ n → e m * e n = 0) + +/-- An orthogonal sequence of minimal projections is injective. -/ +lemma injective_of_isMinimalProjection_orthogonal {N : VonNeumannAlgebra H} + {e : ℕ → (H →L[ℂ] H)} (hmin : ∀ n, IsMinimalProjection N (e n)) + (horth : ∀ m n, m ≠ n → e m * e n = 0) : Function.Injective e := by + intro m n hmn + by_contra hne + have h0 := horth m n hne + rw [hmn, (hmin n).1.isIdempotentElem] at h0 + exact (hmin n).2.2.1 h0 + +/-- The witnessing minimal projections of a type I_∞ factor may be chosen injectively. -/ +lemma IsTypeIInfinite.exists_injective {N : VonNeumannAlgebra H} (hN : IsTypeIInfinite N) : + ∃ e : ℕ → (H →L[ℂ] H), Function.Injective e ∧ + (∀ n, IsMinimalProjection N (e n)) ∧ + (∀ m n, m ≠ n → e m * e n = 0) := by + obtain ⟨-, e, hmin, horth⟩ := hN + exact ⟨e, injective_of_isMinimalProjection_orthogonal hmin horth, hmin, horth⟩ + +/-- A type I_∞ factor is in particular a type I factor. -/ +lemma IsTypeIInfinite.isTypeIFactor {N : VonNeumannAlgebra H} (hN : IsTypeIInfinite N) : + IsTypeIFactor N := hN.1 + +/-! ### Abstract structure theorem + +A type I factor is `⋆`-isomorphic to `B(K)` for some Hilbert space `K`. The spatial content — the +implementing unitary and the multiplicity model `K = ℓ²(F)` — is +`IsFactor.exists_spatial_tensor_decomposition` in `Algebra.VonNeumannAlgebra.StructureTheorem`; +here it is packaged as an abstract `⋆`-isomorphism. -/ + +universe u + +/-- **Type I factor abstract structure theorem.** A type I factor `N` (a factor with a minimal +projection, acting on a nonzero Hilbert space) is `⋆`-isomorphic to the algebra `B(K)` of all +bounded operators on *some* complex Hilbert space `K`. This is the model-independent form of the +classification of type I factors: `B(K)` for `K = ℓ²(F)` is exactly the type `I_{|F|}` factor, and +`K = H` recovers the full algebra `B(H)` as the type `I` factor `𝓑(H)`. The spatial content — that the +isomorphism is implemented by a unitary and that `K` is the multiplicity space of the minimal +projection — is `IsFactor.exists_spatial_tensor_decomposition`; here it is packaged as an abstract +`⋆`-isomorphism, hiding the specific model `K = ℓ²(F)` behind an existential. -/ +theorem IsTypeIFactor.exists_starAlgEquiv {H : Type u} [NormedAddCommGroup H] + [InnerProductSpace ℂ H] [CompleteSpace H] {N : VonNeumannAlgebra H} + (hN : IsTypeIFactor N) : + ∃ (K : Type u) (_ : NormedAddCommGroup K) (_ : InnerProductSpace ℂ K) (_ : CompleteSpace K), + Nonempty (N ≃⋆ₐ[ℂ] (K →L[ℂ] K)) := by + obtain ⟨hFactor, e, he⟩ := hN + obtain ⟨F, U, hU, -⟩ := hFactor.exists_spatial_tensor_decomposition he + haveI : CompleteSpace (LinearMap.range (e : H →ₗ[ℂ] H)) := he.1.completeSpace_range + haveI : Nontrivial (LinearMap.range (e : H →ₗ[ℂ] H)) := by + rw [Submodule.nontrivial_iff_ne_bot, ne_eq, LinearMap.range_eq_bot] + exact fun h => he.2.2.1 (ContinuousLinearMap.coe_injective + (h.trans ContinuousLinearMap.coe_zero.symm)) + exact ⟨lp (fun _ : F => ℂ) 2, inferInstance, inferInstance, inferInstance, + ⟨(conjEquiv U N).trans ((equivOfEq hU).trans HilbertTensor.amplifyLeftStarAlgEquiv.symm)⟩⟩ + +/-! ### The fundamental example: `B(H)` is a type I factor -/ + +open InnerProductSpace + +/-- **`B(H)` is a factor.** The centre of the full algebra is trivial: an operator lying in the +commutant of `𝓑(H)` commutes with every operator, in particular with every rank-one operator, hence +is a scalar (`ContinuousLinearMap.exists_eq_smul_one_of_forall_rankOne_comm`). -/ +theorem isFactor_boundedLinearOperators : IsFactor 𝓑(H) := by + intro x _ hxComm + rw [VonNeumannAlgebra.mem_commutant_iff] at hxComm + refine ContinuousLinearMap.exists_eq_smul_one_of_forall_rankOne_comm (fun a b => ?_) + have hg := hxComm (rankOne ℂ a b) (mem_boundedLinearOperators _) + rw [ContinuousLinearMap.mul_def, ContinuousLinearMap.mul_def] at hg + exact hg.symm + +/-- **A rank-one projection is minimal in `B(H)`.** For a unit vector `u`, the rank-one orthogonal +projection `|u⟩⟨u|` is a minimal projection of `𝓑(H)`: it is a star projection, nonzero, and its corner +is trivial because `|u⟩⟨u| ∘ a ∘ |u⟩⟨u| = ⟪u, a u⟫ • |u⟩⟨u|`. -/ +lemma isMinimalProjection_rankOne_boundedLinearOperators {u : H} (hu : ‖u‖ = 1) : + IsMinimalProjection 𝓑(H) (rankOne ℂ u u) := by + have hu_ne : u ≠ 0 := by rw [← norm_pos_iff, hu]; norm_num + refine ⟨⟨isIdempotentElem_rankOne_self hu, ?_⟩, mem_boundedLinearOperators _, + rankOne_ne_zero hu_ne hu_ne, fun a _ => ?_⟩ + · rw [isSelfAdjoint_iff, ContinuousLinearMap.star_eq_adjoint, adjoint_rankOne] + · exact ⟨inner ℂ u (a u), by + rw [ContinuousLinearMap.mul_def, ContinuousLinearMap.mul_def, + ContinuousLinearMap.comp_assoc, comp_rankOne, rankOne_comp_rankOne]⟩ + +omit [CompleteSpace H] in +/-- A normalised nonzero vector of a nontrivial space, packaged as a unit vector. -/ +private theorem exists_unit_vector [Nontrivial H] : ∃ u : H, ‖u‖ = 1 := by + obtain ⟨v, hv⟩ := exists_ne (0 : H) + exact ⟨(‖v‖⁻¹ : ℂ) • v, by + rw [norm_smul, norm_inv, Complex.norm_real, norm_norm, + inv_mul_cancel₀ (norm_ne_zero_iff.mpr hv)]⟩ + +/-- **`B(H)` has a minimal projection** (a rank-one projection). Needs `H` nonzero. -/ +theorem exists_isMinimalProjection_boundedLinearOperators [Nontrivial H] : + ∃ e : H →L[ℂ] H, IsMinimalProjection 𝓑(H) e := + let ⟨u, hu⟩ := exists_unit_vector (H := H) + ⟨rankOne ℂ u u, isMinimalProjection_rankOne_boundedLinearOperators hu⟩ + +/-- **`B(H)` is a type I factor** (for nonzero `H`). -/ +theorem isTypeIFactor_boundedLinearOperators [Nontrivial H] : + IsTypeIFactor 𝓑(H) := + ⟨isFactor_boundedLinearOperators, exists_isMinimalProjection_boundedLinearOperators⟩ + +/-- **`B(H) ≃⋆ₐ B(ℓ²(ι))` with `H ≃ₗᵢ ℓ²(ι)`.** The full algebra is `⋆`-isomorphic to the bounded +operators on `ℓ²(ι)` for an index set `ι` — the index set of a Hilbert basis of `H` — and the +isomorphism is implemented by the corresponding isometry `H ≃ₗᵢ ℓ²(ι)`, which is returned +alongside it. + +The `ℓ²` model is stated rather than hidden behind an unconstrained `∃ K`. With `K` unconstrained +the statement would be discharged by `K := H` and `boundedLinearOperators.starAlgEquiv`, +carrying none of the classification content: the content is exactly that `K` may be taken of the +form `ℓ²(ι)`, which is what `IsTypeIFactor.exists_starAlgEquiv` hides behind its existential and +what the type `I_{|ι|}` reading of `B(H)` needs. Nonzeroness of `H` is not required: for `H = 0` +the Hilbert basis is empty and both sides are trivial. -/ +theorem exists_starAlgEquiv_boundedLinearOperators {H : Type u} [NormedAddCommGroup H] + [InnerProductSpace ℂ H] [CompleteSpace H] : + ∃ ι : Type u, Nonempty (H ≃ₗᵢ[ℂ] lp (fun _ : ι => ℂ) 2) ∧ + Nonempty ((𝓑(H) : VonNeumannAlgebra H) ≃⋆ₐ[ℂ] + (lp (fun _ : ι => ℂ) 2 →L[ℂ] lp (fun _ : ι => ℂ) 2)) := by + obtain ⟨w, b, -⟩ := exists_hilbertBasis ℂ H + exact ⟨w, ⟨b.repr⟩, ⟨boundedLinearOperators.starAlgEquiv.trans b.repr.conjStarAlgEquiv⟩⟩ + +/-- **`B(H) ≃⋆ₐ B(ℓ²(ι))` with `ι` infinite, when `H` is infinite-dimensional.** The full algebra +is `⋆`-isomorphic to the bounded operators on `ℓ²(ι)` for an *infinite* index set `ι`, with the +implementing isometry `H ≃ₗᵢ ℓ²(ι)` returned alongside; in particular `ℓ²(ι)` is itself +infinite-dimensional. Expressing type I_∞ this way is the standard reading: a type I_n factor is +`B(K)` with `dim K = n`, so I_∞ is exactly the infinite index set. + +As in `exists_starAlgEquiv_boundedLinearOperators`, the `ℓ²` model is part of the statement: an +unconstrained `∃ K` with `¬FiniteDimensional ℂ K` would be discharged by `K := H`. -/ +theorem exists_starAlgEquiv_infiniteDimensional_boundedLinearOperators {H : Type u} + [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + (hinf : ¬FiniteDimensional ℂ H) : + ∃ ι : Type u, Infinite ι ∧ ¬FiniteDimensional ℂ (lp (fun _ : ι => ℂ) 2) ∧ + Nonempty (H ≃ₗᵢ[ℂ] lp (fun _ : ι => ℂ) 2) ∧ + Nonempty ((𝓑(H) : VonNeumannAlgebra H) ≃⋆ₐ[ℂ] + (lp (fun _ : ι => ℂ) 2 →L[ℂ] lp (fun _ : ι => ℂ) 2)) := by + obtain ⟨w, b, -⟩ := exists_hilbertBasis ℂ H + have hwinf : Infinite w := by + rw [← not_finite_iff_infinite] + intro hfin + haveI : Finite w := hfin + haveI : Fintype w := Fintype.ofFinite w + exact hinf b.toOrthonormalBasis.toBasis.finiteDimensional_of_finite + have hnfd : ¬FiniteDimensional ℂ (lp (fun _ : w => ℂ) 2) := by + intro hK + haveI := hK + exact hinf b.repr.symm.toLinearEquiv.finiteDimensional + exact ⟨w, hwinf, hnfd, ⟨b.repr⟩, + ⟨boundedLinearOperators.starAlgEquiv.trans b.repr.conjStarAlgEquiv⟩⟩ + +/-- **`B(H)` is a type I_∞ factor when `H` is infinite-dimensional.** Packaged as the intrinsic +predicate `IsTypeIInfinite`: `𝓑(H) = B(H)` is a type I factor (`isTypeIFactor_boundedLinearOperators`) +carrying an infinite orthogonal family of minimal projections — the rank-one projections +`|uₙ⟩⟨uₙ|` onto a countable orthonormal sequence `(uₙ)` extracted from a Hilbert basis of the +infinite-dimensional `H`. The `⋆`-isomorphism to an infinite-dimensional `B(K)` is +`exists_starAlgEquiv_infiniteDimensional_boundedLinearOperators`. -/ +theorem isTypeIInfinite_boundedLinearOperators {H : Type u} [NormedAddCommGroup H] + [InnerProductSpace ℂ H] [CompleteSpace H] (hinf : ¬FiniteDimensional ℂ H) : + IsTypeIInfinite 𝓑(H) := by + obtain ⟨w, b, -⟩ := exists_hilbertBasis ℂ H + have hwinf : Infinite w := by + rw [← not_finite_iff_infinite] + intro hfin + haveI : Finite w := hfin + haveI : Fintype w := Fintype.ofFinite w + exact hinf b.toOrthonormalBasis.toBasis.finiteDimensional_of_finite + haveI := hwinf + let g : ℕ ↪ w := Infinite.natEmbedding w + set u : ℕ → H := fun n => b (g n) with hu_def + have hon : Orthonormal ℂ u := by + rw [hu_def]; exact b.orthonormal.comp g g.injective + have hnorm : ∀ n, ‖u n‖ = 1 := fun n => hon.1 n + have hmin : ∀ n, IsMinimalProjection 𝓑(H) (rankOne ℂ (u n) (u n)) := + fun n => isMinimalProjection_rankOne_boundedLinearOperators (hnorm n) + have horth : ∀ m n, m ≠ n → rankOne ℂ (u m) (u m) * rankOne ℂ (u n) (u n) = 0 := by + intro m n hmn + rw [ContinuousLinearMap.mul_def, rankOne_comp_rankOne, hon.2 hmn, zero_smul] + haveI : Nontrivial H := + nontrivial_of_ne (u 0) 0 (by rw [← norm_ne_zero_iff, hnorm 0]; norm_num) + exact ⟨isTypeIFactor_boundedLinearOperators, fun n => rankOne ℂ (u n) (u n), hmin, horth⟩ + +end VonNeumannAlgebra diff --git a/QuantumSystem/Analysis/Entropy/KroneckerProduct.lean b/QuantumSystem/Analysis/Entropy/KroneckerProduct.lean index 599a15f..5eed07a 100644 --- a/QuantumSystem/Analysis/Entropy/KroneckerProduct.lean +++ b/QuantumSystem/Analysis/Entropy/KroneckerProduct.lean @@ -1,7 +1,6 @@ module public import QuantumSystem.Analysis.CFC.Diagonal -public import QuantumSystem.Analysis.Matrix.PartialTrace public import QuantumSystem.State /-! @@ -17,7 +16,7 @@ This file is the hub for **Kronecker-product calculus on bipartite matrices**: * preservation of Hermitian / unitary structure under `⊗ₖ`, * Kronecker spectral decomposition, * the **log-tensor identity** - `matrixLog (A ⊗ₖ B) = matrixLog A ⊗ₖ 1 + 1 ⊗ₖ matrixLog B` for PosDef `A`, `B`, + `cfc Real.log (A ⊗ₖ B) = cfc Real.log A ⊗ₖ 1 + 1 ⊗ₖ cfc Real.log B` for PosDef `A`, `B`, * the **equivalence-indexed partial trace** `partialTrace` (for `e : X ≃ A × B`, retain `A` and sum over `B`), * and the **Heisenberg duality at product type** @@ -27,17 +26,13 @@ This file is the hub for **Kronecker-product calculus on bipartite matrices**: The retained subsystem is determined by the codomain of the chosen equivalence `e : X ≃ A × B`. For native product types, `partialTrace (Equiv.refl (n × m))` retains the `n` factor, while `partialTrace (Equiv.prodComm n m)` retains the -`m` factor. LocalNet-facing theorems should instead expose the split subset -`Λ ⊆ Λ_total` (and its complement) explicitly, and use these matrix-level lemmas -only after reindexing by `LocalNet.combineIdx`. +`m` factor. The proof of the log-tensor identity uses the spectral decomposition of `A ⊗ B` constructed explicitly from spectral decompositions of `A` and `B`, combined -with **spectral invariance** of `matrixFunction` (derived from -`Matrix.matrixFunction_eq_cfc` and `StarAlgHomClass.map_cfc` on the ⋆-algebra -automorphism given by conjugation by a unitary). Working via `matrixFunction` -(spectral, bare `Matrix n n ℂ`) instead of the `CStarMatrix` wrapper avoids the -instance diamond that blocked a previous `cfc`-only approach. +with **spectral invariance** of `cfc` (derived from `cfc_spectral_eq` and +`StarAlgHomClass.map_cfc` on the ⋆-algebra automorphism given by conjugation by +a unitary). ## Main definitions @@ -49,11 +44,8 @@ instance diamond that blocked a previous `cfc`-only approach. * `DensityMatrix.kronecker_toMatrix` — underlying-matrix unfolding. * `Matrix.IsHermitian.kronecker` — Kronecker of Hermitian matrices is Hermitian. * `Matrix.kronecker_eq_unitary_conj_diagonal` — Kronecker spectral decomposition. -* `Matrix.matrixLog_kronecker_posDef` — the log-tensor identity. +* `Matrix.cfc_log_kronecker_posDef` — the log-tensor identity. * `Matrix.partialTrace_apply` — entrywise unfolding of the equivalence-indexed partial trace. -* `Matrix.restrict_eq_partialTrace_combineIdx` / - `Matrix.restrict_compl_eq_partialTrace_combineIdx` — LocalNet restriction as an - equivalence-indexed partial trace. * `Matrix.trace_mul_kronecker_one_right` — `Tr(ρ · (X ⊗ 1)) = Tr((partialTrace (Equiv.refl (n × m)) ρ) · X)`. * `Matrix.trace_mul_kronecker_one_left` — @@ -72,7 +64,7 @@ variable {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] omit [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] in /-- The Kronecker product of two Hermitian matrices is Hermitian. -/ -theorem IsHermitian.kronecker {A : Matrix n n ℂ} {B : Matrix m m ℂ} +lemma IsHermitian.kronecker {A : Matrix n n ℂ} {B : Matrix m m ℂ} (hA : A.IsHermitian) (hB : B.IsHermitian) : (A ⊗ₖ B).IsHermitian := by unfold IsHermitian rw [conjTranspose_kronecker, hA.eq, hB.eq] @@ -103,7 +95,7 @@ exhibiting `U_A ⊗ U_B` as a valid unitary diagonaliser of `A ⊗ B`. -/ /-- **Kronecker spectral decomposition.** If `A = U_A * D_A * U_Aᴴ` and `B = U_B * D_B * U_Bᴴ` with `D_A = diagonal dA`, `D_B = diagonal dB`, then `A ⊗ₖ B = (U_A ⊗ U_B) * diagonal (fun (i,j) => dA i * dB j) * (U_A ⊗ U_B)ᴴ`. -/ -theorem kronecker_eq_unitary_conj_diagonal +lemma kronecker_eq_unitary_conj_diagonal {U_A : Matrix n n ℂ} {U_B : Matrix m m ℂ} {dA : n → ℂ} {dB : m → ℂ} {A : Matrix n n ℂ} {B : Matrix m m ℂ} @@ -121,30 +113,15 @@ theorem kronecker_eq_unitary_conj_diagonal section LogTensor -/-- `matrixLog` of a unitary conjugate of a positive real diagonal is the same -unitary conjugation of the diagonal logarithm. Auxiliary for the log-tensor identity. -/ -private lemma matrixLog_unitary_conj_diagonal - {k : Type*} [Fintype k] [DecidableEq k] - (W : unitary (Matrix k k ℂ)) (d : k → ℝ) (hd : ∀ i, 0 < d i) - (hM : ((W : Matrix k k ℂ) * diagonal (fun i => ((d i : ℝ) : ℂ)) * - (W : Matrix k k ℂ)ᴴ).IsHermitian) : - matrixLog ((W : Matrix k k ℂ) * diagonal (fun i => ((d i : ℝ) : ℂ)) * - (W : Matrix k k ℂ)ᴴ) hM = - (W : Matrix k k ℂ) * - diagonal (fun i => ((Real.log (d i) : ℝ) : ℂ)) * (W : Matrix k k ℂ)ᴴ := by - unfold matrixLog - rw [matrixFunction_eq_cfc] - exact cfc_log_unitary_conj_diagonal W d hd - /-- **Log-tensor identity.** For positive-definite matrices `A` and `B`, the matrix logarithm of the Kronecker product decomposes as the sum of tensor-embedded logs: -`matrixLog (A ⊗ₖ B) = matrixLog A ⊗ₖ 1 + 1 ⊗ₖ matrixLog B`. -/ -theorem matrixLog_kronecker_posDef +`cfc Real.log (A ⊗ₖ B) = cfc Real.log A ⊗ₖ 1 + 1 ⊗ₖ cfc Real.log B`. -/ +theorem cfc_log_kronecker_posDef {A : Matrix n n ℂ} (hA : A.PosDef) {B : Matrix m m ℂ} (hB : B.PosDef) : - matrixLog (A ⊗ₖ B) (IsHermitian.kronecker hA.1 hB.1) = - matrixLog A hA.1 ⊗ₖ (1 : Matrix m m ℂ) + - (1 : Matrix n n ℂ) ⊗ₖ matrixLog B hB.1 := by + cfc Real.log (A ⊗ₖ B) = + cfc Real.log A ⊗ₖ (1 : Matrix m m ℂ) + + (1 : Matrix n n ℂ) ⊗ₖ cfc Real.log B := by -- Spectral data set U_A := (hA.1.eigenvectorUnitary : Matrix n n ℂ) with hU_A_def set U_B := (hB.1.eigenvectorUnitary : Matrix m m ℂ) with hU_B_def @@ -191,29 +168,19 @@ theorem matrixLog_kronecker_posDef rw [h] congr 1; congr 1 funext ij; push_cast; ring - -- Hermitianness of Kronecker - have hAB_herm : (A ⊗ₖ B).IsHermitian := IsHermitian.kronecker hA.1 hB.1 -- Positivity of the diagonal product have h_dA_dB_pos : ∀ ij : n × m, 0 < (dA ij.1 * dB ij.2 : ℝ) := fun ij => mul_pos (hdA_pos ij.1) (hdB_pos ij.2) - -- matrixLog of A ⊗ B via aux lemma - have hAB_herm' : ((W : Matrix (n × m) (n × m) ℂ) * - diagonal (fun ij : n × m => (((dA ij.1 * dB ij.2 : ℝ) : ℂ))) * - (W : Matrix (n × m) (n × m) ℂ)ᴴ).IsHermitian := by - rw [← hAB_decomp]; exact hAB_herm - have h_matrixLog_AB : - matrixLog (A ⊗ₖ B) hAB_herm = + -- cfc Real.log of A ⊗ B via aux lemma + have h_cfcLog_AB : + cfc Real.log (A ⊗ₖ B) = (W : Matrix (n × m) (n × m) ℂ) * diagonal (fun ij : n × m => ((Real.log (dA ij.1 * dB ij.2) : ℝ) : ℂ)) * (W : Matrix (n × m) (n × m) ℂ)ᴴ := by - rw [show matrixLog (A ⊗ₖ B) hAB_herm = - matrixLog ((W : Matrix (n × m) (n × m) ℂ) * - diagonal (fun ij : n × m => (((dA ij.1 * dB ij.2 : ℝ) : ℂ))) * - (W : Matrix (n × m) (n × m) ℂ)ᴴ) hAB_herm' from ?_] - · exact matrixLog_unitary_conj_diagonal W - (fun ij : n × m => (dA ij.1 * dB ij.2 : ℝ)) h_dA_dB_pos hAB_herm' - · congr 1 - rw [h_matrixLog_AB] + rw [hAB_decomp] + exact cfc_log_unitary_conj_diagonal W + (fun ij : n × m => (dA ij.1 * dB ij.2 : ℝ)) h_dA_dB_pos + rw [h_cfcLog_AB] -- Split log(dA*dB) = log dA + log dB have h_log_split : diagonal (fun ij : n × m => ((Real.log (dA ij.1 * dB ij.2) : ℝ) : ℂ)) = @@ -249,8 +216,8 @@ theorem matrixLog_kronecker_posDef ← mul_kronecker_mul, ← mul_kronecker_mul] -- Clean up: U_A * 1 * U_Aᴴ = 1, U_B * 1 * U_Bᴴ = 1 rw [Matrix.mul_one U_A, hUA_self', Matrix.mul_one U_B, hUB_self'] - -- Now unfold matrixLog of A and B via their spectral decomposition - rw [matrixLog_spectral_eq hA.1, matrixLog_spectral_eq hB.1] + -- Now unfold cfc Real.log of A and B via their spectral decomposition + rw [cfc_log_spectral_eq hA.1, cfc_log_spectral_eq hB.1] end LogTensor @@ -286,68 +253,6 @@ omit [Fintype m] [DecidableEq n] [DecidableEq m] in partialTrace (A := m) (B := n) (Equiv.prodComm n m) ρ b b' = ∑ a : n, ρ (a, b) (a, b') := rfl -/-! ### LocalNet bridge - -These lemmas identify `Matrix.restrict` on a `LocalNet` with the equivalence-indexed -partial trace of the reindexed matrix induced by `LocalNet.combineIdx`. -/ - -section LocalNetBridge - -variable {L : LocalNet} - -/-- Combining via `h : Λ ⊆ Λ_total` agrees with combining via the complementary split, -after transporting the remaining factor along `Λ_total \ (Λ_total \ Λ) = Λ`. -/ -private lemma combineIdx_swap_apply - {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (x : L.regionIdx Λ) (y : L.regionIdx (Λ_total \ Λ)) : - L.combineIdx h (x, y) = - L.combineIdx Finset.sdiff_subset - (y, L.regionIdxCongr (sdiff_sdiff_eq_self h).symm x) := by - have h_eq : Λ_total \ (Λ_total \ Λ) = Λ := sdiff_sdiff_eq_self h - funext ⟨s, hs⟩ - by_cases hsΛ : s ∈ Λ - · have hns_compl : s ∉ Λ_total \ Λ := fun h_in => (Finset.mem_sdiff.mp h_in).2 hsΛ - have hs_recast : s ∈ Λ_total \ (Λ_total \ Λ) := by - rw [h_eq] - exact hsΛ - rw [LocalNet.combineIdx_apply_mem h _ _ ⟨s, hs⟩ hsΛ, - LocalNet.combineIdx_apply_not_mem Finset.sdiff_subset _ _ ⟨s, hs⟩ hns_compl, - LocalNet.regionIdxCongr_apply (L := L) h_eq.symm x hsΛ hs_recast] - · have hs_compl : s ∈ Λ_total \ Λ := Finset.mem_sdiff.mpr ⟨hs, hsΛ⟩ - rw [LocalNet.combineIdx_apply_not_mem h _ _ ⟨s, hs⟩ hsΛ, - LocalNet.combineIdx_apply_mem Finset.sdiff_subset _ _ ⟨s, hs⟩ hs_compl] - -/-- Restriction to `Λ` equals the partial trace of the reindexed matrix induced by -`combineIdx h`, retaining the `Λ` factor. -/ -theorem restrict_eq_partialTrace_combineIdx - {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (ρ : L.localAlgebra Λ_total) (x x' : L.regionIdx Λ) : - Matrix.restrict h ρ x x' = - Matrix.partialTrace - (A := L.regionIdx Λ) (B := L.regionIdx (Λ_total \ Λ)) - (Equiv.refl (L.regionIdx Λ × L.regionIdx (Λ_total \ Λ))) - (ρ.submatrix (L.combineIdx h) (L.combineIdx h)) x x' := by - rw [Matrix.partialTrace_refl_apply, Matrix.restrict_apply] - simp [Matrix.submatrix_apply] - -/-- Restriction to the complement of `Λ` equals the partial trace of the reindexed matrix -induced by `combineIdx h`, retaining the complementary factor. -/ -theorem restrict_compl_eq_partialTrace_combineIdx - {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (ρ : L.localAlgebra Λ_total) (y y' : L.regionIdx (Λ_total \ Λ)) : - Matrix.restrict Finset.sdiff_subset ρ y y' = - Matrix.partialTrace - (A := L.regionIdx (Λ_total \ Λ)) (B := L.regionIdx Λ) - (Equiv.prodComm (L.regionIdx Λ) (L.regionIdx (Λ_total \ Λ))) - (ρ.submatrix (L.combineIdx h) (L.combineIdx h)) y y' := by - rw [Matrix.partialTrace_prodComm_apply, Matrix.restrict_apply] - rw [← (L.regionIdxCongr (sdiff_sdiff_eq_self h).symm).sum_comp - (fun z => ρ (L.combineIdx Finset.sdiff_subset (y, z)) - (L.combineIdx Finset.sdiff_subset (y', z)))] - refine Finset.sum_congr rfl fun x _ => ?_ - rw [Matrix.submatrix_apply, combineIdx_swap_apply h x y, combineIdx_swap_apply h x y'] - -end LocalNetBridge /-! ### Heisenberg duality at product type @@ -357,7 +262,7 @@ end LocalNetBridge omit [DecidableEq n] in /-- **Right-factor Heisenberg dual**: tracing `ρ` against the embedded observable `X ⊗ 1` reduces to the trace against the partial trace that retains the first factor. -/ -theorem trace_mul_kronecker_one_right +lemma trace_mul_kronecker_one_right (ρ : Matrix (n × m) (n × m) ℂ) (X : Matrix n n ℂ) : Tr (ρ * (X ⊗ₖ (1 : Matrix m m ℂ))) = Tr (partialTrace (A := n) (B := m) (Equiv.refl (n × m)) ρ * X) := by @@ -385,7 +290,7 @@ theorem trace_mul_kronecker_one_right omit [DecidableEq m] in /-- **Left-factor Heisenberg dual**: `Tr(ρ · (1 ⊗ Y))` reduces to the trace against the partial trace that retains the second factor. -/ -theorem trace_mul_kronecker_one_left +lemma trace_mul_kronecker_one_left (ρ : Matrix (n × m) (n × m) ℂ) (Y : Matrix m m ℂ) : Tr (ρ * ((1 : Matrix n n ℂ) ⊗ₖ Y)) = Tr (partialTrace (A := m) (B := n) (Equiv.prodComm n m) ρ * Y) := by diff --git a/QuantumSystem/Analysis/Entropy/MutualInformation.lean b/QuantumSystem/Analysis/Entropy/MutualInformation.lean new file mode 100644 index 0000000..9cd6694 --- /dev/null +++ b/QuantumSystem/Analysis/Entropy/MutualInformation.lean @@ -0,0 +1,101 @@ +module + +public import QuantumSystem.Analysis.Entropy.KroneckerProduct +public import QuantumSystem.Analysis.Entropy.RelativeEntropy +public import QuantumSystem.Analysis.Entropy.VonNeumannEntropy + +/-! +# Mutual-information identity + +The relative-entropy form of quantum mutual information for a bipartite density matrix on a plain +product index type `n × m`: + + `D(ρ_AB ‖ ρ_A ⊗ ρ_B) = -S(ρ_AB) + S(ρ_A) + S(ρ_B)`. + +It is representation-free; the analytic core reused by the direct proof +(`Analysis/Entropy/StrongSubadditivity.lean`) and, via transport, by the planned split-net proof +(`Analysis/Entropy/SplitSSA.lean`, not yet formalised — the split property it rests on is +`LocalNet.SplitProperty`). +-/ + +@[expose] public section + +namespace Matrix + +open scoped Kronecker MatrixOrder ComplexOrder QuantumInfo + +variable {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] + +/-! ### Relative-entropy identity -/ + +/-- **Mutual-information identity**: for a bipartite density +matrix `ρ_AB : DensityMatrix (n × m)` whose canonical partial traces coincide with PosDef +factor states `ρ_A` and `ρ_B`, the relative entropy w.r.t. the product `ρ_A ⊗ ρ_B` +equals `-S(ρ_AB) + S(ρ_A) + S(ρ_B)`. -/ +theorem relativeEntropy_kronecker_marginals + (ρ_AB : DensityMatrix (n × m)) + (ρ_A : DensityMatrix n) (hρ_A : ρ_A.toMatrix.PosDef) + (ρ_B : DensityMatrix m) (hρ_B : ρ_B.toMatrix.PosDef) + (h_A_partialTrace : tr₂(ρ_AB.toMatrix) = ρ_A.toMatrix) + (h_B_partialTrace : tr₁(ρ_AB.toMatrix) = ρ_B.toMatrix) : + D(ρ_AB ∥ ρ_A ⊗ ρ_B) = -S(ρ_AB) + S(ρ_A) + S(ρ_B) := by + classical + have hρ_A_kron_pos : (ρ_A ⊗ ρ_B).toMatrix.PosDef := by + rw [DensityMatrix.kronecker_toMatrix]; exact hρ_A.kronecker hρ_B + -- supp(ρ) ⊆ supp(ρ_A ⊗ ρ_B) holds for PosDef σ. + have h_supp : suppSubset ρ_AB.toMatrix (ρ_A ⊗ ρ_B).toMatrix := by + intro v hv + have hinj : Function.Injective (ρ_A ⊗ ρ_B).toMatrix.mulVec := + Matrix.mulVec_injective_iff_isUnit.mpr hρ_A_kron_pos.isUnit + have h0 : (ρ_A ⊗ ρ_B).toMatrix.mulVec 0 = 0 := by simp + have hv_zero : v = 0 := hinj (hv.trans h0.symm) + rw [hv_zero]; simp + unfold relativeEntropy + simp only [h_supp, if_true] + -- log of ρ_A ⊗ ρ_B decomposes via cfc_log_kronecker_posDef. + have h_log_kron : cfc Real.log (ρ_A ⊗ ρ_B).toMatrix = + cfc Real.log ρ_A.toMatrix ⊗ₖ (1 : Matrix m m ℂ) + + (1 : Matrix n n ℂ) ⊗ₖ cfc Real.log ρ_B.toMatrix := + cfc_log_kronecker_posDef hρ_A hρ_B + -- The trace identity after substitution. + have h_trace_log_kron : + Tr (ρ_AB.toMatrix * cfc Real.log (ρ_A ⊗ ρ_B).toMatrix) = + Tr (ρ_A.toMatrix * cfc Real.log ρ_A.toMatrix) + + Tr (ρ_B.toMatrix * cfc Real.log ρ_B.toMatrix) := by + rw [h_log_kron, Matrix.mul_add, Matrix.trace_add, trace_mul_kronecker_one_right, + trace_mul_kronecker_one_left, h_A_partialTrace, h_B_partialTrace] + -- Split (log ρ - log(ρ_A⊗ρ_B)) and reduce trace. + have h_split : Tr (ρ_AB.toMatrix * (cfc Real.log ρ_AB.toMatrix - + cfc Real.log (ρ_A ⊗ ρ_B).toMatrix)) = + Tr (ρ_AB.toMatrix * cfc Real.log ρ_AB.toMatrix) - + Tr (ρ_AB.toMatrix * cfc Real.log (ρ_A ⊗ ρ_B).toMatrix) := by + rw [Matrix.mul_sub, Matrix.trace_sub] + -- Translate to the goal in EReal. + change (↑(Tr (ρ_AB.toMatrix * (cfc Real.log ρ_AB.toMatrix - + cfc Real.log (ρ_A ⊗ ρ_B).toMatrix))).re : EReal) = + -S(ρ_AB) + S(ρ_A) + S(ρ_B) + rw [h_split, Complex.sub_re, h_trace_log_kron, Complex.add_re] + -- Now: ↑((Tr(ρ · log ρ)).re - ((Tr(ρ_A · log ρ_A)).re + (Tr(ρ_B · log ρ_B)).re)) + -- = -S(ρ) + S(ρ_A) + S(ρ_B) + -- Express the LHS Real value: + set α : ℝ := (Tr (ρ_AB.toMatrix * cfc Real.log ρ_AB.toMatrix)).re with hα + set β : ℝ := (Tr (ρ_A.toMatrix * cfc Real.log ρ_A.toMatrix)).re with hβ + set γ : ℝ := (Tr (ρ_B.toMatrix * cfc Real.log ρ_B.toMatrix)).re with hγ + -- And the S values: + change (↑(α - (β + γ)) : EReal) = -S(ρ_AB) + S(ρ_A) + S(ρ_B) + have hSρ : S(ρ_AB) = -α := by + change -(Tr (ρ_AB.toMatrix * DensityMatrix.log ρ_AB)).re = -α + rfl + have hSρ_A : S(ρ_A) = -β := by + change -(Tr (ρ_A.toMatrix * DensityMatrix.log ρ_A)).re = -β + rfl + have hSρ_B : S(ρ_B) = -γ := by + change -(Tr (ρ_B.toMatrix * DensityMatrix.log ρ_B)).re = -γ + rfl + rw [hSρ, hSρ_A, hSρ_B] + -- Goal in EReal: ↑(α - (β + γ)) = -↑(-α) + ↑(-β) + ↑(-γ) + -- Equivalent Real identity: + have h_real : α - (β + γ) = -(-α) + (-β) + (-γ) := by ring + exact_mod_cast h_real + +end Matrix diff --git a/QuantumSystem/Analysis/Entropy/Regularize.lean b/QuantumSystem/Analysis/Entropy/Regularize.lean deleted file mode 100644 index b9f4760..0000000 --- a/QuantumSystem/Analysis/Entropy/Regularize.lean +++ /dev/null @@ -1,89 +0,0 @@ -module - -public import QuantumSystem.Analysis.Entropy.VonNeumannEntropy -public import QuantumSystem.Analysis.Matrix.PartialTrace - -/-! -# Regularization on a local net: compatibility with restriction - -The structural definitions (`maximallyMixed`, `regularize`, `regularize_eq_cfc`, -`regularize_mapEquiv`) and the entropy identities for regularized states -(`vonNeumannEntropy_regularize_eq_negMulLog_sum`, `tendsto_negMulLog_regularize_sum_zero`, -`vonNeumannEntropy_mapEquiv`) live in `State.lean` and `VonNeumannEntropy.lean`. - -This file specialises that infrastructure to a `LocalNet`. It proves compatibility of -regularization with restriction (partial trace), which is the key fact behind extending -PosDef-only theorems to PosSemidef. The cardinality factorisation for region indices now -lives next to `LocalNet.combineIdx` in `LocalNet.lean`. --/ - -@[expose] public section - -open scoped ComplexOrder - -/-! ### Regularization commutes with restriction - -The marginal of a regularized state equals the regularized marginal: -`restrict h (regularize ρ ε) = regularize (restrict h ρ) ε`. - -This is the key fact behind the "regularization respects partial trace" property -used in extending PosDef-only theorems to PosSemidef. -/ - -namespace Matrix - -/-- `Matrix.restrict h` of the maximally-mixed state at `Λ_total` equals the - maximally-mixed state at `Λ`, after dimension cancellation. -/ -theorem restrict_maximallyMixed {L : LocalNet} {Λ Λ_total : Finset L.sites} - (h : Λ ⊆ Λ_total) [Nonempty (L.regionIdx Λ)] [Nonempty (L.regionIdx Λ_total)] : - Matrix.restrict h (DensityMatrix.maximallyMixed (n := L.regionIdx Λ_total)).toMatrix = - (DensityMatrix.maximallyMixed (n := L.regionIdx Λ)).toMatrix := by - have hcomp_nonempty : Nonempty (L.regionIdx (Λ_total \ Λ)) := by - obtain ⟨x⟩ := ‹Nonempty (L.regionIdx Λ_total)› - exact ⟨((L.combineIdx h).symm x).2⟩ - rw [DensityMatrix.maximallyMixed_toMatrix, - DensityMatrix.maximallyMixed_toMatrix] - rw [Matrix.restrict_smul, Matrix.restrict_one] - rw [smul_smul] - congr 1 - have hdΛ : (Fintype.card (L.regionIdx Λ) : ℂ) ≠ 0 := by - exact_mod_cast (Fintype.card_pos (α := L.regionIdx Λ)).ne' - have hdComp : (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ) ≠ 0 := by - exact_mod_cast (Fintype.card_pos (α := L.regionIdx (Λ_total \ Λ))).ne' - rw [LocalNet.card_regionIdx_total (L := L) h] - have h_inv_mul : - ((((Fintype.card (L.regionIdx Λ) : ℂ) * - (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ)))⁻¹) * - (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ) = - (Fintype.card (L.regionIdx Λ) : ℂ)⁻¹ := by - field_simp [hdΛ, hdComp] - simpa [Nat.cast_mul] using h_inv_mul - -end Matrix - -namespace DensityMatrix - -/-- The regularization commutes with `restrict` (Matrix-level): - `restrict h (regularize ρ ε).toMatrix = (regularize (restrict h ρ) ε).toMatrix`. -/ -theorem regularize_restrict_toMatrix {L : LocalNet} {Λ Λ_total : Finset L.sites} - (h : Λ ⊆ Λ_total) [Nonempty (L.regionIdx Λ)] [Nonempty (L.regionIdx Λ_total)] - (ρ : L.densityMatrix Λ_total) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : - Matrix.restrict h (regularize ρ hε hε').toMatrix = - (regularize (restrict h ρ) hε hε').toMatrix := by - rw [regularize_toMatrix, regularize_toMatrix] - rw [DensityMatrix.restrict_toMatrix] - rw [(Matrix.restrict h).map_add] - rw [(Matrix.restrict h).map_smul, (Matrix.restrict h).map_smul] - rw [Matrix.restrict_maximallyMixed h] - -/-- The regularization commutes with `restrict` (DensityMatrix-level): - `(regularize ρ ε).restrict h = regularize (restrict h ρ) ε`. -/ -theorem regularize_restrict {L : LocalNet} {Λ Λ_total : Finset L.sites} - (h : Λ ⊆ Λ_total) [Nonempty (L.regionIdx Λ)] [Nonempty (L.regionIdx Λ_total)] - (ρ : L.densityMatrix Λ_total) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : - restrict h (regularize ρ hε hε') = - regularize (restrict h ρ) hε hε' := by - apply ext - rw [DensityMatrix.restrict_toMatrix] - exact regularize_restrict_toMatrix h ρ hε hε' - -end DensityMatrix diff --git a/QuantumSystem/Analysis/Entropy/RelativeEntropy.lean b/QuantumSystem/Analysis/Entropy/RelativeEntropy.lean index 87afa6c..796e176 100644 --- a/QuantumSystem/Analysis/Entropy/RelativeEntropy.lean +++ b/QuantumSystem/Analysis/Entropy/RelativeEntropy.lean @@ -13,6 +13,9 @@ This file collects fundamental entropy inequalities for quantum channels. ## Main Results +* `relativeEntropy_nonneg`: Klein's inequality — relative entropy is non-negative, + `0 ≤ D(ρ ‖ σ)`. +* `relativeEntropy_eq_zero_iff`: faithfulness — `D(ρ ‖ σ) = 0 ↔ ρ = σ`. * `relativeEntropy_channel_le`: Monotonicity of relative entropy — quantum channels do not increase relative entropy: S(Φ(ρ) ‖ Φ(σ)) ≤ S(ρ ‖ σ). * `relativeEntropy_channel_eq_iff_recoverable`: Equality in monotonicity holds when a Petz @@ -230,8 +233,8 @@ private lemma trace_ρlogρ_eq (ρ : DensityMatrix n) : have hρ_spec := spectral_expand ρ.toMatrix ρ.isHermitian -- log(ρ) = U * diag(log ev) * Uᴴ have hlogρ_spec : log ρ = U * diagonal (fun i => (Real.log (ev_ρ i) : ℂ)) * Uᴴ := by - unfold DensityMatrix.log matrixLog matrixFunction - rfl + unfold DensityMatrix.log + exact cfc_log_spectral_eq ρ.isHermitian -- ρ * log(ρ) = U * diag(ev) * Uᴴ * U * diag(log ev) * Uᴴ = U * diag(ev * log ev) * Uᴴ -- First rewrite log, then ρ have h1 : (ρ.toMatrix * log ρ).trace.re = @@ -276,12 +279,10 @@ private lemma trace_ρlogσ_eq (ρ σ : DensityMatrix n) : set W := eigW ρ σ set ev_ρ := ρ.isHermitian.eigenvalues set ev_σ := σ.isHermitian.eigenvalues - have hρ : ρ.toMatrix = U * diagonal (fun i => (ev_ρ i : ℂ)) * Uᴴ := by - have h := (matrixFunction_id ρ.isHermitian).symm - unfold matrixFunction at h - simpa [Function.comp] using h + have hρ : ρ.toMatrix = U * diagonal (fun i => (ev_ρ i : ℂ)) * Uᴴ := + spectral_expand ρ.toMatrix ρ.isHermitian have hlogσ : log σ = V * diagonal (fun i => (Real.log (ev_σ i) : ℂ)) * Vᴴ := by - unfold DensityMatrix.log matrixLog matrixFunction; rfl + unfold DensityMatrix.log; exact cfc_log_spectral_eq σ.isHermitian have hUHV : Uᴴ * V = Wᴴ := by calc Uᴴ * V = Uᴴ * (Vᴴ)ᴴ := by rw [conjTranspose_conjTranspose] _ = (Vᴴ * U)ᴴ := by rw [conjTranspose_mul] @@ -675,9 +676,11 @@ private lemma trace_rpow_mul_double_sum (ρ σ : DensityMatrix n) (s : ℝ) : have hpsdρ := ρ.posSemidef have hpsdσ := σ.posSemidef have hρs : ρ.toMatrix ^ s = U * diagonal (fun i => ((ev_ρ i ^ s : ℝ) : ℂ)) * Uᴴ := by - rw [← matrixFunction_rpow_eq hpsdρ]; unfold matrixFunction; rfl + rw [CFC.rpow_eq_cfc_real (a := ρ.toMatrix) (ha := by rw [Matrix.le_iff, sub_zero]; exact hpsdρ), + cfc_spectral_eq ρ.isHermitian (fun x => x ^ s)] have hσs : σ.toMatrix ^ (1 - s) = V * diagonal (fun j => ((ev_σ j ^ (1 - s) : ℝ) : ℂ)) * Vᴴ := by - rw [← matrixFunction_rpow_eq hpsdσ]; unfold matrixFunction; rfl + rw [CFC.rpow_eq_cfc_real (a := σ.toMatrix) (ha := by rw [Matrix.le_iff, sub_zero]; exact hpsdσ), + cfc_spectral_eq σ.isHermitian (fun x => x ^ (1 - s))] have hVU : Vᴴ * U = W := rfl rw [hρs, hσs] -- Use cyclic trace property and W = Vᴴ * U to reduce to W D_ρ Wᴴ D_σ @@ -1321,8 +1324,8 @@ private lemma trace_rpow_mul_channel_le /-- **Monotonicity of Relative Entropy**: Quantum channels do not increase relative entropy. -For a quantum channel Φ and positive definite density matrices ρ, σ: - S(Φ(ρ) || Φ(σ)) ≤ S(ρ || σ) +For a quantum channel Φ and density matrices ρ, σ: + D(Φ(ρ) ∥ Φ(σ)) ≤ D(ρ ∥ σ) **Proof**: Uses derivative argument on g(s) = F_s(Φρ, Φσ) - F_s(ρ, σ) where F_s(A, B) = Tr (Aˢ B¹⁻ˢ). Since g(s) ≥ 0 on (0,1] and g(1) = 0, we get g'(1) ≤ 0, @@ -1562,7 +1565,8 @@ private lemma trace_rpow_mul_jointly_concave (hs0 : 0 ≤ s) (hs1 : s ≤ 1) : p * (Tr (ρ₁ ^ s * σ₁ ^ (1 - s))).re + (1 - p) * (Tr (ρ₂ ^ s * σ₂ ^ (1 - s))).re ≤ - (Tr ((p • ρ₁.toMatrix + (1 - p) • ρ₂.toMatrix) ^ s * (p • σ₁.toMatrix + (1 - p) • σ₂.toMatrix) ^ (1 - s))).re := by + (Tr ((p • ρ₁.toMatrix + (1 - p) • ρ₂.toMatrix) ^ s * + (p • σ₁.toMatrix + (1 - p) • σ₂.toMatrix) ^ (1 - s))).re := by have hpsd₁ := ρ₁.posSemidef have hpsd₂ := ρ₂.posSemidef have hpsdσ₁ := σ₁.posSemidef @@ -1620,7 +1624,8 @@ theorem relativeEntropy_jointly_convex push_cast; ring_nf] rw [EReal.coe_le_coe_iff] -- Step 4: Derivative argument - -- Define h(s) = Re[Tr (ρ_mix^s σ_mix^{1-s})] - p Re[Tr (ρ₁^s σ₁^{1-s})] - (1-p) Re[Tr (ρ₂^s σ₂^{1-s})] + -- Define h(s) = Re[Tr (ρ_mix^s σ_mix^{1-s})] - p Re[Tr (ρ₁^s σ₁^{1-s})] + -- - (1-p) Re[Tr (ρ₂^s σ₂^{1-s})] let g : ℝ → ℝ := fun s => (ρ_mix.toMatrix ^ s * σ_mix.toMatrix ^ (1 - s)).trace.re - (p * (ρ₁.toMatrix ^ s * σ₁.toMatrix ^ (1 - s)).trace.re + @@ -1708,7 +1713,7 @@ variable {m : Type*} [Fintype m] [DecidableEq m] /-- **Quantum relative entropy is invariant under trace-preserving `*-`algebra equivalence** (PosDef case). -/ -theorem relativeEntropy_map_starAlgEquiv_posDef +lemma relativeEntropy_map_starAlgEquiv_posDef (ρ σ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) (hσ : σ.toMatrix.PosDef) (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) (hφ : ∀ A, (φ A).trace = A.trace) : @@ -1733,23 +1738,23 @@ theorem relativeEntropy_map_starAlgEquiv_posDef simp only [h_supp_map, h_supp, if_true] congr 1 change (Tr ((ρ.map φ hφ).toMatrix * - (matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian - - matrixLog (σ.map φ hφ).toMatrix (σ.map φ hφ).isHermitian))).re = + (cfc Real.log (ρ.map φ hφ).toMatrix - + cfc Real.log (σ.map φ hφ).toMatrix))).re = (Tr (ρ.toMatrix * - (matrixLog ρ.toMatrix ρ.isHermitian - - matrixLog σ.toMatrix σ.isHermitian))).re - have h_log_ρ : matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian = - φ (matrixLog ρ.toMatrix ρ.isHermitian) := by - change matrixLog (φ ρ.toMatrix) _ = _ - exact matrixLog_map_starAlgEquiv hρ φ - have h_log_σ : matrixLog (σ.map φ hφ).toMatrix (σ.map φ hφ).isHermitian = - φ (matrixLog σ.toMatrix σ.isHermitian) := by - change matrixLog (φ σ.toMatrix) _ = _ - exact matrixLog_map_starAlgEquiv hσ φ + (cfc Real.log ρ.toMatrix - + cfc Real.log σ.toMatrix))).re + have h_log_ρ : cfc Real.log (ρ.map φ hφ).toMatrix = + φ (cfc Real.log ρ.toMatrix) := by + change cfc Real.log (φ ρ.toMatrix) = _ + exact cfc_log_map_starAlgEquiv hρ φ + have h_log_σ : cfc Real.log (σ.map φ hφ).toMatrix = + φ (cfc Real.log σ.toMatrix) := by + change cfc Real.log (φ σ.toMatrix) = _ + exact cfc_log_map_starAlgEquiv hσ φ rw [h_log_ρ, h_log_σ, DensityMatrix.map_toMatrix, ← map_sub, ← map_mul, hφ] /-- Specialisation of `relativeEntropy_map_starAlgEquiv_posDef` to reindexing. -/ -theorem relativeEntropy_mapEquiv_posDef +lemma relativeEntropy_mapEquiv_posDef (ρ σ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) (hσ : σ.toMatrix.PosDef) (e : n ≃ m) : D(ρ.mapEquiv e ∥ σ.mapEquiv e) = D(ρ ∥ σ) := relativeEntropy_map_starAlgEquiv_posDef ρ σ hρ hσ _ _ diff --git a/QuantumSystem/Analysis/Entropy/SSA.lean b/QuantumSystem/Analysis/Entropy/SSA.lean deleted file mode 100644 index d03306f..0000000 --- a/QuantumSystem/Analysis/Entropy/SSA.lean +++ /dev/null @@ -1,605 +0,0 @@ -module - -public import QuantumSystem.Analysis.Entropy.KroneckerProduct -public import QuantumSystem.Analysis.Entropy.RelativeEntropy -public import QuantumSystem.Analysis.Entropy.Regularize -public import QuantumSystem.Analysis.Matrix.PartialTrace - -/-! -# Strong subadditivity of the von Neumann entropy (LocalNet form) - -For a quantum system on a `LocalNet L`, the public theorem in this file is stated -in a **common-region-explicit** form. Given regions - -- `ΛAB ⊆ ΛABC`, -- `ΛBC ⊆ ΛABC`, -- `ΛB ⊆ ΛABC`, -- `ΛA ⊆ ΛAB`, -- `ΛAB \ ΛA = ΛB`, -- `ΛABC \ ΛA = ΛBC`, - -the **strong subadditivity** inequality states: - - `S(ρ ↾ ΛAB) + S(ρ ↾ ΛBC) ≥ S(ρ) + S(ρ ↾ ΛB)` - -This avoids encoding the theorem through positional subsystem names such as -`A/B/C` or a distinguished three-site tuple. The familiar three-site statement - - `S(ρ ↾ {a, b}) + S(ρ ↾ {b, c}) ≥ S(ρ) + S(ρ ↾ {b})` - -is recovered by instantiating - -- `ΛABC = {a, b, c}`, -- `ΛAB = {a, b}`, -- `ΛBC = {b, c}`, -- `ΛB = {b}`, -- `ΛA = {a}`. - -This file first establishes the **mutual-information identity** in product-type form, - - `D(ρ_AB ‖ ρ_A ⊗ ρ_B) = -S(ρ_AB) + S(ρ_A) + S(ρ_B)`, - -for a PosDef bipartite density matrix `ρ_AB : DensityMatrix (n × m)` whose canonical -partial traces coincide with PosDef factor states `ρ_A` and `ρ_B`. It then uses this -identity together with the data-processing inequality to prove SSA in the -AQFT-natural form: - -1. The bipartite **mutual-information identity** (`relativeEntropy_kronecker_marginals_product`) - applied twice — once for the `(ΛA vs ΛABC \ ΛA)` bipartition of the full system, - once for the `(ΛA vs ΛAB \ ΛA)` bipartition of the `ΛAB`-marginal. -2. The **data-processing inequality** for relative entropy - (`Matrix.relativeEntropy_channel_le`) applied to the LocalNet - `Matrix.QuantumChannel.restrict` channel for the inclusion `ΛAB ⊆ ΛABC`. - -## Main results - -* `Matrix.relativeEntropy_kronecker_marginals_product` — product-type mutual-information - identity. -* `DensityMatrix.vonNeumannEntropy_SSA` — SSA for arbitrary states on a - `LocalNet`, with the common region and split equalities explicit. - -## References - -* Nielsen, Chuang, *Quantum Computation and Quantum Information* §11.3 — quantum mutual - information `I(A:B) = S(ρ_A) + S(ρ_B) − S(ρ_AB)` and the relative-entropy identity - `D(ρ_AB ‖ ρ_A ⊗ ρ_B) = I(A:B)`. --/ - -@[expose] public section - -namespace Matrix - -open scoped Kronecker MatrixOrder ComplexOrder QuantumInfo - -variable {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] - -/-! ### Product-type relative-entropy identity -/ - -/-- **Mutual-information identity (product-type form)**: for a bipartite density -matrix `ρ_AB : DensityMatrix (n × m)` whose canonical partial traces coincide with PosDef -factor states `ρ_A` and `ρ_B`, the relative entropy w.r.t. the product `ρ_A ⊗ ρ_B` -equals `-S(ρ_AB) + S(ρ_A) + S(ρ_B)`. -/ -theorem relativeEntropy_kronecker_marginals_product - (ρ_AB : DensityMatrix (n × m)) - (ρ_A : DensityMatrix n) (hρ_A : ρ_A.toMatrix.PosDef) - (ρ_B : DensityMatrix m) (hρ_B : ρ_B.toMatrix.PosDef) - (h_A_partialTrace : tr₂(ρ_AB.toMatrix) = ρ_A.toMatrix) - (h_B_partialTrace : tr₁(ρ_AB.toMatrix) = ρ_B.toMatrix) : - D(ρ_AB ∥ ρ_A ⊗ ρ_B) = -S(ρ_AB) + S(ρ_A) + S(ρ_B) := by - classical - have hρ_A_kron_pos : (ρ_A ⊗ ρ_B).toMatrix.PosDef := by - rw [DensityMatrix.kronecker_toMatrix]; exact hρ_A.kronecker hρ_B - -- supp(ρ) ⊆ supp(ρ_A ⊗ ρ_B) holds for PosDef σ. - have h_supp : suppSubset ρ_AB.toMatrix (ρ_A ⊗ ρ_B).toMatrix := by - intro v hv - have hinj : Function.Injective (ρ_A ⊗ ρ_B).toMatrix.mulVec := - Matrix.mulVec_injective_iff_isUnit.mpr hρ_A_kron_pos.isUnit - have h0 : (ρ_A ⊗ ρ_B).toMatrix.mulVec 0 = 0 := by simp - have hv_zero : v = 0 := hinj (hv.trans h0.symm) - rw [hv_zero]; simp - unfold relativeEntropy - simp only [h_supp, if_true] - -- log of ρ_A ⊗ ρ_B decomposes via matrixLog_kronecker_posDef. - -- The two `IsHermitian` proofs differ proof-wise but match by Prop irrelevance. - have h_log_kron : matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian = - matrixLog ρ_A.toMatrix hρ_A.1 ⊗ₖ (1 : Matrix m m ℂ) + - (1 : Matrix n n ℂ) ⊗ₖ matrixLog ρ_B.toMatrix hρ_B.1 := - matrixLog_kronecker_posDef hρ_A hρ_B - -- The trace identity after substitution. - have h_trace_log_kron : - Tr (ρ_AB.toMatrix * matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian) = - Tr (ρ_A.toMatrix * matrixLog ρ_A.toMatrix hρ_A.1) + - Tr (ρ_B.toMatrix * matrixLog ρ_B.toMatrix hρ_B.1) := by - rw [h_log_kron, Matrix.mul_add, Matrix.trace_add, trace_mul_kronecker_one_right, - trace_mul_kronecker_one_left, h_A_partialTrace, h_B_partialTrace] - -- Split (log ρ - log(ρ_A⊗ρ_B)) and reduce trace. - have h_split : Tr (ρ_AB.toMatrix * (matrixLog ρ_AB.toMatrix ρ_AB.isHermitian - - matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian)) = - Tr (ρ_AB.toMatrix * matrixLog ρ_AB.toMatrix ρ_AB.isHermitian) - - Tr (ρ_AB.toMatrix * matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian) := by - rw [Matrix.mul_sub, Matrix.trace_sub] - -- Translate to the goal in EReal. - change (↑(Tr (ρ_AB.toMatrix * (matrixLog ρ_AB.toMatrix ρ_AB.isHermitian - - matrixLog (ρ_A ⊗ ρ_B).toMatrix (ρ_A ⊗ ρ_B).isHermitian))).re : EReal) = - -S(ρ_AB) + S(ρ_A) + S(ρ_B) - rw [h_split, Complex.sub_re, h_trace_log_kron, Complex.add_re] - -- Now: ↑((Tr(ρ · log ρ)).re - ((Tr(ρ_A · log ρ_A)).re + (Tr(ρ_B · log ρ_B)).re)) - -- = -S(ρ) + S(ρ_A) + S(ρ_B) - -- Express the LHS Real value: - set α : ℝ := (Tr (ρ_AB.toMatrix * matrixLog ρ_AB.toMatrix ρ_AB.isHermitian)).re with hα - set β : ℝ := (Tr (ρ_A.toMatrix * matrixLog ρ_A.toMatrix hρ_A.1)).re with hβ - set γ : ℝ := (Tr (ρ_B.toMatrix * matrixLog ρ_B.toMatrix hρ_B.1)).re with hγ - -- And the S values: - change (↑(α - (β + γ)) : EReal) = -S(ρ_AB) + S(ρ_A) + S(ρ_B) - have hSρ : S(ρ_AB) = -α := by - change -(Tr (ρ_AB.toMatrix * DensityMatrix.log ρ_AB)).re = -α - rfl - have hSρ_A : S(ρ_A) = -β := by - change -(Tr (ρ_A.toMatrix * DensityMatrix.log ρ_A)).re = -β - rfl - have hSρ_B : S(ρ_B) = -γ := by - change -(Tr (ρ_B.toMatrix * DensityMatrix.log ρ_B)).re = -γ - rfl - rw [hSρ, hSρ_A, hSρ_B] - -- Goal in EReal: ↑(α - (β + γ)) = -↑(-α) + ↑(-β) + ↑(-γ) - -- Equivalent Real identity: - have h_real : α - (β + γ) = -(-α) + (-β) + (-γ) := by ring - exact_mod_cast h_real - -end Matrix - -/-! ### Strong subadditivity (LocalNet form, PosDef case) - -The main theorem. We use the bipartite mutual-information identity twice -(for `(A vs BC)` and for `(A vs B)` within `ρ ↾ {A, B}`) and the data-processing -inequality on the LocalNet `restrict` channel for `{A, B} ⊆ univ`. -/ - -namespace DensityMatrix - -open scoped Kronecker MatrixOrder ComplexOrder -open scoped Matrix.QuantumInfo -open scoped LocalNet.QuantumInfo - -variable {L : LocalNet} - -/-! #### Split-explicit nested-region SSA - -The next theorem is the region-level core of the three-site wrapper below. It is -parameterised by nested inclusions `ΛA ⊆ ΛAB ⊆ ΛABC`; the common/middle region is the -explicit complement `ΛAB \ ΛA`, and the other side is `ΛABC \ ΛA`. --/ - -/-- **Strong subadditivity (PosDef case, split-explicit nested-region form).** - -Given nested regions `ΛA ⊆ ΛAB ⊆ ΛABC`, write the middle/common region as -`ΛAB \ ΛA` and the complementary side as `ΛABC \ ΛA`. For a PosDef state `ρ_ABC` on -`ΛABC`, assuming the relevant marginals are PosDef, strong subadditivity is - -`S(ρ_ABC ↾ ΛAB) + S(ρ_ABC ↾ (ΛABC \ ΛA)) ≥ S(ρ_ABC) + S(ρ_ABC ↾ (ΛAB \ ΛA))`. - -This statement contains the split subset in the hypotheses and does not rely on -site names such as `a b c` or product-factor names such as `A/B`. -/ -private lemma vonNeumannEntropy_SSA_posDef_nested - {L : LocalNet} {ΛA ΛAB ΛABC : Finset L.sites} - (h_AB : ΛAB ⊆ ΛABC) (h_A : ΛA ⊆ ΛAB) - (ρ_ABC : L.densityMatrix ΛABC) - (h_ABC_pos : ρ_ABC.toMatrix.PosDef) - (h_A_pos : (ρ_ABC ↾[h_A.trans h_AB]).toMatrix.PosDef) - (h_AB_pos : (ρ_ABC ↾[h_AB]).toMatrix.PosDef) - (h_BC_pos : (ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]).toMatrix.PosDef) - (h_B_pos : (ρ_ABC ↾[(Finset.sdiff_subset_sdiff h_AB (le_refl ΛA)).trans Finset.sdiff_subset]).toMatrix.PosDef) : - S(ρ_ABC ↾ ΛAB) + - S(ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]) ≥ - S(ρ_ABC) + - S(ρ_ABC ↾[(Finset.sdiff_subset_sdiff h_AB (le_refl ΛA)).trans Finset.sdiff_subset]) := by - classical - set h_B_in_BC : (ΛAB \ ΛA) ⊆ (ΛABC \ ΛA) := - Finset.sdiff_subset_sdiff h_AB (le_refl ΛA) with hh_B_in_BC - set ρ_A : DensityMatrix (L.regionIdx ΛA) := - ρ_ABC ↾[h_A.trans h_AB] with hh_ρ_A - set ρ_AB : DensityMatrix (L.regionIdx ΛAB) := - ρ_ABC ↾[h_AB] with hh_ρ_AB - set ρ_BC : DensityMatrix (L.regionIdx (ΛABC \ ΛA)) := - ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)] with hh_ρ_BC - set ρ_B : DensityMatrix (L.regionIdx (ΛAB \ ΛA)) := - ρ_ABC ↾[h_B_in_BC.trans Finset.sdiff_subset] with hh_ρ_B - have hρ_A : ρ_A.toMatrix.PosDef := h_A_pos - have hρ_AB : ρ_AB.toMatrix.PosDef := h_AB_pos - have hρ_BC : ρ_BC.toMatrix.PosDef := h_BC_pos - have hρ_B : ρ_B.toMatrix.PosDef := h_B_pos - -- Bipartite views of `ρ` and `ρ_AB` via the explicit split subset. - set ρ_pt : DensityMatrix (L.regionIdx ΛA × L.regionIdx (ΛABC \ ΛA)) := - ρ_ABC.mapEquiv (L.combineIdx (h_A.trans h_AB)) with hρ_pt_def - have hρ_pt : ρ_pt.toMatrix.PosDef := h_ABC_pos.mapEquiv _ - set ρ_AB_pt : DensityMatrix (L.regionIdx ΛA × L.regionIdx (ΛAB \ ΛA)) := - ρ_AB.mapEquiv (L.combineIdx h_A) with hρ_AB_pt_def - have hρ_AB_pt : ρ_AB_pt.toMatrix.PosDef := hρ_AB.mapEquiv _ - have hρ_pt_sub : ρ_pt.toMatrix = - ρ_ABC.toMatrix.submatrix (L.combineIdx (h_A.trans h_AB)) - (L.combineIdx (h_A.trans h_AB)) := by - simp [hρ_pt_def, DensityMatrix.mapEquiv_toMatrix] - have hρ_AB_pt_sub : ρ_AB_pt.toMatrix = - ρ_AB.toMatrix.submatrix (L.combineIdx h_A) (L.combineIdx h_A) := by - simp [hρ_AB_pt_def, DensityMatrix.mapEquiv_toMatrix] - -- Product reference state for the full split and its lift back to `ΛABC`. - set σ_pt : DensityMatrix (L.regionIdx ΛA × L.regionIdx (ΛABC \ ΛA)) := - ρ_A ⊗ ρ_BC with hσ_pt_def - have hσ_pt : σ_pt.toMatrix.PosDef := by - rw [hσ_pt_def, DensityMatrix.kronecker_toMatrix] - exact hρ_A.kronecker hρ_BC - set σ_full : L.densityMatrix ΛABC := - σ_pt.mapEquiv (L.combineIdx (h_A.trans h_AB)).symm with hσ_full_def - have hσ_full : σ_full.toMatrix.PosDef := hσ_pt.mapEquiv _ - -- Mutual-information identity for the full split. - have h_ptA_ρ_pt : tr₂(ρ_pt.toMatrix) = ρ_A.toMatrix := by - ext x x' - rw [hρ_pt_sub, ← Matrix.restrict_eq_partialTrace_combineIdx] - rfl - have h_ptBC_ρ_pt : tr₁(ρ_pt.toMatrix) = ρ_BC.toMatrix := by - ext y y' - rw [hρ_pt_sub, ← Matrix.restrict_compl_eq_partialTrace_combineIdx - (h_A.trans h_AB)] - rfl - have h_mut_full : - D(ρ_pt ∥ σ_pt) = -S(ρ_pt) + S(ρ_A) + S(ρ_BC) := - Matrix.relativeEntropy_kronecker_marginals_product ρ_pt ρ_A hρ_A ρ_BC hρ_BC - h_ptA_ρ_pt h_ptBC_ρ_pt - -- Mutual-information identity for the `ΛA ⊆ ΛAB` split. - have h_ptA_AB : tr₂(ρ_AB_pt.toMatrix) = ρ_A.toMatrix := by - ext x x' - rw [hρ_AB_pt_sub, ← Matrix.restrict_eq_partialTrace_combineIdx] - change ((ρ_ABC ↾[h_AB]) ↾[h_A]).toMatrix x x' = - (ρ_ABC ↾[h_A.trans h_AB]).toMatrix x x' - rw [DensityMatrix.restrict_restrict] - have h_ptB_AB : tr₁(ρ_AB_pt.toMatrix) = ρ_B.toMatrix := by - ext y y' - rw [hρ_AB_pt_sub, ← Matrix.restrict_compl_eq_partialTrace_combineIdx h_A] - change ((ρ_ABC ↾[h_AB]) ↾[(Finset.sdiff_subset : - ΛAB \ ΛA ⊆ ΛAB)]).toMatrix y y' = - (ρ_ABC ↾[h_B_in_BC.trans Finset.sdiff_subset]).toMatrix y y' - rw [DensityMatrix.restrict_restrict] - have h_mut_AB : - D(ρ_AB_pt ∥ ρ_A ⊗ ρ_B) = -S(ρ_AB_pt) + S(ρ_A) + S(ρ_B) := - Matrix.relativeEntropy_kronecker_marginals_product ρ_AB_pt ρ_A hρ_A ρ_B hρ_B - h_ptA_AB h_ptB_AB - -- DPI for restriction from `ΛABC` to `ΛAB`. - set Φ : Matrix.QuantumChannel (L.regionIdx ΛABC) (L.regionIdx ΛAB) := - Matrix.QuantumChannel.restrict h_AB with hΦ_def - have h_Φρ_mat : ((Φ : Matrix.QuantumChannel _ _) ρ_ABC).toMatrix = ρ_AB.toMatrix := by - change (ρ_ABC ↾[h_AB]).toMatrix = ρ_AB.toMatrix - rfl - have h_Φρ_pos : ((Φ : Matrix.QuantumChannel _ _) ρ_ABC).toMatrix.PosDef := h_Φρ_mat ▸ hρ_AB - -- Restricting the lifted product state gives the product of the restricted factors. - have h_Φσ_mat : - ((Φ : Matrix.QuantumChannel _ _) σ_full).toMatrix = - (ρ_A ⊗ ρ_B).toMatrix.submatrix - (L.combineIdx h_A).symm (L.combineIdx h_A).symm := by - change Matrix.restrict h_AB σ_full.toMatrix = _ - ext s s' - rw [show s = (L.combineIdx h_A) - ((L.combineIdx h_A).symm s) from - ((L.combineIdx h_A).apply_symm_apply s).symm, - show s' = (L.combineIdx h_A) - ((L.combineIdx h_A).symm s') from - ((L.combineIdx h_A).apply_symm_apply s').symm] - set p := (L.combineIdx h_A).symm s with hp - set p' := (L.combineIdx h_A).symm s' with hp' - rw [Matrix.restrict_apply, Matrix.submatrix_apply] - simp only [Equiv.symm_apply_apply] - simp_rw [Matrix.combineIdx_assoc_eq h_AB h_A] - have h_σ_full_apply : ∀ (x x' : L.regionIdx ΛA) - (y y' : L.regionIdx (ΛABC \ ΛA)), - σ_full.toMatrix - (L.combineIdx (h_A.trans h_AB) (x, y)) - (L.combineIdx (h_A.trans h_AB) (x', y')) = - σ_pt.toMatrix (x, y) (x', y') := by - intro x x' y y' - simp [hσ_full_def, DensityMatrix.mapEquiv_toMatrix, - Matrix.submatrix_apply, Equiv.symm_apply_apply] - simp_rw [h_σ_full_apply] - rw [hσ_pt_def, DensityMatrix.kronecker_toMatrix] - simp only [Matrix.kronecker_apply] - rw [← Finset.mul_sum] - rw [DensityMatrix.kronecker_toMatrix, Matrix.kronecker_apply] - congr 1 - rw [show ρ_B.toMatrix p.2 p'.2 = Matrix.restrict h_B_in_BC ρ_BC.toMatrix p.2 p'.2 - from by - change Matrix.restrict (h_B_in_BC.trans Finset.sdiff_subset) ρ_ABC.toMatrix p.2 p'.2 = - Matrix.restrict h_B_in_BC (Matrix.restrict Finset.sdiff_subset ρ_ABC.toMatrix) p.2 p'.2 - rw [Matrix.restrict_restrict], - Matrix.restrict_eq_partialTrace_combineIdx h_B_in_BC, - Matrix.partialTrace_refl_apply] - have h_compl_eq : (ΛABC \ ΛA) \ (ΛAB \ ΛA) = ΛABC \ ΛAB := by - ext x - simp only [Finset.mem_sdiff] - constructor - · rintro ⟨⟨hxABC, hxA⟩, hx_not_BminusA⟩ - exact ⟨hxABC, fun hxAB => hx_not_BminusA ⟨hxAB, hxA⟩⟩ - · rintro ⟨hxABC, hxAB⟩ - exact ⟨⟨hxABC, fun hxA => hxAB (h_A hxA)⟩, - fun hxBminusA => hxAB hxBminusA.1⟩ - rw [← (L.regionIdxCongr h_compl_eq.symm).sum_comp - (fun b => ρ_BC.toMatrix.submatrix (L.combineIdx h_B_in_BC) (L.combineIdx h_B_in_BC) - (p.2, b) (p'.2, b))] - refine Finset.sum_congr rfl fun γ _ => ?_ - have hR_eq : ∀ (z : L.regionIdx (ΛAB \ ΛA)), - Matrix.restrictAssocEquiv h_AB h_A (z, γ) = - L.combineIdx h_B_in_BC (z, L.regionIdxCongr h_compl_eq.symm γ) := by - intro z - funext ⟨v, hv⟩ - by_cases hv_in_AB : v ∈ ΛAB - · have hv_in_combine : v ∈ ΛAB \ ΛA := - Finset.mem_sdiff.mpr ⟨hv_in_AB, (Finset.mem_sdiff.mp hv).2⟩ - rw [LocalNet.combineIdx_apply_mem h_B_in_BC _ _ ⟨v, hv⟩ hv_in_combine] - exact dif_pos hv_in_AB - · have hv_not_in_combine : v ∉ ΛAB \ ΛA := fun h_in => - hv_in_AB (Finset.mem_sdiff.mp h_in).1 - have hv_compl : v ∈ ΛABC \ ΛAB := - Finset.mem_sdiff.mpr ⟨(Finset.mem_sdiff.mp hv).1, hv_in_AB⟩ - have hv_compl_compl : v ∈ (ΛABC \ ΛA) \ (ΛAB \ ΛA) := - Finset.mem_sdiff.mpr ⟨hv, hv_not_in_combine⟩ - rw [LocalNet.combineIdx_apply_not_mem h_B_in_BC _ _ ⟨v, hv⟩ hv_not_in_combine, - show L.regionIdxCongr h_compl_eq.symm γ ⟨v, hv_compl_compl⟩ = γ ⟨v, hv_compl⟩ - from LocalNet.regionIdxCongr_apply (L := L) h_compl_eq.symm γ hv_compl hv_compl_compl] - exact dif_neg hv_in_AB - rw [hR_eq p.2, hR_eq p'.2] - rfl - have h_Φσ_pos : ((Φ : Matrix.QuantumChannel _ _) σ_full).toMatrix.PosDef := by - rw [h_Φσ_mat] - have h_kron_pos : (ρ_A ⊗ ρ_B).toMatrix.PosDef := by - rw [DensityMatrix.kronecker_toMatrix] - exact hρ_A.kronecker hρ_B - exact h_kron_pos.mapEquiv _ - have h_dpi : - D((Φ : Matrix.QuantumChannel _ _) ρ_ABC ∥ (Φ : Matrix.QuantumChannel _ _) σ_full) ≤ - D(ρ_ABC ∥ σ_full) := - Matrix.relativeEntropy_channel_le Φ ρ_ABC σ_full - have h_dpi_lhs : - D((Φ : Matrix.QuantumChannel _ _) ρ_ABC ∥ (Φ : Matrix.QuantumChannel _ _) σ_full) = - D(ρ_AB_pt ∥ ρ_A ⊗ ρ_B) := by - have h_Φρ_dm : (Φ : Matrix.QuantumChannel _ _) ρ_ABC = - ρ_AB_pt.mapEquiv (L.combineIdx h_A).symm := by - apply DensityMatrix.ext - rw [h_Φρ_mat, DensityMatrix.mapEquiv_toMatrix, hρ_AB_pt_sub, - Matrix.submatrix_submatrix] - simp - have h_Φσ_dm : (Φ : Matrix.QuantumChannel _ _) σ_full = - (ρ_A ⊗ ρ_B).mapEquiv (L.combineIdx h_A).symm := by - apply DensityMatrix.ext - exact h_Φσ_mat - rw [h_Φρ_dm, h_Φσ_dm] - have h_kron_pos : (ρ_A ⊗ ρ_B).toMatrix.PosDef := by - rw [DensityMatrix.kronecker_toMatrix] - exact hρ_A.kronecker hρ_B - exact Matrix.relativeEntropy_mapEquiv_posDef _ _ hρ_AB_pt h_kron_pos _ - have h_ρ_eq_pt : ρ_ABC = ρ_pt.mapEquiv (L.combineIdx (h_A.trans h_AB)).symm := by - apply DensityMatrix.ext - rw [DensityMatrix.mapEquiv_toMatrix, hρ_pt_sub, Matrix.submatrix_submatrix] - simp - have h_σ_full_eq_pt : - σ_full = σ_pt.mapEquiv (L.combineIdx (h_A.trans h_AB)).symm := rfl - have h_dpi_rhs : D(ρ_ABC ∥ σ_full) = D(ρ_pt ∥ σ_pt) := by - rw [h_ρ_eq_pt, h_σ_full_eq_pt] - exact Matrix.relativeEntropy_mapEquiv_posDef _ _ hρ_pt hσ_pt _ - have h_Sρ_eq : S(ρ_ABC) = S(ρ_pt) := by - rw [h_ρ_eq_pt] - exact Matrix.vonNeumannEntropy_mapEquiv_posDef _ hρ_pt _ - have h_SρAB_eq : S(ρ_AB) = S(ρ_AB_pt) := by - rw [show ρ_AB = ρ_AB_pt.mapEquiv (L.combineIdx h_A).symm from by - apply DensityMatrix.ext - rw [DensityMatrix.mapEquiv_toMatrix, hρ_AB_pt_sub, Matrix.submatrix_submatrix] - simp] - exact Matrix.vonNeumannEntropy_mapEquiv_posDef _ hρ_AB_pt _ - rw [h_dpi_lhs, h_dpi_rhs] at h_dpi - rw [h_mut_AB, h_mut_full] at h_dpi - rw [h_Sρ_eq, h_SρAB_eq] - have h_real : - -S(ρ_AB_pt) + S(ρ_A) + S(ρ_B) ≤ - -S(ρ_pt) + S(ρ_A) + S(ρ_BC) := by - exact_mod_cast h_dpi - linarith - -/-! #### Finset-equality bridges for the user-facing wrapper - -For two `Λ ⊆ Λ_total` and `Λ' ⊆ Λ_total` Subset proofs whose Finsets are propositionally -equal (`Λ = Λ'`), the partial-trace `restrict h ρ` and `restrict h' ρ` are essentially -identical: their entropies and PosDef status agree. Discharged by `subst h_eq` plus -proof-irrelevance for `Subset`. Used in `vonNeumannEntropy_SSA_posDef` to bridge -user-facing `S(restrict h_BC ρ)` (with `{b,c}`) to the bipartite-natural form -`S(restrict sdiff_subset ρ)` (with `{a,b,c} \ {a}`). -/ - -private lemma vonNeumannEntropy_restrict_finset_eq - {L : LocalNet} {Λ Λ' Λ_total : Finset L.sites} (h_eq : Λ = Λ') - (h : Λ ⊆ Λ_total) (h' : Λ' ⊆ Λ_total) (ρ : L.densityMatrix Λ_total) : - S(ρ ↾[h]) = S(ρ ↾[h']) := by - subst h_eq - rfl - -private lemma posDef_restrict_finset_eq - {L : LocalNet} {Λ Λ' Λ_total : Finset L.sites} (h_eq : Λ = Λ') - (h : Λ ⊆ Λ_total) (h' : Λ' ⊆ Λ_total) (ρ : L.densityMatrix Λ_total) : - (ρ ↾[h]).toMatrix.PosDef ↔ (ρ ↾[h']).toMatrix.PosDef := by - subst h_eq - rfl - -/-- **Strong subadditivity (PosDef case, common-region-explicit form).** - -This is the public region-level wrapper around -`vonNeumannEntropy_SSA_posDef_nested`. The theorem does not infer the common region from -the names `a b c` or from a product-factor order. Instead it receives explicit data: - -* `ΛAB ⊆ ΛABC`, the first two-block marginal, -* `ΛA ⊆ ΛAB`, the split used inside `ΛAB`, -* `ΛBC ⊆ ΛABC`, the other two-block marginal, -* `ΛB ⊆ ΛABC`, the common/middle marginal, -* `ΛAB \ ΛA = ΛB`, identifying the common region, -* `ΛABC \ ΛA = ΛBC`, identifying the side obtained by tracing out `ΛA`. - -Under PosDef hypotheses for the displayed marginals, the conclusion is exactly -`S(ρ_ABC ↾ ΛAB) + S(ρ_ABC ↾ ΛBC) ≥ S(ρ_ABC) + S(ρ_ABC ↾ ΛB)`. -/ -private lemma vonNeumannEntropy_SSA_posDef - {L : LocalNet} {ΛA ΛB ΛAB ΛBC ΛABC : Finset L.sites} - (h_AB_total : ΛAB ⊆ ΛABC) (h_A_in_AB : ΛA ⊆ ΛAB) - (h_BC_total : ΛBC ⊆ ΛABC) (h_B_total : ΛB ⊆ ΛABC) - (h_B_eq : ΛAB \ ΛA = ΛB) (h_BC_eq : ΛABC \ ΛA = ΛBC) - (ρ_ABC : L.densityMatrix ΛABC) - (h_ABC_pos : ρ_ABC.toMatrix.PosDef) - (h_A_pos : (ρ_ABC ↾[h_A_in_AB.trans h_AB_total]).toMatrix.PosDef) - (h_AB_pos : (ρ_ABC ↾[h_AB_total]).toMatrix.PosDef) - (h_BC_pos : (ρ_ABC ↾[h_BC_total]).toMatrix.PosDef) - (h_B_pos : (ρ_ABC ↾[h_B_total]).toMatrix.PosDef) : - S(ρ_ABC ↾[h_AB_total]) + S(ρ_ABC ↾[h_BC_total]) ≥ - S(ρ_ABC) + S(ρ_ABC ↾[h_B_total]) := by - classical - let h_B_nested : (ΛAB \ ΛA) ⊆ ΛABC := - (Finset.sdiff_subset_sdiff h_AB_total (le_refl ΛA)).trans Finset.sdiff_subset - have h_BC_pos_nested : - (ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]).toMatrix.PosDef := - posDef_restrict_finset_eq h_BC_eq.symm h_BC_total Finset.sdiff_subset ρ_ABC |>.mp h_BC_pos - have h_B_pos_nested : (ρ_ABC ↾[h_B_nested]).toMatrix.PosDef := - posDef_restrict_finset_eq h_B_eq.symm h_B_total h_B_nested ρ_ABC |>.mp h_B_pos - have h_nested := vonNeumannEntropy_SSA_posDef_nested h_AB_total h_A_in_AB ρ_ABC h_ABC_pos - h_A_pos h_AB_pos h_BC_pos_nested h_B_pos_nested - have h_S_BC : S(ρ_ABC ↾[h_BC_total]) = - S(ρ_ABC ↾[(Finset.sdiff_subset : ΛABC \ ΛA ⊆ ΛABC)]) := - vonNeumannEntropy_restrict_finset_eq h_BC_eq.symm - h_BC_total Finset.sdiff_subset ρ_ABC - have h_S_B : S(ρ_ABC ↾[h_B_total]) = S(ρ_ABC ↾[h_B_nested]) := - vonNeumannEntropy_restrict_finset_eq h_B_eq.symm h_B_total h_B_nested ρ_ABC - rwa [← h_S_BC, ← h_S_B] at h_nested - - -/-- **Strong subadditivity (common-region-explicit form, no PosDef hypothesis).** - -PosDef-free version of `vonNeumannEntropy_SSA_posDef`. Given the geometric data of a -common-region split - -* `ΛAB ⊆ ΛABC`, `ΛA ⊆ ΛAB`, `ΛBC ⊆ ΛABC`, `ΛB ⊆ ΛABC`, -* `ΛAB \ ΛA = ΛB`, `ΛABC \ ΛA = ΛBC`, - -and `[Nonempty (L.regionIdx ΛABC)]` (which propagates to every sub-region by -`regionIdx_nonempty_of_subset`), strong subadditivity holds for every density matrix -`ρ_ABC : L.densityMatrix ΛABC`: - - `S(ρ_ABC ↾ ΛAB) + S(ρ_ABC ↾ ΛBC) ≥ S(ρ_ABC) + S(ρ_ABC ↾ ΛB)`. - -The proof regularises `ρ` to the PosDef state `(1 - ε) ρ + ε · π_ΛABC` for `ε ∈ (0, 1]`, -applies `vonNeumannEntropy_SSA_posDef`, and passes to the limit `ε → 0⁺` -via the eigenvalue continuity formulas in `Regularize.lean`. -/ -theorem vonNeumannEntropy_SSA - {L : LocalNet} {ΛA ΛB ΛAB ΛBC ΛABC : Finset L.sites} - (h_AB : ΛAB ⊆ ΛABC) (h_A : ΛA ⊆ ΛAB) - (h_BC : ΛBC ⊆ ΛABC) (h_B : ΛB ⊆ ΛABC) - (h_B_eq : ΛAB \ ΛA = ΛB) (h_BC_eq : ΛABC \ ΛA = ΛBC) - [Nonempty (L.regionIdx ΛABC)] - (ρ_ABC : L.densityMatrix ΛABC) : - S(ρ_ABC ↾[h_AB]) + S(ρ_ABC ↾[h_BC]) ≥ - S(ρ_ABC) + S(ρ_ABC ↾[h_B]) := by - -- Sub-region Nonempty instances, derived from `Nonempty (regionIdx ΛABC)`. - haveI : Nonempty (L.regionIdx ΛAB) := L.regionIdx_nonempty_of_subset h_AB - haveI : Nonempty (L.regionIdx ΛBC) := L.regionIdx_nonempty_of_subset h_BC - haveI : Nonempty (L.regionIdx ΛB) := L.regionIdx_nonempty_of_subset h_B - haveI : Nonempty (L.regionIdx ΛA) := - L.regionIdx_nonempty_of_subset (h_A.trans h_AB) - -- Marginals and eigenvalue-formula functions. - set ρ_AB := ρ_ABC ↾[h_AB] with hρ_AB - set ρ_BC := ρ_ABC ↾[h_BC] with hρ_BC - set ρ_B := ρ_ABC ↾[h_B] with hρ_B - let f_AB : ℝ → ℝ := fun ε => - ∑ i, Real.negMulLog ((1 - ε) * ρ_AB.isHermitian.eigenvalues i + - ε / Fintype.card (L.regionIdx ΛAB)) - let f_BC : ℝ → ℝ := fun ε => - ∑ i, Real.negMulLog ((1 - ε) * ρ_BC.isHermitian.eigenvalues i + - ε / Fintype.card (L.regionIdx ΛBC)) - let f_full : ℝ → ℝ := fun ε => - ∑ i, Real.negMulLog ((1 - ε) * ρ_ABC.isHermitian.eigenvalues i + - ε / Fintype.card (L.regionIdx ΛABC)) - let f_B : ℝ → ℝ := fun ε => - ∑ i, Real.negMulLog ((1 - ε) * ρ_B.isHermitian.eigenvalues i + - ε / Fintype.card (L.regionIdx ΛB)) - -- For ε ∈ (0, 1]: regularised state and its 4 marginals are PosDef, so the PosDef - -- common-region SSA applies. - have h_ineq_pos : ∀ ε : ℝ, 0 < ε → ε ≤ 1 → - f_full ε + f_B ε ≤ f_AB ε + f_BC ε := by - intro ε hε_pos hε_le - have hρ_reg_pos : - (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix.PosDef := - DensityMatrix.regularize_posDef ρ_ABC hε_pos hε_le - have h_AB_eq_reg := - regularize_restrict_toMatrix h_AB ρ_ABC hε_pos.le hε_le - have h_BC_eq_reg := - regularize_restrict_toMatrix h_BC ρ_ABC hε_pos.le hε_le - have h_A_eq_reg := - regularize_restrict_toMatrix (h_A.trans h_AB) ρ_ABC hε_pos.le hε_le - have h_B_eq_reg := - regularize_restrict_toMatrix h_B ρ_ABC hε_pos.le hε_le - have hAB_reg_pos : - ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) ↾[h_AB]).toMatrix.PosDef - := by - change (Matrix.restrict h_AB - (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef - rw [h_AB_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le - have hBC_reg_pos : - ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) ↾[h_BC]).toMatrix.PosDef - := by - change (Matrix.restrict h_BC - (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef - rw [h_BC_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le - have hA_reg_pos : - ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) - ↾[h_A.trans h_AB]).toMatrix.PosDef := by - change (Matrix.restrict (h_A.trans h_AB) - (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef - rw [h_A_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le - have hB_reg_pos : - ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) ↾[h_B]).toMatrix.PosDef - := by - change (Matrix.restrict h_B - (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).toMatrix).PosDef - rw [h_B_eq_reg]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le - have h_ssa := vonNeumannEntropy_SSA_posDef - h_AB h_A h_BC h_B h_B_eq h_BC_eq - (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) - hρ_reg_pos hA_reg_pos hAB_reg_pos hBC_reg_pos hB_reg_pos - have h_AB_dm := regularize_restrict h_AB ρ_ABC hε_pos.le hε_le - have h_BC_dm := regularize_restrict h_BC ρ_ABC hε_pos.le hε_le - have h_B_dm := regularize_restrict h_B ρ_ABC hε_pos.le hε_le - rw [h_AB_dm, h_BC_dm, h_B_dm] at h_ssa - have ef_AB := - Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_AB hε_pos.le hε_le - have ef_BC := - Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_BC hε_pos.le hε_le - have ef_full := - Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_ABC hε_pos.le hε_le - have ef_B := - Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_B hε_pos.le hε_le - rw [ef_AB, ef_BC, ef_full, ef_B] at h_ssa - exact h_ssa - have h_cont_AB : Filter.Tendsto f_AB (nhds 0) (nhds S(ρ_AB)) := - Matrix.tendsto_negMulLog_regularize_sum_zero ρ_AB - have h_cont_BC : Filter.Tendsto f_BC (nhds 0) (nhds S(ρ_BC)) := - Matrix.tendsto_negMulLog_regularize_sum_zero ρ_BC - have h_cont_full : Filter.Tendsto f_full (nhds 0) (nhds S(ρ_ABC)) := - Matrix.tendsto_negMulLog_regularize_sum_zero ρ_ABC - have h_cont_B : Filter.Tendsto f_B (nhds 0) (nhds S(ρ_B)) := - Matrix.tendsto_negMulLog_regularize_sum_zero ρ_B - have h_within : ∀ᶠ ε in nhdsWithin (0 : ℝ) (Set.Ioi 0), - f_full ε + f_B ε ≤ f_AB ε + f_BC ε := by - rw [eventually_nhdsWithin_iff] - have h_le_one : ∀ᶠ ε in nhds (0 : ℝ), ε ≤ 1 := - Filter.eventually_of_mem (IsOpen.mem_nhds isOpen_Iio (by norm_num : (0 : ℝ) < 1)) <| by - intros ε hε - exact le_of_lt hε - filter_upwards [h_le_one] with ε hε_le_one hε_pos - exact h_ineq_pos ε hε_pos hε_le_one - have h_LHS_lim : - Filter.Tendsto (fun ε => f_AB ε + f_BC ε) (nhdsWithin (0 : ℝ) (Set.Ioi 0)) - (nhds (S(ρ_AB) + S(ρ_BC))) := - (h_cont_AB.add h_cont_BC).mono_left nhdsWithin_le_nhds - have h_RHS_lim : - Filter.Tendsto (fun ε => f_full ε + f_B ε) (nhdsWithin (0 : ℝ) (Set.Ioi 0)) - (nhds (S(ρ_ABC) + S(ρ_B))) := - (h_cont_full.add h_cont_B).mono_left nhdsWithin_le_nhds - exact le_of_tendsto_of_tendsto h_RHS_lim h_LHS_lim h_within - -end DensityMatrix diff --git a/QuantumSystem/Analysis/Entropy/StrongSubadditivity.lean b/QuantumSystem/Analysis/Entropy/StrongSubadditivity.lean new file mode 100644 index 0000000..4e3358b --- /dev/null +++ b/QuantumSystem/Analysis/Entropy/StrongSubadditivity.lean @@ -0,0 +1,303 @@ +module + +public import QuantumSystem.Channel +public import QuantumSystem.Analysis.Entropy.MutualInformation + +/-! +# Strong subadditivity of the von Neumann entropy + +This file proves strong subadditivity (SSA) directly on plain product index types +`A × B × C`, with marginals taken by the positional partial traces `Matrix.traceLeft` / +`Matrix.traceRight`. It is representation-free — no net structure — the proof is the bare +finite-dimensional quantum-information argument + +1. the mutual-information identity `Matrix.relativeEntropy_kronecker_marginals` + (applied to the `(A : B×C)` and `(A : B)` bipartitions), and +2. the data-processing inequality `Matrix.relativeEntropy_channel_le` for the + trace-out-`C` channel `Matrix.QuantumChannel.traceOutC`. + +The AQFT companion — the same inequality stated over a local net with nested regions, using the +split property `LocalNet.SplitProperty` (`Algebra/LocalNet/SplitProperty.lean`) — is the planned +`LocalNet.SplitProperty.vonNeumannEntropy_SSA` (`Analysis/Entropy/SplitSSA.lean`, not yet +formalised), which will transport this result to the net. + +## Main results + +* `DensityMatrix.vonNeumannEntropy_SSA_posDef` — SSA on `A × B × C` for a positive + definite density matrix. +* `DensityMatrix.vonNeumannEntropy_SSA` — SSA on `A × B × C` for an arbitrary density + matrix (via regularisation). +-/ + +@[expose] public section + +namespace Matrix + +open scoped Kronecker MatrixOrder ComplexOrder QuantumInfo + +/-! ### Bridges between `Matrix.partialTrace` and `Matrix.traceLeft`/`traceRight` -/ + +/-- `tr₂` (trace out the second factor) is `Matrix.traceRight`. -/ +@[simp] lemma partialTrace_refl_eq_traceRight {X Y : Type*} [Fintype Y] (M : Matrix (X × Y) (X × Y) ℂ) : + Matrix.partialTrace (Equiv.refl (X × Y)) M = Matrix.traceRight M := by + ext i j; rw [Matrix.partialTrace_refl_apply, traceRight_apply] + +/-- `tr₁` (trace out the first factor) is `Matrix.traceLeft`. -/ +@[simp] lemma partialTrace_prodComm_eq_traceLeft {X Y : Type*} [Fintype X] + (M : Matrix (X × Y) (X × Y) ℂ) : + Matrix.partialTrace (Equiv.prodComm X Y) M = Matrix.traceLeft M := by + ext i j; rw [Matrix.partialTrace_prodComm_apply, traceLeft_apply] + +/-! ### Associativity of iterated partial traces over `prodAssoc` -/ + +variable {A B C : Type*} [Fintype A] [Fintype B] [Fintype C] + +omit [Fintype A] in +/-- Tracing out `C` (after the associativity reindex to `(A×B)×C`) then `B` equals tracing out +`B × C` directly. -/ +lemma traceRight_traceRight_submatrix_prodAssoc (M : Matrix (A × B × C) (A × B × C) ℂ) : + Matrix.traceRight (Matrix.traceRight + (M.submatrix (Equiv.prodAssoc A B C) (Equiv.prodAssoc A B C))) + = Matrix.traceRight M := by + ext a a' + simp only [traceRight_apply, Matrix.submatrix_apply, Equiv.prodAssoc_apply] + rw [Fintype.sum_prod_type] + +omit [Fintype B] in +/-- Tracing out `C` (after the reindex) then `A` equals tracing out `C` of (trace out `A`). -/ +lemma traceLeft_traceRight_submatrix_prodAssoc (M : Matrix (A × B × C) (A × B × C) ℂ) : + Matrix.traceLeft (Matrix.traceRight + (M.submatrix (Equiv.prodAssoc A B C) (Equiv.prodAssoc A B C))) + = Matrix.traceRight (Matrix.traceLeft M) := by + ext b b' + simp only [traceLeft_apply, traceRight_apply, Matrix.submatrix_apply, Equiv.prodAssoc_apply] + rw [Finset.sum_comm] + +omit [Fintype A] [Fintype B] in +/-- Tracing out `C` of `(M_A ⊗ M_BC)` reassociated to `(A×B)×C` factors through `M_BC`. -/ +lemma traceRight_submatrix_prodAssoc_kronecker (M_A : Matrix A A ℂ) + (M_BC : Matrix (B × C) (B × C) ℂ) : + Matrix.traceRight ((M_A ⊗ₖ M_BC).submatrix (Equiv.prodAssoc A B C) (Equiv.prodAssoc A B C)) + = M_A ⊗ₖ Matrix.traceRight M_BC := by + ext p q + simp only [traceRight_apply, Matrix.submatrix_apply, Equiv.prodAssoc_apply, + Matrix.kroneckerMap_apply] + rw [Finset.mul_sum] + +end Matrix + +namespace DensityMatrix + +open Matrix +open scoped Kronecker MatrixOrder ComplexOrder Matrix.QuantumInfo + +/-! ### Bundled partial-trace marginals -/ + +/-- Partial trace over the right factor of a density matrix: `DensityMatrix (X × Y) → DensityMatrix X`. -/ +noncomputable def ptRight {X Y : Type*} [Fintype X] [DecidableEq X] [Fintype Y] [DecidableEq Y] + (ρ : DensityMatrix (X × Y)) : DensityMatrix X where + toMatrix := Matrix.traceRight ρ.toMatrix + posSemidef := Matrix.traceRight_posSemidef ρ.posSemidef + trace_eq_one := by rw [Matrix.trace_traceRight]; exact ρ.trace_eq_one + +@[simp] lemma ptRight_toMatrix {X Y : Type*} [Fintype X] [DecidableEq X] [Fintype Y] [DecidableEq Y] + (ρ : DensityMatrix (X × Y)) : (ρ.ptRight).toMatrix = Matrix.traceRight ρ.toMatrix := rfl + +/-- Partial trace over the left factor of a density matrix: `DensityMatrix (X × Y) → DensityMatrix Y`. -/ +noncomputable def ptLeft {X Y : Type*} [Fintype X] [DecidableEq X] [Fintype Y] [DecidableEq Y] + (ρ : DensityMatrix (X × Y)) : DensityMatrix Y where + toMatrix := Matrix.traceLeft ρ.toMatrix + posSemidef := Matrix.traceLeft_posSemidef ρ.posSemidef + trace_eq_one := by rw [Matrix.trace_traceLeft]; exact ρ.trace_eq_one + +@[simp] lemma ptLeft_toMatrix {X Y : Type*} [Fintype X] [DecidableEq X] [Fintype Y] [DecidableEq Y] + (ρ : DensityMatrix (X × Y)) : (ρ.ptLeft).toMatrix = Matrix.traceLeft ρ.toMatrix := rfl + +/-! ### Strong subadditivity -/ + +variable {A B C : Type*} [Fintype A] [DecidableEq A] [Fintype B] [DecidableEq B] + [Fintype C] [DecidableEq C] + +/-- **Strong subadditivity (positive definite case).** For a positive definite +density matrix `ρ` on `A × B × C`, with all four marginals positive definite, +`S(ρ) + S(ρ_B) ≤ S(ρ_AB) + S(ρ_BC)`. -/ +private lemma vonNeumannEntropy_SSA_posDef + (ρ_ABC : DensityMatrix (A × B × C)) + (ρ_A : DensityMatrix A) (ρ_AB : DensityMatrix (A × B)) + (ρ_BC : DensityMatrix (B × C)) (ρ_B : DensityMatrix B) + (h_A : ρ_A = ρ_ABC.ptRight) + (h_AB : ρ_AB = (ρ_ABC.mapEquiv (Equiv.prodAssoc A B C)).ptRight) + (h_BC : ρ_BC = ρ_ABC.ptLeft) (h_B : ρ_B = ρ_ABC.ptLeft.ptRight) + (hA : ρ_A.toMatrix.PosDef) (hBC : ρ_BC.toMatrix.PosDef) (hB : ρ_B.toMatrix.PosDef) : + S(ρ_ABC) + S(ρ_B) ≤ S(ρ_AB) + S(ρ_BC) := by + subst h_A h_AB h_BC h_B + classical + set ρ_A := ρ_ABC.ptRight with hρ_A + set ρ_BC := ρ_ABC.ptLeft with hρ_BC + set ρ_AB := (ρ_ABC.mapEquiv (Equiv.prodAssoc A B C)).ptRight with hρ_AB + set ρ_B := ρ_ABC.ptLeft.ptRight with hρ_B + -- Mutual-information identity for the `(A : B×C)` split of `ρ_ABC`. + have h_tr2 : tr₂(ρ_ABC.toMatrix) = ρ_A.toMatrix := by + rw [hρ_A, ptRight_toMatrix]; exact partialTrace_refl_eq_traceRight ρ_ABC.toMatrix + have h_tr1 : tr₁(ρ_ABC.toMatrix) = ρ_BC.toMatrix := by + rw [hρ_BC, ptLeft_toMatrix]; exact partialTrace_prodComm_eq_traceLeft ρ_ABC.toMatrix + have h_id1 : D(ρ_ABC ∥ ρ_A ⊗ ρ_BC) = -S(ρ_ABC) + S(ρ_A) + S(ρ_BC) := + Matrix.relativeEntropy_kronecker_marginals ρ_ABC ρ_A hA ρ_BC hBC h_tr2 h_tr1 + -- Mutual-information identity for the `(A : B)` split of `ρ_AB`. + have h_tr2' : tr₂(ρ_AB.toMatrix) = ρ_A.toMatrix := by + rw [hρ_AB, hρ_A, ptRight_toMatrix, ptRight_toMatrix, DensityMatrix.mapEquiv_toMatrix, + partialTrace_refl_eq_traceRight] + exact traceRight_traceRight_submatrix_prodAssoc ρ_ABC.toMatrix + have h_tr1' : tr₁(ρ_AB.toMatrix) = ρ_B.toMatrix := by + rw [hρ_AB, hρ_B, ptRight_toMatrix, ptRight_toMatrix, ptLeft_toMatrix, + DensityMatrix.mapEquiv_toMatrix, partialTrace_prodComm_eq_traceLeft] + exact traceLeft_traceRight_submatrix_prodAssoc ρ_ABC.toMatrix + have h_id2 : D(ρ_AB ∥ ρ_A ⊗ ρ_B) = -S(ρ_AB) + S(ρ_A) + S(ρ_B) := + Matrix.relativeEntropy_kronecker_marginals ρ_AB ρ_A hA ρ_B hB h_tr2' h_tr1' + -- Data-processing inequality for the trace-out-`C` channel. + set Φ := Matrix.QuantumChannel.traceOutC (A := A) (B := B) (C := C) with hΦ + have h_Φρ_ABC : Φ ρ_ABC = ρ_AB := by + apply DensityMatrix.ext + change Φ.val ρ_ABC.toMatrix = ρ_AB.toMatrix + rw [hΦ, Matrix.QuantumChannel.traceOutC_val_apply, hρ_AB, ptRight_toMatrix, + DensityMatrix.mapEquiv_toMatrix, Matrix.reindex_apply, Equiv.symm_symm] + have h_Φσ : Φ (ρ_A ⊗ ρ_BC) = ρ_A ⊗ ρ_B := by + apply DensityMatrix.ext + change Φ.val (ρ_A ⊗ ρ_BC).toMatrix = (ρ_A ⊗ ρ_B).toMatrix + rw [hΦ, Matrix.QuantumChannel.traceOutC_val_apply, DensityMatrix.kronecker_toMatrix, + DensityMatrix.kronecker_toMatrix, Matrix.reindex_apply, Equiv.symm_symm, + Matrix.traceRight_submatrix_prodAssoc_kronecker] + simp only [hρ_B, hρ_BC, ptRight_toMatrix, ptLeft_toMatrix] + have h_dpi : D(Φ ρ_ABC ∥ Φ (ρ_A ⊗ ρ_BC)) ≤ D(ρ_ABC ∥ ρ_A ⊗ ρ_BC) := + Matrix.relativeEntropy_channel_le Φ ρ_ABC (ρ_A ⊗ ρ_BC) + rw [h_Φρ_ABC, h_Φσ, h_id2, h_id1] at h_dpi + have h_real : -S(ρ_AB) + S(ρ_A) + S(ρ_B) ≤ -S(ρ_ABC) + S(ρ_A) + S(ρ_BC) := by exact_mod_cast h_dpi + linarith + +/-! ### Regularisation: commutation of marginals with `regularize` -/ + +/-- The right partial trace of the maximally mixed state is maximally mixed. -/ +lemma traceRight_maximallyMixed {X Y : Type*} [Fintype X] [DecidableEq X] [Nonempty X] + [Fintype Y] [DecidableEq Y] [Nonempty Y] : + Matrix.traceRight (DensityMatrix.maximallyMixed (n := X × Y)).toMatrix + = (DensityMatrix.maximallyMixed (n := X)).toMatrix := by + rw [DensityMatrix.maximallyMixed_toMatrix, DensityMatrix.maximallyMixed_toMatrix, + ← Matrix.partialTraceRightₗ_apply, map_smul, Matrix.partialTraceRightₗ_apply, + Matrix.traceRight_one, smul_smul] + congr 1 + rw [Fintype.card_prod] + have hX : (Fintype.card X : ℂ) ≠ 0 := Nat.cast_ne_zero.mpr Fintype.card_ne_zero + have hY : (Fintype.card Y : ℂ) ≠ 0 := Nat.cast_ne_zero.mpr Fintype.card_ne_zero + push_cast + field_simp + +/-- The left partial trace of the maximally mixed state is maximally mixed. -/ +lemma traceLeft_maximallyMixed {X Y : Type*} [Fintype X] [DecidableEq X] [Nonempty X] + [Fintype Y] [DecidableEq Y] [Nonempty Y] : + Matrix.traceLeft (DensityMatrix.maximallyMixed (n := X × Y)).toMatrix + = (DensityMatrix.maximallyMixed (n := Y)).toMatrix := by + rw [DensityMatrix.maximallyMixed_toMatrix, DensityMatrix.maximallyMixed_toMatrix, + ← Matrix.partialTraceLeftₗ_apply, map_smul, Matrix.partialTraceLeftₗ_apply, + Matrix.traceLeft_one, smul_smul] + congr 1 + rw [Fintype.card_prod] + have hX : (Fintype.card X : ℂ) ≠ 0 := Nat.cast_ne_zero.mpr Fintype.card_ne_zero + have hY : (Fintype.card Y : ℂ) ≠ 0 := Nat.cast_ne_zero.mpr Fintype.card_ne_zero + push_cast + field_simp + +variable {A B C : Type*} [Fintype A] [DecidableEq A] [Fintype B] [DecidableEq B] + [Fintype C] [DecidableEq C] + +/-- `ptRight` commutes with `regularize`. -/ +lemma ptRight_regularize {X Y : Type*} [Fintype X] [DecidableEq X] [Nonempty X] + [Fintype Y] [DecidableEq Y] [Nonempty Y] (ρ : DensityMatrix (X × Y)) {ε : ℝ} + (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + (DensityMatrix.regularize ρ hε hε').ptRight = DensityMatrix.regularize ρ.ptRight hε hε' := by + apply DensityMatrix.ext + rw [ptRight_toMatrix, DensityMatrix.regularize_toMatrix, DensityMatrix.regularize_toMatrix, + ptRight_toMatrix, ← Matrix.partialTraceRightₗ_apply, map_add, map_smul, map_smul, + Matrix.partialTraceRightₗ_apply, Matrix.partialTraceRightₗ_apply, traceRight_maximallyMixed] + +/-- `ptLeft` commutes with `regularize`. -/ +lemma ptLeft_regularize {X Y : Type*} [Fintype X] [DecidableEq X] [Nonempty X] + [Fintype Y] [DecidableEq Y] [Nonempty Y] (ρ : DensityMatrix (X × Y)) {ε : ℝ} + (hε : 0 ≤ ε) (hε' : ε ≤ 1) : + (DensityMatrix.regularize ρ hε hε').ptLeft = DensityMatrix.regularize ρ.ptLeft hε hε' := by + apply DensityMatrix.ext + rw [ptLeft_toMatrix, DensityMatrix.regularize_toMatrix, DensityMatrix.regularize_toMatrix, + ptLeft_toMatrix, ← Matrix.partialTraceLeftₗ_apply, map_add, map_smul, map_smul, + Matrix.partialTraceLeftₗ_apply, Matrix.partialTraceLeftₗ_apply, traceLeft_maximallyMixed] + +/-- **Strong subadditivity.** For any density matrix `ρ` on `A × B × C`, +`S(ρ) + S(ρ_B) ≤ S(ρ_AB) + S(ρ_BC)`. Obtained from the positive-definite case by regularisation +and a limit. -/ +theorem vonNeumannEntropy_SSA [Nonempty A] [Nonempty B] [Nonempty C] + (ρ_ABC : DensityMatrix (A × B × C)) + (ρ_AB : DensityMatrix (A × B)) (ρ_BC : DensityMatrix (B × C)) (ρ_B : DensityMatrix B) + (h_AB : ρ_AB = (ρ_ABC.mapEquiv (Equiv.prodAssoc A B C)).ptRight) + (h_BC : ρ_BC = ρ_ABC.ptLeft) (h_B : ρ_B = ρ_ABC.ptLeft.ptRight) : + S(ρ_ABC) + S(ρ_B) ≤ S(ρ_AB) + S(ρ_BC) := by + subst h_AB h_BC h_B + set ρ_AB := (ρ_ABC.mapEquiv (Equiv.prodAssoc A B C)).ptRight with hρ_AB + set ρ_BC := ρ_ABC.ptLeft with hρ_BC + set ρ_B := ρ_ABC.ptLeft.ptRight with hρ_B + let f_full : ℝ → ℝ := fun ε => ∑ i, Real.negMulLog + ((1 - ε) * ρ_ABC.isHermitian.eigenvalues i + ε / Fintype.card (A × B × C)) + let f_B : ℝ → ℝ := fun ε => ∑ i, Real.negMulLog + ((1 - ε) * ρ_B.isHermitian.eigenvalues i + ε / Fintype.card B) + let f_AB : ℝ → ℝ := fun ε => ∑ i, Real.negMulLog + ((1 - ε) * ρ_AB.isHermitian.eigenvalues i + ε / Fintype.card (A × B)) + let f_BC : ℝ → ℝ := fun ε => ∑ i, Real.negMulLog + ((1 - ε) * ρ_BC.isHermitian.eigenvalues i + ε / Fintype.card (B × C)) + have h_ineq_pos : ∀ ε : ℝ, 0 < ε → ε ≤ 1 → f_full ε + f_B ε ≤ f_AB ε + f_BC ε := by + intro ε hε_pos hε_le + have hA_pos : ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).ptRight).toMatrix.PosDef := by + rw [ptRight_regularize]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le + have hBC_pos : ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).ptLeft).toMatrix.PosDef := by + rw [ptLeft_regularize]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le + have hB_pos : + ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).ptLeft.ptRight).toMatrix.PosDef := by + rw [ptLeft_regularize, ptRight_regularize]; exact DensityMatrix.regularize_posDef _ hε_pos hε_le + have h_ssa := vonNeumannEntropy_SSA_posDef + (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le) _ _ _ _ rfl rfl rfl rfl + hA_pos hBC_pos hB_pos + rw [show (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).ptLeft.ptRight + = DensityMatrix.regularize ρ_B hε_pos.le hε_le from by + rw [ptLeft_regularize, ptRight_regularize, hρ_B], + show ((DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).mapEquiv (Equiv.prodAssoc A B C)).ptRight + = DensityMatrix.regularize ρ_AB hε_pos.le hε_le from by + rw [← DensityMatrix.regularize_mapEquiv, ptRight_regularize, hρ_AB], + show (DensityMatrix.regularize ρ_ABC hε_pos.le hε_le).ptLeft + = DensityMatrix.regularize ρ_BC hε_pos.le hε_le from by + rw [ptLeft_regularize, hρ_BC]] at h_ssa + rw [Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_ABC hε_pos.le hε_le, + Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_B hε_pos.le hε_le, + Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_AB hε_pos.le hε_le, + Matrix.vonNeumannEntropy_regularize_eq_negMulLog_sum ρ_BC hε_pos.le hε_le] at h_ssa + exact h_ssa + have h_cont_full : Filter.Tendsto f_full (nhds 0) (nhds S(ρ_ABC)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_ABC + have h_cont_B : Filter.Tendsto f_B (nhds 0) (nhds S(ρ_B)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_B + have h_cont_AB : Filter.Tendsto f_AB (nhds 0) (nhds S(ρ_AB)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_AB + have h_cont_BC : Filter.Tendsto f_BC (nhds 0) (nhds S(ρ_BC)) := + Matrix.tendsto_negMulLog_regularize_sum_zero ρ_BC + have h_within : ∀ᶠ ε in nhdsWithin (0 : ℝ) (Set.Ioi 0), + f_full ε + f_B ε ≤ f_AB ε + f_BC ε := by + rw [eventually_nhdsWithin_iff] + have h_le_one : ∀ᶠ ε in nhds (0 : ℝ), ε ≤ 1 := + Filter.eventually_of_mem (IsOpen.mem_nhds isOpen_Iio (by norm_num : (0 : ℝ) < 1)) + (fun ε hε => le_of_lt hε) + filter_upwards [h_le_one] with ε hε_le_one hε_pos + exact h_ineq_pos ε hε_pos hε_le_one + have h_LHS_lim : Filter.Tendsto (fun ε => f_AB ε + f_BC ε) (nhdsWithin (0 : ℝ) (Set.Ioi 0)) + (nhds (S(ρ_AB) + S(ρ_BC))) := + (h_cont_AB.add h_cont_BC).mono_left nhdsWithin_le_nhds + have h_RHS_lim : Filter.Tendsto (fun ε => f_full ε + f_B ε) (nhdsWithin (0 : ℝ) (Set.Ioi 0)) + (nhds (S(ρ_ABC) + S(ρ_B))) := + (h_cont_full.add h_cont_B).mono_left nhdsWithin_le_nhds + exact le_of_tendsto_of_tendsto h_RHS_lim h_LHS_lim h_within + +end DensityMatrix diff --git a/QuantumSystem/Analysis/Entropy/VonNeumannEntropy.lean b/QuantumSystem/Analysis/Entropy/VonNeumannEntropy.lean index fdbdd62..18766ee 100644 --- a/QuantumSystem/Analysis/Entropy/VonNeumannEntropy.lean +++ b/QuantumSystem/Analysis/Entropy/VonNeumannEntropy.lean @@ -12,6 +12,9 @@ This file contains definitions and core properties of von Neumann entropy. ## Main Results +* `vonNeumannEntropy_eq_negMulLog_sum`: eigenvalue-sum form `S(ρ) = ∑ᵢ negMulLog λᵢ`. +* `vonNeumannEntropy_nonneg`: Von Neumann entropy is non-negative, `0 ≤ S(ρ)`. +* `vonNeumannEntropy_le_log_dim`: Von Neumann entropy is bounded by `log (dim)`. * `vonNeumannEntropy_concave`: Von Neumann entropy is concave. -/ @@ -23,19 +26,6 @@ open scoped MatrixOrder ComplexOrder QuantumInfo variable {n : Type*} [Fintype n] [DecidableEq n] -/-- The entropy function η(x) = -x log x, extended by continuity to η(0) = 0. -This is concave on [0, ∞). -/ -noncomputable def entropyFun (x : ℝ) : ℝ := - if x ≤ 0 then 0 else -x * Real.log x - -theorem entropyFun_nonneg {x : ℝ} (hx : 0 ≤ x) (hx1 : x ≤ 1) : 0 ≤ entropyFun x := by - unfold entropyFun - split_ifs with hle - · exact le_refl 0 - · push Not at hle - have hlog : Real.log x ≤ 0 := Real.log_nonpos hx hx1 - nlinarith [hle, hlog] - /-- Von Neumann entropy of a density matrix: S(ρ) = −Tr (ρ log ρ). Since ρ log ρ is Hermitian (see `DensityMatrix.mul_log_isHermitian`), its trace is real, so `.re` is lossless (see `vonNeumannEntropy_ofReal`). -/ @@ -48,80 +38,48 @@ end QuantumInfo /-- Casting `S(ρ)` back to ℂ recovers −Tr(ρ log ρ) exactly, confirming the trace is real. -/ @[simp] -theorem vonNeumannEntropy_ofReal (ρ : DensityMatrix n) : +lemma vonNeumannEntropy_ofReal (ρ : DensityMatrix n) : (S(ρ) : ℂ) = -(Tr (ρ * log ρ)) := by unfold vonNeumannEntropy rw [Complex.ofReal_neg] congr 1 exact ρ.mul_log_isHermitian.trace_ofReal_re -/-- Von Neumann entropy equals the eigenvalue sum S(ρ) = ∑ᵢ (−λᵢ log λᵢ). -/ -theorem vonNeumannEntropy_eq_sum (ρ : DensityMatrix n) : - S(ρ) = ∑ i, entropyFun (ρ.isHermitian.eigenvalues i) := by - unfold vonNeumannEntropy DensityMatrix.log matrixLog - change -(Tr (ρ.toMatrix * matrixFunction _ ρ.toMatrix ρ.isHermitian)).re = _ - rw [trace_mul_matrixFunction, Complex.re_sum] +/-- `vonNeumannEntropy ρ = ∑ᵢ Real.negMulLog (ρ.eigenvalues i)`, the eigenvalue-sum form +in terms of Mathlib's `Real.negMulLog`. -/ +theorem vonNeumannEntropy_eq_negMulLog_sum (ρ : DensityMatrix n) : + vonNeumannEntropy ρ = ∑ i, Real.negMulLog (ρ.isHermitian.eigenvalues i) := by + unfold vonNeumannEntropy DensityMatrix.log + change -(Tr (ρ.toMatrix * cfc Real.log ρ.toMatrix)).re = _ + rw [trace_mul_cfc ρ.isHermitian, Complex.re_sum] simp_rw [← Complex.ofReal_mul, Complex.ofReal_re, ← Finset.sum_neg_distrib] congr 1 ext i - unfold entropyFun - split_ifs with hle - · have h0 := le_antisymm hle (ρ.eigenvalues_nonneg i) - simp [h0] - · ring - -/-- For `x ≥ 0`, `entropyFun x = Real.negMulLog x`. -/ -theorem entropyFun_eq_negMulLog_of_nonneg {x : ℝ} (hx : 0 ≤ x) : - entropyFun x = Real.negMulLog x := by - unfold entropyFun Real.negMulLog - split_ifs with h - · have h0 : x = 0 := le_antisymm h hx - rw [h0]; ring - · rfl - -/-- `vonNeumannEntropy ρ = ∑ᵢ Real.negMulLog (ρ.eigenvalues i)`. - -Since `ρ` is PSD, its eigenvalues are non-negative, so `entropyFun = Real.negMulLog` -on the sum range. -/ -theorem vonNeumannEntropy_eq_negMulLog_sum (ρ : DensityMatrix n) : - vonNeumannEntropy ρ = ∑ i, Real.negMulLog (ρ.isHermitian.eigenvalues i) := by - rw [vonNeumannEntropy_eq_sum] - refine Finset.sum_congr rfl fun i _ => ?_ - exact entropyFun_eq_negMulLog_of_nonneg (ρ.eigenvalues_nonneg i) - -/-- `vonNeumannEntropy` expressed via the spectral matrix function `matrixFunction` - applied to `Real.negMulLog`. This bridges to Mathlib's continuous functional - calculus, enabling continuity arguments. -/ -theorem vonNeumannEntropy_eq_matrixFunction_re (ρ : DensityMatrix n) : + simp only [Real.negMulLog] + ring + +/-- `vonNeumannEntropy` expressed via Mathlib's continuous functional calculus + `cfc` applied to `Real.negMulLog`, enabling continuity arguments. -/ +lemma vonNeumannEntropy_eq_cfc_re (ρ : DensityMatrix n) : vonNeumannEntropy ρ = - (Tr (matrixFunction (fun x : ℝ => (Real.negMulLog x : ℂ)) - ρ.toMatrix ρ.isHermitian)).re := by - rw [matrixFunction_trace, vonNeumannEntropy_eq_negMulLog_sum] + (Tr (cfc Real.negMulLog ρ.toMatrix)).re := by + rw [trace_cfc ρ.isHermitian, vonNeumannEntropy_eq_negMulLog_sum] rw [Complex.re_sum] simp_rw [Complex.ofReal_re] /-- Von Neumann entropy is non-negative. -/ theorem vonNeumannEntropy_nonneg (ρ : DensityMatrix n) : 0 ≤ S(ρ) := by - rw [vonNeumannEntropy_eq_sum] + rw [vonNeumannEntropy_eq_negMulLog_sum] apply Finset.sum_nonneg intro i _ - exact entropyFun_nonneg (ρ.eigenvalues_nonneg i) (ρ.eigenvalue_le_one i) + exact Real.negMulLog_nonneg (ρ.eigenvalues_nonneg i) (ρ.eigenvalue_le_one i) /-- Von Neumann entropy is at most log(dim), achieved for the maximally mixed state. This follows from Jensen's inequality applied to the concave function -x log x. -/ theorem vonNeumannEntropy_le_log_dim [Nonempty n] (ρ : DensityMatrix n) : S(ρ) ≤ Real.log (Fintype.card n) := by - rw [vonNeumannEntropy_eq_sum] - have heq : ∀ i, entropyFun (ρ.isHermitian.eigenvalues i) = - Real.negMulLog (ρ.isHermitian.eigenvalues i) := by - intro i - unfold entropyFun Real.negMulLog - split_ifs with hle - · have h0 : ρ.isHermitian.eigenvalues i = 0 := le_antisymm hle (ρ.eigenvalues_nonneg i) - simp [h0] - · rfl - simp_rw [heq] + rw [vonNeumannEntropy_eq_negMulLog_sum] have hlog_inv : Real.log (1 / Fintype.card n) = -Real.log (Fintype.card n) := by rw [one_div, Real.log_inv] have hunif_pos : ∀ i : n, 0 < 1 / (Fintype.card n : ℝ) := fun _ => by positivity @@ -168,7 +126,8 @@ This follows from the spectral theorem: ρ^s = U diag(λᵢ^s) U†, and trace cyclicity Tr (U D U†) = Tr (D) = ∑ Dᵢᵢ. -/ lemma trace_rpow_eq_sum_pow (ρ : Matrix n n ℂ) (hρ : ρ.PosSemidef) (s : ℝ) : (Tr (ρ ^ s)).re = ∑ i, hρ.1.eigenvalues i ^ s := by - rw [← matrixFunction_rpow_eq hρ s, matrixFunction_trace] + have h0 : (0 : Matrix n n ℂ) ≤ ρ := by rw [Matrix.le_iff, sub_zero]; exact hρ + rw [CFC.rpow_eq_cfc_real (a := ρ) (ha := h0), trace_cfc hρ.1] simp [Complex.ofReal_re] /-- HasDerivAt of eigenvalue rpow sum. @@ -211,12 +170,18 @@ lemma trace_rpow_concave (A B : Matrix n n ℂ) (hA : A.PosSemidef) (hB : B.PosS (hA.real_smul hp).add (hB.real_smul (by linarith)) have hlowner := rpow_isLownerConcave hs0 hs1 n A B hA hB p hp hp1 hpsd_mix.1 simp only [] at hlowner - have hfunc_eq : (fun x : ℝ => ((-x ^ s : ℝ) : ℂ)) = (fun x : ℝ => -(((x ^ s : ℝ) : ℂ))) := by - ext x - exact Complex.ofReal_neg _ - rw [hfunc_eq] at hlowner - rw [matrixFunction_neg hA.1, matrixFunction_neg hB.1, matrixFunction_neg hpsd_mix.1, - matrixFunction_rpow_eq hA, matrixFunction_rpow_eq hB, matrixFunction_rpow_eq hpsd_mix] at hlowner + have hA0 : (0 : Matrix n n ℂ) ≤ A := by rw [Matrix.le_iff, sub_zero]; exact hA + have hB0 : (0 : Matrix n n ℂ) ≤ B := by rw [Matrix.le_iff, sub_zero]; exact hB + have hM0 : (0 : Matrix n n ℂ) ≤ p • A + (1 - p) • B := by + rw [Matrix.le_iff, sub_zero]; exact hpsd_mix + have eA : cfc (fun x : ℝ => -(x ^ s)) A = -(A ^ s) := by + rw [cfc_neg, ← CFC.rpow_eq_cfc_real (a := A) (ha := hA0)] + have eB : cfc (fun x : ℝ => -(x ^ s)) B = -(B ^ s) := by + rw [cfc_neg, ← CFC.rpow_eq_cfc_real (a := B) (ha := hB0)] + have eM : cfc (fun x : ℝ => -(x ^ s)) (p • A + (1 - p) • B) = + -((p • A + (1 - p) • B) ^ s) := by + rw [cfc_neg, ← CFC.rpow_eq_cfc_real (a := p • A + (1 - p) • B) (ha := hM0)] + rw [eA, eB, eM] at hlowner have hlowner' : p • A ^ s + (1 - p) • B ^ s ≤ (p • A + (1 - p) • B) ^ s := by have heq : p • -A ^ s + (1 - p) • -B ^ s = -(p • A ^ s + (1 - p) • B ^ s) := by have h1 : p • -A ^ s = -(p • A ^ s) := smul_neg p (A ^ s) @@ -304,34 +269,25 @@ theorem vonNeumannEntropy_concave (ρ₁ ρ₂ : DensityMatrix n) (p : ℝ) (hp deriv_nonpos_of_forall_lt_min g _ 1 (1 / 2) (by norm_num) hderiv_g hmin have hmix_eq : ∑ i, ρ_mix.isHermitian.eigenvalues i * Real.log (ρ_mix.isHermitian.eigenvalues i) = -vonNeumannEntropy ρ_mix := by - rw [vonNeumannEntropy_eq_sum, ← Finset.sum_neg_distrib] + rw [vonNeumannEntropy_eq_negMulLog_sum, ← Finset.sum_neg_distrib] congr 1 ext i - unfold entropyFun - split_ifs with h - · simp [le_antisymm h (ρ_mix.eigenvalues_nonneg i)] - · push Not at h - ring + simp only [Real.negMulLog] + ring have h₁_eq : ∑ i, ρ₁.isHermitian.eigenvalues i * Real.log (ρ₁.isHermitian.eigenvalues i) = -vonNeumannEntropy ρ₁ := by - rw [vonNeumannEntropy_eq_sum, ← Finset.sum_neg_distrib] + rw [vonNeumannEntropy_eq_negMulLog_sum, ← Finset.sum_neg_distrib] congr 1 ext i - unfold entropyFun - split_ifs with h - · simp [le_antisymm h (ρ₁.eigenvalues_nonneg i)] - · push Not at h - ring + simp only [Real.negMulLog] + ring have h₂_eq : ∑ i, ρ₂.isHermitian.eigenvalues i * Real.log (ρ₂.isHermitian.eigenvalues i) = -vonNeumannEntropy ρ₂ := by - rw [vonNeumannEntropy_eq_sum, ← Finset.sum_neg_distrib] + rw [vonNeumannEntropy_eq_negMulLog_sum, ← Finset.sum_neg_distrib] congr 1 ext i - unfold entropyFun - split_ifs with h - · simp [le_antisymm h (ρ₂.eigenvalues_nonneg i)] - · push Not at h - ring + simp only [Real.negMulLog] + ring rw [hmix_eq, h₁_eq, h₂_eq] at hderiv_g_nonpos linarith @@ -355,27 +311,27 @@ variable {m : Type*} [Fintype m] [DecidableEq m] /-- **Von Neumann entropy is invariant under trace-preserving `*-`algebra equivalence** (PosDef case). -/ -theorem vonNeumannEntropy_map_starAlgEquiv_posDef +lemma vonNeumannEntropy_map_starAlgEquiv_posDef (ρ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) (hφ : ∀ A, (φ A).trace = A.trace) : S(ρ.map φ hφ) = S(ρ) := by unfold vonNeumannEntropy - have h_log_eq : matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian = - φ (matrixLog ρ.toMatrix ρ.isHermitian) := by - change matrixLog (φ ρ.toMatrix) _ = _ - exact matrixLog_map_starAlgEquiv hρ φ + have h_log_eq : cfc Real.log (ρ.map φ hφ).toMatrix = + φ (cfc Real.log ρ.toMatrix) := by + change cfc Real.log (φ ρ.toMatrix) = _ + exact cfc_log_map_starAlgEquiv hρ φ have h_tr : Tr ((ρ.map φ hφ).toMatrix * - matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian) = - Tr (ρ.toMatrix * matrixLog ρ.toMatrix ρ.isHermitian) := by + cfc Real.log (ρ.map φ hφ).toMatrix) = + Tr (ρ.toMatrix * cfc Real.log ρ.toMatrix) := by rw [h_log_eq, DensityMatrix.map_toMatrix, ← map_mul, hφ] change -(Tr ((ρ.map φ hφ).toMatrix * - matrixLog (ρ.map φ hφ).toMatrix (ρ.map φ hφ).isHermitian)).re = - -(Tr (ρ.toMatrix * matrixLog ρ.toMatrix ρ.isHermitian)).re + cfc Real.log (ρ.map φ hφ).toMatrix)).re = + -(Tr (ρ.toMatrix * cfc Real.log ρ.toMatrix)).re rw [h_tr] /-- Specialisation of `vonNeumannEntropy_map_starAlgEquiv_posDef` to reindexing. -/ -theorem vonNeumannEntropy_mapEquiv_posDef +lemma vonNeumannEntropy_mapEquiv_posDef (ρ : DensityMatrix m) (hρ : ρ.toMatrix.PosDef) (e : n ≃ m) : S(ρ.mapEquiv e) = S(ρ) := vonNeumannEntropy_map_starAlgEquiv_posDef ρ hρ _ _ @@ -395,12 +351,11 @@ variable {m : Type*} [Fintype m] [DecidableEq m] /-- **Eigenvalue formula for the entropy of a regularization**: `S(regularize ρ ε) = ∑ᵢ Real.negMulLog ((1-ε) λᵢ + ε/d)` where `λᵢ = ρ.eigenvalues i`. -/ -theorem vonNeumannEntropy_regularize_eq_negMulLog_sum [Nonempty n] +lemma vonNeumannEntropy_regularize_eq_negMulLog_sum [Nonempty n] (ρ : DensityMatrix n) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : vonNeumannEntropy (DensityMatrix.regularize ρ hε hε') = ∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + ε / Fintype.card n) := by - rw [vonNeumannEntropy_eq_matrixFunction_re] - rw [matrixFunction_eq_cfc] + rw [vonNeumannEntropy_eq_cfc_re] rw [DensityMatrix.regularize_eq_cfc ρ hε hε'] have hρ_sa : IsSelfAdjoint ρ.toMatrix := ρ.isHermitian rw [← cfc_comp (R := ℝ) Real.negMulLog @@ -408,14 +363,13 @@ theorem vonNeumannEntropy_regularize_eq_negMulLog_sum [Nonempty n] hρ_sa Real.continuous_negMulLog.continuousOn (by fun_prop)] rw [show (Real.negMulLog ∘ (fun x : ℝ => (1 - ε) * x + ε / (Fintype.card n : ℝ))) = (fun x : ℝ => Real.negMulLog ((1 - ε) * x + ε / (Fintype.card n : ℝ))) from rfl] - rw [← matrixFunction_eq_cfc ρ.isHermitian - (fun x : ℝ => Real.negMulLog ((1 - ε) * x + ε / Fintype.card n))] - rw [matrixFunction_trace, Complex.re_sum] + rw [trace_cfc ρ.isHermitian + (fun x : ℝ => Real.negMulLog ((1 - ε) * x + ε / Fintype.card n)), Complex.re_sum] simp_rw [Complex.ofReal_re] /-- The eigenvalue-formula function `ε ↦ ∑ᵢ Real.negMulLog ((1-ε) λᵢ + ε/d)` is continuous in `ε ∈ ℝ`. -/ -theorem continuous_negMulLog_regularize_sum (ρ : DensityMatrix n) : +lemma continuous_negMulLog_regularize_sum (ρ : DensityMatrix n) : Continuous (fun ε : ℝ => ∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + ε / Fintype.card n)) := by refine continuous_finset_sum _ fun i _ => ?_ @@ -429,7 +383,7 @@ theorem continuous_negMulLog_regularize_sum (ρ : DensityMatrix n) : This is the key continuity result. Combined with `vonNeumannEntropy_regularize_eq_negMulLog_sum`, it gives `lim_{ε → 0+} S(regularize ρ ε) = S(ρ)`, the foundation for extending PosDef-only theorems to PosSemidef. -/ -theorem tendsto_negMulLog_regularize_sum_zero (ρ : DensityMatrix n) : +lemma tendsto_negMulLog_regularize_sum_zero (ρ : DensityMatrix n) : Filter.Tendsto (fun ε : ℝ => ∑ i, Real.negMulLog ((1 - ε) * ρ.isHermitian.eigenvalues i + ε / Fintype.card n)) @@ -447,7 +401,7 @@ theorem tendsto_negMulLog_regularize_sum_zero (ρ : DensityMatrix n) : For any density matrix `ρ` and equivalence `e`, the entropy is preserved: `S(ρ.mapEquiv e) = S(ρ)`. Proven via regularization + limit. -/ -theorem vonNeumannEntropy_mapEquiv [Nonempty n] [Nonempty m] +lemma vonNeumannEntropy_mapEquiv [Nonempty n] [Nonempty m] (ρ : DensityMatrix m) (e : n ≃ m) : vonNeumannEntropy (DensityMatrix.mapEquiv ρ e) = vonNeumannEntropy ρ := by have h_eq : ∀ ε : ℝ, ∀ (hε_pos : 0 < ε) (hε_le : ε ≤ 1), diff --git a/QuantumSystem/Analysis/Matrix/Effros.lean b/QuantumSystem/Analysis/Matrix/Effros.lean index d7738b4..3e4b070 100644 --- a/QuantumSystem/Analysis/Matrix/Effros.lean +++ b/QuantumSystem/Analysis/Matrix/Effros.lean @@ -11,7 +11,7 @@ This file formalises the Effros (2008) machinery used to prove Lieb's joint conc ## Contents 1. **Compression lemmas** – `compression_pow_eq`, `compression_aeval_eq`, - `eigenvalues_compression_subset`, and `matrixFunction_compression_of_commuting`: + `eigenvalues_compression_subset`, and `cfc_compression_of_commuting`: the map `X ↦ V† X V` (sandwiching) interacts well with polynomial/functional calculus when `V†V = I` and `M` commutes with `VV†`. 2. **Block diagonal** – `compression_of_fromBlocks_cfc` and related CFC lemmas. @@ -44,16 +44,6 @@ noncomputable def rightMul {m : Type*} [Fintype m] (B : Matrix m m ℂ) : Matrix m m ℂ →ₗ[ℂ] Matrix m m ℂ := mulRightLinearMap m ℂ B -/-- `leftMul A` applied to a matrix `X` yields `A * X`. -/ -@[simp] lemma leftMul_apply {m : Type*} [Fintype m] - (A X : Matrix m m ℂ) : leftMul A X = A * X := by - simp [leftMul] - -/-- `rightMul B` applied to a matrix `X` yields `X * B`. -/ -@[simp] lemma rightMul_apply {m : Type*} [Fintype m] - (B X : Matrix m m ℂ) : rightMul B X = X * B := by - simp [rightMul] - /-- Left and right multiplication operators commute as linear maps. -/ lemma leftMul_rightMul_commute {m : Type*} [Fintype m] (A B : Matrix m m ℂ) : @@ -105,7 +95,7 @@ lemma leftMulMatrix_rightMulMatrix_commute {m : Type*} [Fintype m] [DecidableEq simpa [hcomp_left, hcomp_right] using hcomm /-- leftMulMatrix is additive: leftMulMatrix (A + B) = leftMulMatrix A + leftMulMatrix B -/ -theorem leftMulMatrix_add {m : Type*} [Fintype m] [DecidableEq m] +lemma leftMulMatrix_add {m : Type*} [Fintype m] [DecidableEq m] (A B : Matrix m m ℂ) : 𝐋 (A + B) = 𝐋 A + 𝐋 B := by simp only [leftMulMatrix] @@ -125,7 +115,7 @@ lemma leftMulMatrix_smul {m : Type*} [Fintype m] [DecidableEq m] exact (LinearMap.toMatrix (matrixBasis m) (matrixBasis m)).map_smul c (leftMul A) /-- rightMulMatrix is additive: rightMulMatrix (A + B) = rightMulMatrix A + rightMulMatrix B -/ -theorem rightMulMatrix_add {m : Type*} [Fintype m] [DecidableEq m] +lemma rightMulMatrix_add {m : Type*} [Fintype m] [DecidableEq m] (A B : Matrix m m ℂ) : 𝐑 (A + B) = 𝐑 A + 𝐑 B := by simp only [rightMulMatrix] @@ -145,7 +135,7 @@ lemma rightMulMatrix_smul {m : Type*} [Fintype m] [DecidableEq m] exact (LinearMap.toMatrix (matrixBasis m) (matrixBasis m)).map_smul c (rightMul A) /-- leftMulMatrix is homogeneous for real scalars -/ -theorem leftMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] +lemma leftMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] (r : ℝ) (A : Matrix m m ℂ) : 𝐋 (r • A) = r • 𝐋 A := by have h : (r : ℂ) • A = r • A := by @@ -156,7 +146,7 @@ theorem leftMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] simp [Complex.real_smul] /-- rightMulMatrix is homogeneous for real scalars -/ -theorem rightMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] +lemma rightMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] (r : ℝ) (A : Matrix m m ℂ) : 𝐑 (r • A) = r • 𝐑 A := by have h : (r : ℂ) • A = r • A := by @@ -166,23 +156,6 @@ theorem rightMulMatrix_smul_real {m : Type*} [Fintype m] [DecidableEq m] ext i j simp [Complex.real_smul] -/-- leftMulMatrix is multiplicative: leftMulMatrix (A * B) = leftMulMatrix A * leftMulMatrix B -/ -lemma leftMulMatrix_mul {m : Type*} [Fintype m] [DecidableEq m] - (A B : Matrix m m ℂ) : - 𝐋 (A * B) = 𝐋 A * 𝐋 B := by - simp only [leftMulMatrix] - have h : leftMul (A * B) = (leftMul A).comp (leftMul B) := by - ext X; simp [leftMul] - rw [h, LinearMap.toMatrix_comp (matrixBasis m) (matrixBasis m) (matrixBasis m)] - -/-- leftMulMatrix maps identity to identity -/ -lemma leftMulMatrix_one {m : Type*} [Fintype m] [DecidableEq m] : - 𝐋 (1 : Matrix m m ℂ) = (1 : Matrix (m × m) (m × m) ℂ) := by - simp only [leftMulMatrix] - have h : leftMul (1 : Matrix m m ℂ) = LinearMap.id := by - ext X; simp [leftMul] - rw [h, LinearMap.toMatrix_id (matrixBasis m)] - /-- rightMulMatrix is anti-multiplicative: rightMulMatrix (A * B) = rightMulMatrix B * rightMulMatrix A -/ lemma rightMulMatrix_mul {m : Type*} [Fintype m] [DecidableEq m] @@ -201,26 +174,6 @@ lemma rightMulMatrix_one {m : Type*} [Fintype m] [DecidableEq m] : ext X; simp [rightMul] rw [h, LinearMap.toMatrix_id (matrixBasis m)] - -/-- rightMulMatrix preserves powers: rightMulMatrix (B ^ n) = (rightMulMatrix B) ^ n -/ -lemma rightMulMatrix_pow {m : Type*} [Fintype m] [DecidableEq m] - (B : Matrix m m ℂ) (n : ℕ) : - 𝐑 (B ^ n) = (𝐑 B) ^ n := by - induction n with - | zero => simp [rightMulMatrix_one] - | succ n ih => - rw [pow_succ, rightMulMatrix_mul, ih, ← pow_succ'] - -/-- leftMulMatrix preserves powers: leftMulMatrix (A ^ n) = (leftMulMatrix A) ^ n -/ -lemma leftMulMatrix_pow {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (n : ℕ) : - 𝐋 (A ^ n) = (𝐋 A) ^ n := by - induction n with - | zero => simp [leftMulMatrix_one] - | succ n ih => - rw [pow_succ, leftMulMatrix_mul, ih] - rw [pow_succ] - /-- The standard basis element at index `(i, j)` is the matrix with `1` at `(i, j)` and `0` elsewhere. -/ lemma matrixBasis_apply_eq_single {m : Type*} [Fintype m] [DecidableEq m] (ij : m × m) : matrixBasis m ij = Matrix.single ij.1 ij.2 (1 : ℂ) := by @@ -273,7 +226,7 @@ lemma matrixBasis_repr_apply {m : Type*} [Fintype m] [DecidableEq m] _ = M i j := hsum' /-- Entry `(i, j), (k, l)` of `leftMulMatrix A` equals `A i k` if `j = l`, else `0`. -/ -theorem leftMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] +lemma leftMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] (A : Matrix m m ℂ) (i j k l : m) : 𝐋 A (i, j) (k, l) = if j = l then A i k else 0 := by classical @@ -287,7 +240,7 @@ theorem leftMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] · simp [hjl] /-- Entry `(i, j), (k, l)` of `rightMulMatrix B` equals `B l j` if `i = k`, else `0`. -/ -theorem rightMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] +lemma rightMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] (B : Matrix m m ℂ) (i j k l : m) : 𝐑 B (i, j) (k, l) = if i = k then B l j else 0 := by classical @@ -301,13 +254,6 @@ theorem rightMulMatrix_apply {m : Type*} [Fintype m] [DecidableEq m] simp · simp [hik] -/-- Action of `leftMulMatrix A` on the vectorized form of `Kᴴ` yields `(A * Kᴴ) i j`. -/ -lemma leftMulMatrix_mulVec_vecConjTranspose {m : Type*} [Fintype m] [DecidableEq m] - (A K : Matrix m m ℂ) (i j : m) : - (𝐋 A *ᵥ (fun x : m × m => Kᴴ x.1 x.2)) (i, j) = (A * Kᴴ) i j := by - classical - simp [Matrix.mulVec, dotProduct, leftMulMatrix_apply, Matrix.mul_apply, Fintype.sum_prod_type] - /-- Action of `rightMulMatrix B` on the vectorized form of `Kᴴ` yields `(Kᴴ * B) i j`. -/ lemma rightMulMatrix_mulVec_vecConjTranspose {m : Type*} [Fintype m] [DecidableEq m] (B K : Matrix m m ℂ) (i j : m) : @@ -333,7 +279,7 @@ lemma leftRightMul_mulVec_vecConjTranspose {m : Type*} [Fintype m] [DecidableEq /-- The quadratic form star(vec(K†)) ⬝ᵥ (leftMulMatrix X * rightMulMatrix Y) *ᵥ vec(K†) equals Tr(K * X * K† * Y). -/ -theorem quadForm_leftRightMul_eq_trace {m : Type*} [Fintype m] [DecidableEq m] +lemma quadForm_leftRightMul_eq_trace {m : Type*} [Fintype m] [DecidableEq m] (X Y K : Matrix m m ℂ) : let v : (m × m) → ℂ := fun x => Kᴴ x.1 x.2 star v ⬝ᵥ ((leftMulMatrix X * rightMulMatrix Y) *ᵥ v) = @@ -368,49 +314,16 @@ lemma rightMulMatrix_eq_one_kronecker_transpose {m : Type*} [Fintype m] [Decidab simp [rightMulMatrix_apply, Matrix.kroneckerMap_apply, Matrix.transpose_apply] · simp [rightMulMatrix_apply, Matrix.kroneckerMap_apply, Matrix.transpose_apply, hik] -/-- `leftMulMatrix` commutes with conjugate transpose: `leftMulMatrix (Aᴴ) = (leftMulMatrix A)ᴴ`. -/ -lemma leftMulMatrix_conjTranspose {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) : - 𝐋 (Aᴴ) = (𝐋 A)ᴴ := by - classical - ext ⟨i, j⟩ ⟨k, l⟩ - simp only [leftMulMatrix_apply, Matrix.conjTranspose_apply] - by_cases hjl : j = l - · subst hjl; simp - · have hlj : ¬ l = j := Ne.symm hjl - simp [hjl, hlj] - -/-- `rightMulMatrix` commutes with conjugate transpose: `rightMulMatrix (Bᴴ) = (rightMulMatrix B)ᴴ`. -/ -lemma rightMulMatrix_conjTranspose {m : Type*} [Fintype m] [DecidableEq m] - (B : Matrix m m ℂ) : - 𝐑 (Bᴴ) = (𝐑 B)ᴴ := by - classical - ext ⟨i, j⟩ ⟨k, l⟩ - simp only [rightMulMatrix_apply, Matrix.conjTranspose_apply] - by_cases hik : i = k - · subst hik; simp - · have hki : ¬ k = i := Ne.symm hik - simp [hik, hki] - /-- `leftMulMatrix` preserves positive semidefiniteness. -/ -theorem leftMulMatrix_posSemidef {m : Type*} [Fintype m] [DecidableEq m] +lemma leftMulMatrix_posSemidef {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosSemidef) : (𝐋 A).PosSemidef := by classical simpa [leftMulMatrix_eq_kronecker_one] using (Matrix.PosSemidef.kronecker (m := m) (x := A) (y := (1 : Matrix m m ℂ)) hA posSemidef_one) -/-- `rightMulMatrix` preserves positive semidefiniteness. -/ -lemma rightMulMatrix_posSemidef {m : Type*} [Fintype m] [DecidableEq m] - {B : Matrix m m ℂ} (hB : B.PosSemidef) : - (𝐑 B).PosSemidef := by - classical - have hB' : Bᵀ.PosSemidef := hB.transpose - simpa [rightMulMatrix_eq_one_kronecker_transpose] using - (Matrix.PosSemidef.kronecker (m := m) (x := (1 : Matrix m m ℂ)) (y := Bᵀ) posSemidef_one hB') - /-- `rightMulMatrix` preserves positive definiteness. -/ -theorem rightMulMatrix_posDef {m : Type*} [Fintype m] [DecidableEq m] +lemma rightMulMatrix_posDef {m : Type*} [Fintype m] [DecidableEq m] {B : Matrix m m ℂ} (hB : B.PosDef) : (𝐑 B).PosDef := by classical @@ -418,245 +331,25 @@ theorem rightMulMatrix_posDef {m : Type*} [Fintype m] [DecidableEq m] simpa [rightMulMatrix_eq_one_kronecker_transpose] using (Matrix.PosDef.kronecker (m := m) (x := (1 : Matrix m m ℂ)) (y := Bᵀ) posDef_one hB') -/-- leftMulMatrix as a star algebra homomorphism over ℝ. - This allows using the CFC infrastructure to relate - leftMulMatrix (f(A)) = f(leftMulMatrix(A)) for continuous f. -/ -noncomputable def leftMulStarAlgHom {m : Type*} [Fintype m] [DecidableEq m] : - Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ where - toFun := leftMulMatrix - map_one' := leftMulMatrix_one - map_mul' := leftMulMatrix_mul - map_zero' := by - change 𝐋 0 = 0 - have h : leftMul (0 : Matrix m m ℂ) = 0 := by ext X; simp [leftMul] - simp only [leftMulMatrix, h, map_zero] - map_add' := leftMulMatrix_add - commutes' r := by - simp only [Algebra.algebraMap_eq_smul_one] - rw [leftMulMatrix_smul_real, leftMulMatrix_one] - map_star' a := by - simp only [star_eq_conjTranspose] - exact leftMulMatrix_conjTranspose a - -/-- The leftMulMatrix homomorphism is continuous (finite dimensional). -/ -lemma leftMulStarAlgHom_continuous {m : Type*} [Fintype m] [DecidableEq m] : - Continuous (leftMulStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ) := by - -- leftMulMatrix is a linear map between finite-dimensional normed spaces, hence continuous - letI : SeminormedAddCommGroup (Matrix m m ℂ) := Matrix.linftyOpSeminormedAddCommGroup - letI : NormedSpace ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedSpace - letI : IsBoundedSMul ℝ (Matrix m m ℂ) := Matrix.linftyOpIsBoundedSMul - letI : SeminormedAddCommGroup (Matrix (m × m) (m × m) ℂ) := - Matrix.linftyOpSeminormedAddCommGroup - letI : NormedSpace ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedSpace - letI : IsBoundedSMul ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpIsBoundedSMul - haveI : FiniteDimensional ℝ (Matrix m m ℂ) := inferInstance - let f : Matrix m m ℂ →ₗ[ℝ] Matrix (m × m) (m × m) ℂ := - (leftMulStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] _).toAlgHom.toLinearMap - change Continuous (f : Matrix m m ℂ → _) - exact f.continuous_of_finiteDimensional - -/-- CFC commutes with leftMulMatrix: for self-adjoint A and continuous f, - leftMulMatrix (cfc f A) = cfc f (leftMulMatrix A). -/ -lemma leftMulMatrix_cfc {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : IsSelfAdjoint A) (f : ℝ → ℝ) - (hf : ContinuousOn f (spectrum ℝ A) := by cfc_cont_tac) : - 𝐋 (cfc f A) = cfc f (𝐋 A) := by - letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix m m ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) - letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) - exact StarAlgHom.map_cfc leftMulStarAlgHom f A hf - leftMulStarAlgHom_continuous hA - -/-- leftMulMatrix preserves rpow: leftMulMatrix (A ^ s) = (leftMulMatrix A) ^ s - for positive semidefinite A and real s. -/ -lemma leftMulMatrix_rpow {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.PosSemidef) {s : ℝ} (hs : 0 ≤ s) : - 𝐋 (A ^ s) = (𝐋 A) ^ s := by - letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix m m ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) - letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) - have hA0 : (0 : Matrix m m ℂ) ≤ A := by simpa [Matrix.le_iff] using hA - have hLA0 : (0 : Matrix (m × m) (m × m) ℂ) ≤ 𝐋 A := by - simpa [Matrix.le_iff] using leftMulMatrix_posSemidef hA - have hcont_rpow : ContinuousOn (fun x : ℝ => x ^ s) (spectrum ℝ A) := - (Real.continuous_rpow_const hs).continuousOn - rw [CFC.rpow_eq_cfc_real (a := A) (ha := hA0), - CFC.rpow_eq_cfc_real (a := 𝐋 A) (ha := hLA0)] - exact leftMulMatrix_cfc A hA.1.isSelfAdjoint (· ^ s) hcont_rpow - -/-- B ↦ rightMulMatrix(Bᴴ) as a star algebra homomorphism over ℝ. -This composes the anti-homomorphism `rightMulMatrix` with the anti-involution -conjTranspose, yielding a genuine homomorphism. -/ -noncomputable def rightMulConjTransposeStarAlgHom {m : Type*} [Fintype m] [DecidableEq m] : - Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ where - toFun B := 𝐑 (Bᴴ) - map_one' := by simp [conjTranspose_one, rightMulMatrix_one] - map_mul' A B := by - change 𝐑 ((A * B)ᴴ) = 𝐑 (Aᴴ) * 𝐑 (Bᴴ) - rw [conjTranspose_mul, rightMulMatrix_mul] - map_zero' := by - change 𝐑 (0ᴴ) = 0 - rw [conjTranspose_zero] - have h : rightMul (0 : Matrix m m ℂ) = 0 := by ext X; simp [rightMul] - simp only [rightMulMatrix, h, map_zero] - map_add' A B := by - change 𝐑 ((A + B)ᴴ) = 𝐑 (Aᴴ) + 𝐑 (Bᴴ) - rw [conjTranspose_add, rightMulMatrix_add] - commutes' r := by - change 𝐑 ((algebraMap ℝ (Matrix m m ℂ) r)ᴴ) = algebraMap ℝ _ r - simp only [Algebra.algebraMap_eq_smul_one] - rw [conjTranspose_smul, conjTranspose_one, star_trivial, - rightMulMatrix_smul_real, rightMulMatrix_one] - map_star' A := by - simp only [star_eq_conjTranspose, conjTranspose_conjTranspose, - rightMulMatrix_conjTranspose] - -/-- The `rightMulConjTransposeStarAlgHom` is continuous (finite dimensional). -/ -lemma rightMulConjTransposeStarAlgHom_continuous {m : Type*} [Fintype m] [DecidableEq m] : - Continuous (rightMulConjTransposeStarAlgHom : - Matrix m m ℂ →⋆ₐ[ℝ] Matrix (m × m) (m × m) ℂ) := by - letI : SeminormedAddCommGroup (Matrix m m ℂ) := Matrix.linftyOpSeminormedAddCommGroup - letI : NormedSpace ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedSpace - letI : IsBoundedSMul ℝ (Matrix m m ℂ) := Matrix.linftyOpIsBoundedSMul - letI : SeminormedAddCommGroup (Matrix (m × m) (m × m) ℂ) := - Matrix.linftyOpSeminormedAddCommGroup - letI : NormedSpace ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedSpace - letI : IsBoundedSMul ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpIsBoundedSMul - haveI : FiniteDimensional ℝ (Matrix m m ℂ) := inferInstance - let f : Matrix m m ℂ →ₗ[ℝ] Matrix (m × m) (m × m) ℂ := - (rightMulConjTransposeStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] _).toAlgHom.toLinearMap - change Continuous (f : Matrix m m ℂ → _) - exact f.continuous_of_finiteDimensional - -/-- CFC commutes with rightMulMatrix for self-adjoint (Hermitian) matrices. -Uses the `rightMulConjTransposeStarAlgHom` to transport CFC via `StarAlgHom.map_cfc`. -Key insight: For Hermitian B, `Bᴴ = B`, so `Ψ(B) = rightMulMatrix(B)`, -and for self-adjoint `cfc f B`, `Ψ(cfc f B) = rightMulMatrix(cfc f B)`. -/ -lemma rightMulMatrix_cfc {m : Type*} [Fintype m] [DecidableEq m] - (B : Matrix m m ℂ) (hB : IsSelfAdjoint B) (f : ℝ → ℝ) - (hf : ContinuousOn f (spectrum ℝ B) := by cfc_cont_tac) : - 𝐑 (cfc f B) = cfc f (𝐑 B) := by - letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix m m ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) - letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) - -- Ψ = rightMulConjTransposeStarAlgHom: B ↦ rightMulMatrix(Bᴴ) - -- StarAlgHom.map_cfc gives: Ψ(cfc f B) = cfc f (Ψ B) - have h_map := StarAlgHom.map_cfc rightMulConjTransposeStarAlgHom f B hf - rightMulConjTransposeStarAlgHom_continuous hB - -- Ψ(B) = rightMulMatrix(Bᴴ) = rightMulMatrix(B) since B is Hermitian - have h_psi_B : (rightMulConjTransposeStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] _) B = - 𝐑 B := by - dsimp [rightMulConjTransposeStarAlgHom] - rw [← star_eq_conjTranspose, hB.star_eq] - -- Ψ(cfc f B) = rightMulMatrix((cfc f B)ᴴ) = rightMulMatrix(cfc f B) - -- since cfc f B is self-adjoint - have h_psi_cfc : (rightMulConjTransposeStarAlgHom : Matrix m m ℂ →⋆ₐ[ℝ] _) (cfc f B) = - 𝐑 (cfc f B) := by - dsimp [rightMulConjTransposeStarAlgHom] - rw [← star_eq_conjTranspose, (cfc_predicate f B : IsSelfAdjoint (cfc f B)).star_eq] - rw [h_psi_B, h_psi_cfc] at h_map - exact h_map - -/-- `rightMulMatrix` preserves rpow: `rightMulMatrix (B ^ s) = (rightMulMatrix B) ^ s` -for positive semidefinite `B` and real `s`. -/ -lemma rightMulMatrix_rpow {m : Type*} [Fintype m] [DecidableEq m] - {B : Matrix m m ℂ} (hB : B.PosSemidef) {s : ℝ} (hs : 0 ≤ s) : - 𝐑 (B ^ s) = (𝐑 B) ^ s := by - letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix m m ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m) (A := ℂ) - letI : NormedRing (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedRing - letI : NormedAlgebra ℝ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : NormedAlgebra ℂ (Matrix (m × m) (m × m) ℂ) := Matrix.linftyOpNormedAlgebra - letI : CStarAlgebra (Matrix (m × m) (m × m) ℂ) := by - simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := m × m) (A := ℂ) - have hB0 : (0 : Matrix m m ℂ) ≤ B := by simpa [Matrix.le_iff] using hB - have hRB0 : (0 : Matrix (m × m) (m × m) ℂ) ≤ 𝐑 B := by - simpa [Matrix.le_iff] using rightMulMatrix_posSemidef hB - have hcont_rpow : ContinuousOn (fun x : ℝ => x ^ s) (spectrum ℝ B) := - (Real.continuous_rpow_const hs).continuousOn - rw [CFC.rpow_eq_cfc_real (a := B) (ha := hB0), - CFC.rpow_eq_cfc_real (a := 𝐑 B) (ha := hRB0)] - exact rightMulMatrix_cfc B hB.1.isSelfAdjoint (· ^ s) hcont_rpow - /-- Matrix perspective of a function `f` using the Kubo-Ando style formula. Defined for PSD `L` and PD `R`. -/ noncomputable def matrixPerspective {m : Type*} [Fintype m] [DecidableEq m] - (f : ℝ → ℝ) (L R : Matrix m m ℂ) (hL : L.PosSemidef) (hR : R.PosDef) : Matrix m m ℂ := + (f : ℝ → ℝ) (L R : Matrix m m ℂ) (_hL : L.PosSemidef) (hR : R.PosDef) : Matrix m m ℂ := let Rinv := matrixInvSqrt R hR let inner := Rinvᴴ * L * Rinv - let hinner : inner.IsHermitian := - isHermitian_conjTranspose_mul_mul (B := Rinv) (A := L) hL.1 - let fInner := matrixFunction (fun x => (f x : ℂ)) inner hinner + let fInner := cfc f inner let Rhalf := matrixSqrt R hR.posSemidef Rhalf * fInner * Rhalf -/-- The matrix perspective of a function preserves Hermiticity. -/ -lemma matrixPerspective_isHermitian {m : Type*} [Fintype m] [DecidableEq m] - (f : ℝ → ℝ) (L R : Matrix m m ℂ) (hL : L.PosSemidef) (hR : R.PosDef) : - (matrixPerspective f L R hL hR).IsHermitian := by - classical - unfold matrixPerspective - dsimp - set Rinv := matrixInvSqrt R hR - set inner := Rinvᴴ * L * Rinv - have hinner : inner.IsHermitian := - isHermitian_conjTranspose_mul_mul (B := Rinv) (A := L) hL.1 - set fInner := matrixFunction (fun x => (f x : ℂ)) inner hinner - set Rhalf := matrixSqrt R hR.posSemidef - have hRhalf : Rhalf.IsHermitian := matrixSqrt_isHermitian hR.posSemidef - have hfin : fInner.IsHermitian := - matrixFunction_isHermitian hinner (fun x => f x) - simpa [hRhalf.eq] using - (isHermitian_mul_mul_conjTranspose (B := Rhalf) (A := fInner) hfin) - /-- Congruence lemma for matrixPerspective: equal matrices give equal results regardless of the proof terms. -/ -theorem matrixPerspective_congr {m : Type*} [Fintype m] [DecidableEq m] +lemma matrixPerspective_congr {m : Type*} [Fintype m] [DecidableEq m] (f : ℝ → ℝ) (L₁ L₂ R₁ R₂ : Matrix m m ℂ) (hL₁ : L₁.PosSemidef) (hL₂ : L₂.PosSemidef) (hR₁ : R₁.PosDef) (hR₂ : R₂.PosDef) (hL : L₁ = L₂) (hR : R₁ = R₂) : matrixPerspective f L₁ R₁ hL₁ hR₁ = matrixPerspective f L₂ R₂ hL₂ hR₂ := by cases hL; cases hR; rfl -/-- Perspective for left/right multiplication matrices. -/ -noncomputable def leftRightMatrixPerspective {m : Type*} [Fintype m] [DecidableEq m] - (f : ℝ → ℝ) (A B : Matrix m m ℂ) - (hA : (𝐋 A).PosSemidef) (hB : (𝐑 B).PosDef) : - Matrix (m × m) (m × m) ℂ := - matrixPerspective f (𝐋 A) (𝐑 B) hA hB - -/-- The left-right matrix perspective preserves Hermiticity. -/ -lemma leftRightMatrixPerspective_isHermitian {m : Type*} [Fintype m] [DecidableEq m] - (f : ℝ → ℝ) (A B : Matrix m m ℂ) - (hA : (𝐋 A).PosSemidef) (hB : (𝐑 B).PosDef) : - (leftRightMatrixPerspective f A B hA hB).IsHermitian := by - simpa [leftRightMatrixPerspective] using - (matrixPerspective_isHermitian (m := m × m) f (𝐋 A) (𝐑 B) hA hB) - /-- Cancellation for (c · (S · P))† (c · (S · P)) = c² · (P · R · P) when S² = R. -/ lemma perspective_AA_cancel {n : Type*} [Fintype n] (c w : ℝ) (P S R : Matrix n n ℂ) @@ -790,21 +483,18 @@ theorem matrixPerspective_joint_convex.{v} {m : Type v} [Fintype m] [DecidableEq (isHermitian_conjTranspose_mul_mul (B := A₁) (A := T₁) hT₁_herm) (isHermitian_conjTranspose_mul_mul (B := A₂) (A := T₂) hT₂_herm) have hconv' := hconv (m := m) (A := A₁) (B := A₂) (T₁ := T₁) (T₂ := T₂) hT₁ hT₂ hAB hC + simp only [] at hconv' have hpsd : - (A₁ᴴ * - matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A₁ + - A₂ᴴ * - matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * A₂ - - matrixFunction (fun x => (f x : ℂ)) (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) hC + (A₁ᴴ * cfc f T₁ * A₁ + + A₂ᴴ * cfc f T₂ * A₂ - + cfc f (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) ).PosSemidef := by simpa [Matrix.le_iff] using hconv' have hpsd' : (Rhalfᴴ * - (A₁ᴴ * - matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A₁ + - A₂ᴴ * - matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * A₂ - - matrixFunction (fun x => (f x : ℂ)) (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) hC) * Rhalf + (A₁ᴴ * cfc f T₁ * A₁ + + A₂ᴴ * cfc f T₂ * A₂ - + cfc f (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂)) * Rhalf ).PosSemidef := hpsd.conjTranspose_mul_mul_same Rhalf have hRhalf_eq : Rhalfᴴ = Rhalf := hRhalf_herm.eq @@ -841,13 +531,9 @@ theorem matrixPerspective_joint_convex.{v} {m : Type v} [Fintype m] [DecidableEq rw [hA₂_adj, mul_smul_comm]; congr 1; rw [← mul_assoc, hRhalf_Rinv, one_mul] have hA₂_Rhalf : A₂ * Rhalf = (Real.sqrt w₂ : ℂ) • R₂half := by simp only [A₂, smul_mul_assoc, mul_assoc, hRinv_Rhalf, mul_one] - -- matrixFunction_congr for the f(C) term (precomputed for performance) - have hmfC : matrixFunction (fun x => (f x : ℂ)) - (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) hC = - matrixFunction (fun x => (f x : ℂ)) (Rinvᴴ * L * Rinv) - (isHermitian_conjTranspose_mul_mul (B := Rinv) (A := L) - ((hL₁.real_smul hw₁).add (hL₂.real_smul hw₂)).1) := - matrixFunction_congr (fun x => (f x : ℂ)) hC _ hinner + -- congruence for the f(C) term (precomputed for performance) + have hmfC : cfc f (A₁ᴴ * T₁ * A₁ + A₂ᴴ * T₂ * A₂) = cfc f (Rinvᴴ * L * Rinv) := + congrArg (cfc f) hinner -- Final step: apply sandwich equation and conclude have hfinal : matrixPerspective f L R ((hL₁.real_smul hw₁).add (hL₂.real_smul hw₂)) hR ≤ @@ -860,47 +546,8 @@ theorem matrixPerspective_joint_convex.{v} {m : Type v} [Fintype m] [DecidableEq exact hpsd' simpa [L, R] using hfinal -/-- The sign matrix Σ = I ⊕ (-I) on m ⊕ m is unitary. -/ -lemma signMatrix_mem_unitary {m : Type*} [Fintype m] [DecidableEq m] : - fromBlocks (1 : Matrix m m ℂ) 0 0 (-1 : Matrix m m ℂ) ∈ - unitary (Matrix (m ⊕ m) (m ⊕ m) ℂ) := by - rw [Unitary.mem_iff] - constructor <;> (simp [star_eq_conjTranspose, fromBlocks_conjTranspose, - fromBlocks_multiply, fromBlocks_one]) - -/-- The sign matrix is self-adjoint: Σ* = Σ. -/ -lemma signMatrix_star_eq {m : Type*} [DecidableEq m] : - star (fromBlocks (1 : Matrix m m ℂ) 0 0 (-1 : Matrix m m ℂ)) = - fromBlocks (1 : Matrix m m ℂ) 0 0 (-1 : Matrix m m ℂ) := by - simp [star_eq_conjTranspose, fromBlocks_conjTranspose] - /-! ### Kronecker Product Powers and Perspective Identity -/ -/-- Kronecker product of natural number powers: `(A ⊗ₖ M)^n = A^n ⊗ₖ M^n`. -/ -lemma kronecker_npow {m : Type*} [Fintype m] [DecidableEq m] - (A M : Matrix m m ℂ) (n : ℕ) : - (A ⊗ₖ M) ^ n = (A ^ n) ⊗ₖ (M ^ n) := by - induction n with - | zero => simp - | succ n ih => - -- Work with the explicit Kronecker product type - set K : Matrix (m × m) (m × m) ℂ := A ⊗ₖ M with hK - change K ^ (n + 1) = _ - rw [pow_succ, ih, hK, ← mul_kronecker_mul, ← pow_succ, ← pow_succ] - -/-- The matrixFunction `x ↦ x^(p:ℂ)` equals rpow for PosSemidef matrices with non-negative - eigenvalues and real exponent p. -/ -lemma matrixFunction_cpow_eq_rpow {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.PosSemidef) (p : ℝ) : - matrixFunction (fun x => x ^ (p : ℂ)) A hA.1 = - matrixFunction (fun x => ((x ^ p : ℝ) : ℂ)) A hA.1 := by - unfold matrixFunction - have h_diag : (fun i => (hA.1.eigenvalues i : ℂ) ^ (p : ℂ)) = - (fun i => ((hA.1.eigenvalues i ^ p : ℝ) : ℂ)) := by - funext i - exact (Complex.ofReal_cpow (hA.eigenvalues_nonneg i) p).symm - simp_rw [h_diag] - /-- For commuting PSD L and PD R, the perspective inner matrix simplifies: Rinv† * L * Rinv = L * R^{-1}. Since matrixInvSqrt is Hermitian (self-adjoint), Rinv† = Rinv, @@ -920,8 +567,7 @@ lemma perspective_inner_eq_mul_inv {n : Type*} [Fintype n] [DecidableEq n] (hcomm : L * R = R * L) : (matrixInvSqrt R hR)ᴴ * L * matrixInvSqrt R hR = L * R⁻¹ := by rw [perspective_inner_eq_commuting hL hR hcomm] - have hRinv_eq : matrixInvSqrt R hR = R ^ (-1 / 2 : ℝ) := by - simpa [matrixInvSqrt] using matrixFunction_rpow_eq hR.posSemidef (-1 / 2 : ℝ) + have hRinv_eq : matrixInvSqrt R hR = R ^ (-1 / 2 : ℝ) := matrixInvSqrt_eq_rpow hR rw [hRinv_eq] letI : NormedRing (Matrix n n ℂ) := Matrix.linftyOpNormedRing letI : NormedAlgebra ℝ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra @@ -1111,9 +757,7 @@ lemma perspective_inner_rpow_comm_sqrt_leftRight {m : Type*} [Fintype m] [Decida have hR_nonneg : (0 : Matrix (m × m) (m × m) ℂ) ≤ R := by simpa [Matrix.le_iff] using hR_pd.posSemidef have hR_unit : IsUnit R := hR_pd.isUnit - have hRhalf_eq : T = R ^ (1 / 2 : ℝ) := by - change matrixSqrt R hR_pd.posSemidef = R ^ (1 / 2 : ℝ) - simpa [matrixSqrt] using matrixFunction_rpow_eq hR_pd.posSemidef (1 / 2 : ℝ) + have hRhalf_eq : T = R ^ (1 / 2 : ℝ) := matrixSqrt_eq_rpow hR_pd.posSemidef have hR_det : IsUnit R.det := (Matrix.isUnit_iff_isUnit_det R).mp hR_unit have hLRinv_comm_R : Commute R (L * R⁻¹) := by rw [Commute, SemiconjBy] @@ -1138,7 +782,7 @@ lemma perspective_inner_rpow_comm_sqrt_leftRight {m : Type*} [Fintype m] [Decida equals −(L_{Aᵖ} · R_{B¹⁻ᵖ}) for PD matrices A, B and p ≥ 0. Here L = L_A, R = R_B, S = R^(⁻¹⁄₂), T = R^(¹⁄₂), and the perspective is T · f(S* L S) · T. -/ -theorem matrixPerspective_neg_leftRight_eq {m : Type*} [Fintype m] [DecidableEq m] +lemma matrixPerspective_neg_leftRight_eq {m : Type*} [Fintype m] [DecidableEq m] (A B : Matrix m m ℂ) (hA : A.PosDef) (hB : B.PosDef) (p : ℝ) (hp : 0 ≤ p) (hL_psd : (𝐋 A).PosSemidef) (hR_pd : (𝐑 B).PosDef) : matrixPerspective (fun x => -(x ^ p)) (𝐋 A) (𝐑 B) hL_psd hR_pd = @@ -1149,13 +793,9 @@ theorem matrixPerspective_neg_leftRight_eq {m : Type*} [Fintype m] [DecidableEq set T := matrixSqrt R hR_pd.posSemidef have hinner_psd : (Sᴴ * L * S).PosSemidef := hL_psd.conjTranspose_mul_mul_same S - have hfun_neg : matrixFunction (fun x => ((-(x ^ p) : ℝ) : ℂ)) - (Sᴴ * L * S) hinner_psd.1 = -((Sᴴ * L * S) ^ p) := by - have h1 : (fun x : ℝ => ((-(x ^ p) : ℝ) : ℂ)) = (fun x : ℝ => -((x ^ p : ℝ) : ℂ)) := by - ext x - push_cast - ring - rw [h1, matrixFunction_neg hinner_psd.1, matrixFunction_rpow_eq hinner_psd p] + have hfun_neg : cfc (fun x : ℝ => -(x ^ p)) (Sᴴ * L * S) = -((Sᴴ * L * S) ^ p) := by + rw [cfc_neg, ← CFC.rpow_eq_cfc_real (a := Sᴴ * L * S) + (ha := by rw [Matrix.le_iff, sub_zero]; exact hinner_psd)] have hRhalf_sq : T * T = R := matrixSqrt_mul_self_posSemidef hR_pd.posSemidef have hinnerp_comm_Rhalf : (Sᴴ * L * S) ^ p * T = T * (Sᴴ * L * S) ^ p := by simpa [L, R, S, T] using diff --git a/QuantumSystem/Analysis/Matrix/HermitianFunctionalCalculus.lean b/QuantumSystem/Analysis/Matrix/HermitianFunctionalCalculus.lean index 50f7dad..96c2d16 100644 --- a/QuantumSystem/Analysis/Matrix/HermitianFunctionalCalculus.lean +++ b/QuantumSystem/Analysis/Matrix/HermitianFunctionalCalculus.lean @@ -15,23 +15,24 @@ This file develops the core tools for matrix analysis. Foundational lemmas about matrices, positive semidefiniteness, block-matrix identities, and the Löwner order are in `QuantumSystem.ForMathlib.Analysis.Matrix.*`. +All functional calculus is expressed directly through Mathlib's continuous functional +calculus `cfc`; the spectral expansion `cfc f A = U diag(f(λᵢ)) Uᴴ` is `cfc_spectral_eq`. + ## Main results -### Functional Calculus -- `matrixFunction f A hA`: spectral decomposition f(A) = U diag(f(λᵢ)) U* - for Hermitian A with eigendecomposition A = UΛ U*. -- Algebraic properties: `matrixFunction_id`, `matrixFunction_neg`, `matrixFunction_add`, - `matrixFunction_smul`, `matrixFunction_const`, `matrixFunction_add_const`, etc. -- Complex power instances: `matrixFunction_cpow_zero`, `matrixFunction_cpow_one`. -- Compatibility with Mathlib's CFC: `matrixFunction_eq_cfc`. -- Special functions: `matrixExp`, `matrixLog`, `matrixSqrt` (via `matrixFunction`). +### Continuous Functional Calculus +- `cfc_spectral_eq`: spectral decomposition `cfc f A = U diag(f(λᵢ)) Uᴴ` + for Hermitian A with eigendecomposition A = UΛ Uᴴ. +- `trace_cfc`, `trace_mul_cfc`: trace formulas `Tr(f(A)) = ∑ f(λᵢ)` and `Tr(A·f(A)) = ∑ λᵢ f(λᵢ)`. +- `cfc_isHermitian`, `mul_cfc_isHermitian`: `f(A)` and `A·f(A)` are Hermitian for real `f`. +- `cfc_add_const_eq`, `cfc_inv_add_const`, `cfc_resolvent`: affine / resolvent identities. +- `cfc_compression_of_commuting`: `Vᴴ f(M) V = f(Vᴴ M V)` for an isometry commuting with `M`. +- Matrix logarithm `cfc Real.log`: `cfc_spectral_eq`, `cfc_log_spectral_eq`, `cfc_log_map_starAlgEquiv`. +- Special functions: `matrixSqrt`, `matrixInvSqrt` defined via `CFC.rpow`. ### Hermitian and PSD Structure -- `matrixFunction_isHermitian`: f(A) is Hermitian when f maps ℝ to ℝ. -- `matrixFunction_posSemidef`: f(A) ≥ 0 when f(λᵢ) ≥ 0 on eigenvalues. -- `matrixFunction_inv_add_const`: (A + tI)⁻¹ from `matrixFunction`. -- `matrixFunction_rpow_eq`: `matrixFunction` agrees with `CFC.rpow` on PSD matrices. -- `matrixSqrt`: the matrix square root A¹⁄² for PSD A. +- `matrixSqrt`: the matrix square root A¹⁄² for PSD A (`= A ^ (1/2 : ℝ)`). +- `matrixInvSqrt`: the matrix inverse square root A⁻¹⁄² for PD A (`= A ^ (-1/2 : ℝ)`). - `matrixInvSqrt_commute_of_commute`: R⁻¹⁄² commutes with L when L and R commute (for PSD L, PD R). @@ -53,212 +54,75 @@ namespace Matrix open scoped MatrixOrder ComplexOrder -/-- Functional calculus for Hermitian matrices via spectral decomposition. -Given f : ℝ → ℂ and a Hermitian matrix A = U Λ U*, we define f(A) = U f(Λ) U* -where f(Λ) applies f to each diagonal entry (eigenvalue). -/ -noncomputable def matrixFunction {m : Type*} [Fintype m] [DecidableEq m] - (f : ℝ → ℂ) (A : Matrix m m ℂ) (hA : A.IsHermitian) : Matrix m m ℂ := - let U : Matrix m m ℂ := hA.eigenvectorUnitary - let Λ := diagonal (fun i => f (hA.eigenvalues i)) - U * Λ * Uᴴ +/-- `cfc f A` of a Hermitian matrix expands into the spectral decomposition +`U · diag(f(λᵢ)) · Uᴴ` attached to the chosen Hermitian proof. -/ +lemma cfc_spectral_eq {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : + cfc f A = + (hA.eigenvectorUnitary : Matrix m m ℂ) * + diagonal (fun i => ((f (hA.eigenvalues i) : ℝ) : ℂ)) * + (hA.eigenvectorUnitary : Matrix m m ℂ)ᴴ := by + rw [Matrix.IsHermitian.cfc_eq hA f] + unfold Matrix.IsHermitian.cfc + rw [Unitary.conjStarAlgAut_apply] + simp only [Function.comp_def, star_eq_conjTranspose] + rfl -/-- Trace of f(A) equals sum of f(λ_i) by cyclicity of trace and unitarity of U. -/ -lemma matrixFunction_trace {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) (f : ℝ → ℂ) : - (matrixFunction f A hA).trace = ∑ i, f (hA.eigenvalues i) := by - unfold matrixFunction - rw [trace_mul_cycle] +/-- Trace of `cfc f A` equals the sum of `f` over the eigenvalues of `A`. -/ +lemma trace_cfc {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : + (cfc f A).trace = ∑ i, (f (hA.eigenvalues i) : ℂ) := by + rw [cfc_spectral_eq hA f, trace_mul_cycle] have h := Unitary.coe_star_mul_self hA.eigenvectorUnitary simp only [star_eq_conjTranspose] at h rw [h, Matrix.one_mul] exact trace_diagonal _ -/-- matrixFunction of a real-valued function produces a Hermitian matrix. -/ -lemma matrixFunction_isHermitian {m : Type*} [Fintype m] [DecidableEq m] +/-- `f(A)` is Hermitian for Hermitian `A` and real `f` (continuous functional calculus). -/ +lemma cfc_isHermitian {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : - (matrixFunction (fun x => (f x : ℂ)) A hA).IsHermitian := by - unfold matrixFunction - -- U * D * Uᴴ is Hermitian when D is Hermitian and U is unitary - have hD : (diagonal (fun i => (f (hA.eigenvalues i) : ℂ))).IsHermitian := by + (cfc f A).IsHermitian := by + rw [cfc_spectral_eq hA f] + have hD : (diagonal (fun i => ((f (hA.eigenvalues i) : ℝ) : ℂ))).IsHermitian := by rw [isHermitian_diagonal_iff] intro i exact Complex.conj_ofReal _ - -- (U D Uᴴ)ᴴ = U Dᴴ Uᴴ = U D Uᴴ since D is Hermitian rw [IsHermitian] simp only [conjTranspose_mul, conjTranspose_conjTranspose] conv_rhs => rw [mul_assoc] rw [hD] -/-- Spectral lemma: matrixFunction(id) = A. -/ -lemma matrixFunction_id {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) : - matrixFunction (fun x => (x : ℂ)) A hA = A := by - unfold matrixFunction - simp only - conv_rhs => rw [hA.spectral_theorem] - unfold Unitary.conjStarAlgAut - simp only [MonoidHom.coe_mk, OneHom.coe_mk] - rfl - -/-- Negation distributes through matrixFunction. -/ -lemma matrixFunction_neg {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℂ) : - matrixFunction (fun x => -f x) A hA = -matrixFunction f A hA := by - unfold matrixFunction - simp only - have hdiag : diagonal (fun i => -f (hA.eigenvalues i)) = - -diagonal (fun i => f (hA.eigenvalues i)) := by - ext i j - simp only [diagonal_apply, neg_apply] - split_ifs <;> ring - rw [hdiag, mul_neg, neg_mul] - -/-- matrixFunction distributes over addition. -/ -lemma matrixFunction_add {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) (f g : ℝ → ℂ) : - matrixFunction (fun x => f x + g x) A hA = - matrixFunction f A hA + matrixFunction g A hA := by - unfold matrixFunction - simp only - have hdiag : - diagonal (fun i => f (hA.eigenvalues i) + g (hA.eigenvalues i)) = - diagonal (fun i => f (hA.eigenvalues i)) + - diagonal (fun i => g (hA.eigenvalues i)) := by - ext i j - by_cases h : i = j - · subst h - simp - · simp [h] - rw [hdiag] - rw [Matrix.mul_add, Matrix.add_mul] - -/-- matrixFunction commutes with scalar multiplication. -/ -lemma matrixFunction_smul {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) (c : ℂ) (f : ℝ → ℂ) : - matrixFunction (fun x => c * f x) A hA = c • matrixFunction f A hA := by - unfold matrixFunction - simp only - have hdiag : - diagonal (fun i => c * f (hA.eigenvalues i)) = - c • diagonal (fun i => f (hA.eigenvalues i)) := by - ext i j - by_cases h : i = j - · subst h - simp - · simp [h] - rw [hdiag] - rw [Matrix.mul_smul, Matrix.smul_mul] - -/-- matrixFunction of a constant function is a scalar multiple of the identity. -/ -lemma matrixFunction_const {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) (c : ℂ) : - matrixFunction (fun _ => c) A hA = c • (1 : Matrix m m ℂ) := by - classical - unfold matrixFunction - simp only - have hdiag : diagonal (fun _ => c) = c • (1 : Matrix m m ℂ) := by - ext i j - by_cases h : i = j - · subst h - simp - · simp [h] - rw [hdiag] - have hU : (hA.eigenvectorUnitary : Matrix m m ℂ) * - (hA.eigenvectorUnitary : Matrix m m ℂ)ᴴ = 1 := by - simpa [star_eq_conjTranspose] using Unitary.coe_mul_star_self hA.eigenvectorUnitary - simp [hU] - -/-- matrixFunction of negation is negation of A. -/ -lemma matrixFunction_neg_id {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) : - matrixFunction (fun x => -(x : ℂ)) A hA = -A := by - have h : (fun x : ℝ => -(x : ℂ)) = (fun x => -((fun y : ℝ => (y : ℂ)) x)) := rfl - rw [h, matrixFunction_neg, matrixFunction_id] - -/-- matrixFunction depends only on the matrix value, not on the specific proof term. - If two matrices are equal, their matrixFunctions are equal. -/ -lemma matrixFunction_congr {m : Type*} [Fintype m] [DecidableEq m] - {A B : Matrix m m ℂ} (f : ℝ → ℂ) (hA : A.IsHermitian) (hB : B.IsHermitian) - (hAB : A = B) : matrixFunction f A hA = matrixFunction f B hB := by - subst hAB - rfl - -/-- matrixFunction equals Mathlib's IsHermitian.cfc for real-valued functions. -This connects our spectral decomposition definition to Mathlib's CFC infrastructure. -/ -lemma matrixFunction_eq_cfc {m : Type*} [Fintype m] [DecidableEq m] +/-- `Tr(A · f(A)) = ∑ᵢ λᵢ · f(λᵢ)` for the continuous functional calculus. -/ +lemma trace_mul_cfc {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : - matrixFunction (fun x => (f x : ℂ)) A hA = cfc f A := by - have h : matrixFunction (fun x => (f x : ℂ)) A hA = hA.cfc f := by - unfold matrixFunction Matrix.IsHermitian.cfc - rw [Unitary.conjStarAlgAut_apply] - simp only [Function.comp_def, star_eq_conjTranspose] - rfl - calc - matrixFunction (fun x => (f x : ℂ)) A hA = hA.cfc f := h - _ = cfc f A := by - simpa using (Matrix.IsHermitian.cfc_eq (A := A) (hA := hA) (f := f)).symm - -/-- `matrixFunction` for the affine function `x ↦ x + t` adds `t • I`. -/ -lemma matrixFunction_add_const {m : Type*} [Fintype m] [DecidableEq m] + (A * cfc f A).trace = ∑ i, ((hA.eigenvalues i : ℂ) * (f (hA.eigenvalues i) : ℂ)) := by + have hfin : (spectrum ℝ A).Finite := by + rw [hA.spectrum_real_eq_range_eigenvalues]; exact Set.finite_range _ + have hcont_id : ContinuousOn (fun x : ℝ => x) (spectrum ℝ A) := continuousOn_id + have hcont_f : ContinuousOn f (spectrum ℝ A) := hfin.continuousOn f + have hsa : IsSelfAdjoint A := hA + have hmul : A * cfc f A = cfc (fun x => x * f x) A := by + rw [cfc_mul (fun x => x) f A hcont_id hcont_f, cfc_id' (R := ℝ) (a := A) hsa] + rw [hmul, trace_cfc hA (fun x => x * f x)] + refine Finset.sum_congr rfl fun i _ => ?_ + push_cast + ring + +/-- `cfc (· + t) A = A + t • I` for Hermitian `A` via the continuous functional calculus. -/ +lemma cfc_add_const_eq {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.IsHermitian) (t : ℝ) : - matrixFunction (fun x => ((x + t : ℝ) : ℂ)) A hA = A + (t : ℂ) • 1 := by - classical - unfold matrixFunction - set U : Matrix m m ℂ := (hA.eigenvectorUnitary : Matrix m m ℂ) - have hdiag : - diagonal (fun i => ((hA.eigenvalues i + t : ℝ) : ℂ)) = - diagonal (fun i => (hA.eigenvalues i : ℂ)) + (t : ℂ) • 1 := by - ext i j - change (diagonal (fun i => ((hA.eigenvalues i + t : ℝ) : ℂ))) i j = - (diagonal (fun i => (hA.eigenvalues i : ℂ))) i j + ((t : ℂ) • (1 : Matrix m m ℂ)) i j - by_cases h : i = j - · subst h - rw [Matrix.diagonal_apply_eq, Matrix.diagonal_apply_eq, Matrix.smul_apply, - Matrix.one_apply_eq, smul_eq_mul, mul_one] - push_cast; ring - · rw [Matrix.diagonal_apply_ne _ h, Matrix.diagonal_apply_ne _ h, Matrix.smul_apply, - Matrix.one_apply_ne h, smul_zero, add_zero] - rw [hdiag] - simp only [Matrix.mul_add, Matrix.add_mul] - have hU : U * Uᴴ = 1 := by - simpa [U, star_eq_conjTranspose] using Unitary.coe_mul_star_self hA.eigenvectorUnitary - have hU1 : U * (1 : Matrix m m ℂ) * Uᴴ = 1 := by - simp [hU] - have hspec := hA.spectral_theorem - rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at hspec - have hspec' : U * diagonal (fun i => (hA.eigenvalues i : ℂ)) * Uᴴ = A := by - simpa [U, Function.comp] using hspec.symm - -- Rewrite the two diagonal terms using the spectral theorem and unitarity. - calc - U * diagonal (fun i => (hA.eigenvalues i : ℂ)) * Uᴴ + - U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = - A + (t : ℂ) • 1 := by - rw [hspec'] - have hUt : U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = - (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) := by - calc - U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = - ((t : ℂ) • (U * (1 : Matrix m m ℂ))) * Uᴴ := by - simp - _ = (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) := by - simp - calc - A + U * ((t : ℂ) • (1 : Matrix m m ℂ)) * Uᴴ = - A + (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) := by - rw [hUt] - _ = A + (t : ℂ) • 1 := by - calc - A + (t : ℂ) • (U * (1 : Matrix m m ℂ) * Uᴴ) = - A + (t : ℂ) • (U * Uᴴ) := by - simp - _ = A + (t : ℂ) • 1 := by - simp [hU] + cfc (fun x => x + t) A = A + (t : ℂ) • 1 := by + have hsa : IsSelfAdjoint A := hA + have hcont_id : ContinuousOn (fun x : ℝ => x) (spectrum ℝ A) := continuousOn_id + have hcont_c : ContinuousOn (fun _ : ℝ => t) (spectrum ℝ A) := continuousOn_const + rw [cfc_add A (fun x => x) (fun _ => t) hcont_id hcont_c, cfc_id' (R := ℝ) (a := A) hsa, + cfc_const t A, Algebra.algebraMap_eq_smul_one] + congr 1 -/-- `matrixFunction` for `x ↦ (x + t)⁻¹` equals `(A + t•I)⁻¹` when `t > 0` and `A` is PSD. -/ -lemma matrixFunction_inv_add_const {m : Type*} [Fintype m] [DecidableEq m] +/-- `cfc ((· + t)⁻¹) A = (A + t • I)⁻¹` for PSD `A` and `t > 0`. -/ +lemma cfc_inv_add_const {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosSemidef) {t : ℝ} (ht : 0 < t) : - matrixFunction (fun x => ((x + t : ℝ) : ℂ)⁻¹) A hA.1 = - (A + (t : ℂ) • 1)⁻¹ := by + cfc (fun x : ℝ => (x + t)⁻¹) A = (A + (t : ℂ) • 1)⁻¹ := by classical have hA' : A.IsHermitian := hA.1 have hneq : ∀ x ∈ spectrum ℝ A, (x + t) ≠ 0 := by @@ -266,188 +130,48 @@ lemma matrixFunction_inv_add_const {m : Type*} [Fintype m] [DecidableEq m] have hx' : x ∈ Set.range hA'.eigenvalues := by simpa [hA'.spectrum_real_eq_range_eigenvalues] using hx rcases hx' with ⟨i, rfl⟩ - have hx_nonneg : 0 ≤ hA.1.eigenvalues i := hA.eigenvalues_nonneg i - linarith - have hcfcinv : - cfc (fun x : ℝ => (x + t)⁻¹) A = Ring.inverse (cfc (fun x : ℝ => x + t) A) := by + linarith [hA.eigenvalues_nonneg i] + have hcfcinv : cfc (fun x : ℝ => (x + t)⁻¹) A = Ring.inverse (cfc (fun x : ℝ => x + t) A) := by simpa using (cfc_inv (A := Matrix m m ℂ) (f := fun x : ℝ => x + t) (a := A) hneq) - have hcfcaff : cfc (fun x : ℝ => x + t) A = A + (t : ℂ) • 1 := by - have h := matrixFunction_add_const (m := m) hA' t - calc - cfc (fun x : ℝ => x + t) A = - matrixFunction (fun x => ((x + t : ℝ) : ℂ)) A hA' := by - simpa using (matrixFunction_eq_cfc hA' (fun x => x + t)).symm - _ = A + (t : ℂ) • 1 := h + have hcfcaff : cfc (fun x : ℝ => x + t) A = A + (t : ℂ) • 1 := cfc_add_const_eq hA' t have hposdef : (A + (t : ℂ) • 1).PosDef := PosSemidef.add_smul_one_posDef hA ht have hunit : IsUnit (A + (t : ℂ) • 1) := hposdef.isUnit let _ := hunit.invertible have hcfcaff_inv : Ring.inverse (cfc (fun x : ℝ => x + t) A) = (A + (t : ℂ) • 1)⁻¹ := by simpa [hcfcaff] using (Ring.inverse_unit hunit.unit) - have hmf : matrixFunction (fun x => ((x + t : ℝ) : ℂ)⁻¹) A hA.1 = - cfc (fun x : ℝ => (x + t)⁻¹) A := by - simpa using (matrixFunction_eq_cfc hA' (fun x => (x + t)⁻¹)) - rw [hmf, hcfcinv, hcfcaff_inv] + rw [hcfcinv, hcfcaff_inv] -/-- `matrixFunction` agrees with `CFC.rpow` for PSD matrices. -/ -lemma matrixFunction_rpow_eq {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.PosSemidef) (s : ℝ) : - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 = A ^ s := by - have hA0 : 0 ≤ A := by - simpa [Matrix.le_iff] using hA - calc - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 = cfc (fun x : ℝ => x ^ s) A := by - simpa using (matrixFunction_eq_cfc hA.1 (fun x => x ^ s)) - _ = A ^ s := by - symm - exact CFC.rpow_eq_cfc_real (A := Matrix m m ℂ) (a := A) (y := s) (ha := hA0) - -/-- Resolvent form for `matrixFunction` on PSD matrices. -/ -lemma matrixFunction_resolvent {m : Type*} [Fintype m] [DecidableEq m] +/-- Resolvent form for `cfc` on PSD matrices: +`cfc (1 - r·(· + r)⁻¹) A = I - r·(A + r·I)⁻¹` for `r > 0`. -/ +lemma cfc_resolvent {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosSemidef) {r : ℝ} (hr : 0 < r) : - matrixFunction (fun x => ((1 - r * (x + r)⁻¹ : ℝ) : ℂ)) A hA.1 = + cfc (fun x => 1 - r * (x + r)⁻¹) A = (1 : Matrix m m ℂ) - (r : ℂ) • (A + (r : ℂ) • 1)⁻¹ := by - have hfun_inv : (fun x : ℝ => ((x : ℂ) + (r : ℂ))⁻¹) = - (fun x : ℝ => ((x + r : ℝ) : ℂ)⁻¹) := by - funext x - simp - have hfun : (fun x : ℝ => ((1 - r * (x + r)⁻¹ : ℝ) : ℂ)) = - (fun x : ℝ => (1 : ℂ) + -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) := by - funext x - push_cast - ring - have hconst : matrixFunction (fun _ => (1 : ℂ)) A hA.1 = (1 : Matrix m m ℂ) := by - simpa using (matrixFunction_const (m := m) hA.1 (1 : ℂ)) - have hinv : matrixFunction (fun x => ((x + r : ℝ) : ℂ)⁻¹) A hA.1 = - (A + (r : ℂ) • 1)⁻¹ := by - exact matrixFunction_inv_add_const (m := m) hA hr - calc - matrixFunction (fun x => ((1 - r * (x + r)⁻¹ : ℝ) : ℂ)) A hA.1 = - matrixFunction (fun x => (1 : ℂ) + -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) A hA.1 := by - rw [hfun] - _ = matrixFunction (fun _ => (1 : ℂ)) A hA.1 + - matrixFunction (fun x => -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) A hA.1 := by - simpa using (matrixFunction_add hA.1 (fun _ => (1 : ℂ)) - (fun x => -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹))) - _ = (1 : Matrix m m ℂ) + - -((r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1) := by - have hsmul : - matrixFunction (fun x => (r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹) A hA.1 = - (r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1 := by - simpa using - (matrixFunction_smul hA.1 (r : ℂ) (fun x => ((x : ℂ) + (r : ℂ))⁻¹)) - have hneg : - matrixFunction (fun x => -((r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) A hA.1 = - -((r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1) := by - simpa [hsmul] using - (matrixFunction_neg hA.1 (fun x => (r : ℂ) * ((x : ℂ) + (r : ℂ))⁻¹)) - simp [hconst, hneg] - _ = (1 : Matrix m m ℂ) - (r : ℂ) • (A + (r : ℂ) • 1)⁻¹ := by - calc - (1 : Matrix m m ℂ) + - -((r : ℂ) • matrixFunction (fun x => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1) = - (1 : Matrix m m ℂ) + -((r : ℂ) • (A + (r : ℂ) • 1)⁻¹) := by - have hinv' : - matrixFunction (fun x : ℝ => ((x : ℂ) + (r : ℂ))⁻¹) A hA.1 = - (A + (r : ℂ) • 1)⁻¹ := by - simpa [hfun_inv] using hinv - simp [hinv'] - _ = (1 : Matrix m m ℂ) - (r : ℂ) • (A + (r : ℂ) • 1)⁻¹ := by - simp [sub_eq_add_neg] - -/-- matrixFunction preserves positive semidefiniteness when f maps nonneg eigenvalues to nonneg. -/ -lemma matrixFunction_posSemidef {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.PosSemidef) - (f : ℝ → ℝ) (hf : ∀ i, 0 ≤ f (hA.1.eigenvalues i)) : - (matrixFunction (fun x => (f x : ℂ)) A hA.1).PosSemidef := by - unfold matrixFunction - have hD : (diagonal (fun i => (f (hA.1.eigenvalues i) : ℂ))).PosSemidef := - PosSemidef.diagonal_ofReal hf - have key := hD.conjTranspose_mul_mul_same ((hA.1.eigenvectorUnitary : Matrix m m ℂ)ᴴ) - simp only [conjTranspose_conjTranspose] at key - exact key - -/-- matrixFunction (f - c) = matrixFunction f - c • I -/ -lemma matrixFunction_sub_const {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) (c : ℝ) : - matrixFunction (fun x => (f x - c : ℂ)) A hA = matrixFunction (fun x => (f x : ℂ)) A hA - (c : ℂ) • 1 := by - classical - let U : Matrix m m ℂ := hA.eigenvectorUnitary - have hU : U * Uᴴ = 1 := by - simpa [U, star_eq_conjTranspose] using Unitary.coe_mul_star_self hA.eigenvectorUnitary - have hdiag : - diagonal (fun i => (f (hA.eigenvalues i) - c : ℂ)) = - diagonal (fun i => (f (hA.eigenvalues i) : ℂ)) - diagonal (fun _ => (c : ℂ)) := by - ext i j - by_cases h : i = j - · subst h - simp - · simp [h] - have hdiagc : diagonal (fun _ => (c : ℂ)) = (c : ℂ) • (1 : Matrix m m ℂ) := by - ext i j - by_cases h : i = j - · subst h - simp - · simp [h] - unfold matrixFunction - rw [hdiag] - simp only [Matrix.mul_sub, Matrix.sub_mul] - rw [hdiagc] - simp only [Matrix.mul_smul, Matrix.smul_mul, Matrix.mul_one] - rw [hU] - -/-- Product of two matrixFunctions is the matrixFunction of the pointwise product. -Since both share the eigenbasis U, f(A) g(A) = U diag(f(λ) · g(λ)) U*. -/ -lemma matrixFunction_mul {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) (f g : ℝ → ℂ) : - matrixFunction f A hA * matrixFunction g A hA = - matrixFunction (fun x => f x * g x) A hA := by - unfold matrixFunction - set U : Matrix m m ℂ := (hA.eigenvectorUnitary : Matrix m m ℂ) with hU_def - have hUU : Uᴴ * U = 1 := by - simpa [star_eq_conjTranspose] using Unitary.coe_star_mul_self hA.eigenvectorUnitary - simp only [hU_def, Matrix.mul_assoc] + have hA' : A.IsHermitian := hA.1 + have hneq : ∀ x ∈ spectrum ℝ A, (x + r) ≠ 0 := by + intro x hx + have hx' : x ∈ Set.range hA'.eigenvalues := by + simpa [hA'.spectrum_real_eq_range_eigenvalues] using hx + rcases hx' with ⟨i, rfl⟩ + linarith [hA.eigenvalues_nonneg i] + have hcont_inv : ContinuousOn (fun x : ℝ => (x + r)⁻¹) (spectrum ℝ A) := + ContinuousOn.inv₀ (by fun_prop) hneq + have hcontc : ContinuousOn (fun _ : ℝ => (1 : ℝ)) (spectrum ℝ A) := continuousOn_const + have hcontri : ContinuousOn (fun x : ℝ => r * (x + r)⁻¹) (spectrum ℝ A) := + continuousOn_const.mul hcont_inv + rw [cfc_sub (fun _ : ℝ => (1 : ℝ)) (fun x => r * (x + r)⁻¹) A hcontc hcontri, + cfc_const (1 : ℝ) A, map_one, + cfc_const_mul r (fun x : ℝ => (x + r)⁻¹) A hcont_inv, + cfc_inv_add_const hA hr] congr 1 - rw [← Matrix.mul_assoc Uᴴ U, hUU, Matrix.one_mul, ← Matrix.mul_assoc, diagonal_mul_diagonal] - -/-- Tr(A · f(A)) = ∑ᵢ λᵢ · f(λᵢ). -We first rewrite A as id(A) via `matrixFunction_id`, then apply -`matrixFunction_mul` and `matrixFunction_trace`. -/ -lemma trace_mul_matrixFunction {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) (f : ℝ → ℂ) : - (A * matrixFunction f A hA).trace = - ∑ i, ((hA.eigenvalues i : ℂ) * f (hA.eigenvalues i)) := by - suffices h : (matrixFunction (fun x => (x : ℂ)) A hA * - matrixFunction f A hA).trace = - ∑ i, ((hA.eigenvalues i : ℂ) * f (hA.eigenvalues i)) by - rwa [matrixFunction_id] at h - rw [matrixFunction_mul, matrixFunction_trace] -/-- A Hermitian matrix commutes with any matrixFunction of itself. -This follows from `matrixFunction_id` (A = id(A)) and `matrixFunction_mul` -(id(A) · f(A) = f(A) · id(A) by pointwise commutativity of multiplication). -/ -lemma commute_matrixFunction_self {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℂ) : - Commute A (matrixFunction f A hA) := by - have hlhs : A * matrixFunction f A hA = - matrixFunction (fun x => (x : ℂ) * f x) A hA := by - have := matrixFunction_mul hA (fun x => (x : ℂ)) f - rwa [matrixFunction_id] at this - have hrhs : matrixFunction f A hA * A = - matrixFunction (fun x => f x * (x : ℂ)) A hA := by - have := matrixFunction_mul hA f (fun x => (x : ℂ)) - rwa [matrixFunction_id] at this - change A * matrixFunction f A hA = matrixFunction f A hA * A - rw [hlhs, hrhs] - congr 1; ext x; ring - -/-- The product A · f(A) is Hermitian when A is Hermitian and f : ℝ → ℝ. -Since A and f(A) share the same eigenbasis, they commute; both are Hermitian, -so their product is Hermitian by `IsHermitian.commute_iff`. -/ -lemma mul_matrixFunction_isHermitian {m : Type*} [Fintype m] [DecidableEq m] +/-- The product `A · f(A)` is Hermitian for Hermitian `A` and real `f`. -/ +lemma mul_cfc_isHermitian {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.IsHermitian) (f : ℝ → ℝ) : - (A * matrixFunction (fun x => (f x : ℂ)) A hA).IsHermitian := - (hA.commute_iff (matrixFunction_isHermitian hA f)).mp - (commute_matrixFunction_self hA _) + (A * cfc f A).IsHermitian := by + have hsa : IsSelfAdjoint A := hA + have hcomm : Commute A (cfc f A) := (hsa.commute_cfc (Commute.refl A) f).symm + exact (hA.commute_iff (cfc_isHermitian hA f)).mp hcomm /-- The trace of a Hermitian matrix is real: casting its real part back to ℂ recovers the trace. Proof: Aᴴ = A implies star(Tr A) = Tr(Aᴴ) = Tr A, so Tr A is self-adjoint, @@ -460,58 +184,22 @@ lemma IsHermitian.trace_ofReal_re {m : Type*} [Fintype m] rw [← trace_conjTranspose, hA.eq] exact (RCLike.conj_eq_iff_re (K := ℂ)).mp h -/-- Matrix exponential for Hermitian matrices via spectral decomposition. -/ -noncomputable def matrixExp {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) : Matrix m m ℂ := - matrixFunction (fun x => Real.exp x) A hA - -/-- Matrix logarithm for positive definite matrices via spectral decomposition. -/ -noncomputable def matrixLog {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) : Matrix m m ℂ := - matrixFunction (fun x => Real.log x) A hA - -/-- `matrixLog` expands into the spectral decomposition attached to the chosen -Hermitian proof. -/ -lemma matrixLog_spectral_eq {m : Type*} [Fintype m] [DecidableEq m] +/-- `cfc Real.log` of a Hermitian matrix (the matrix logarithm) expands into the spectral +decomposition attached to the chosen Hermitian proof. -/ +lemma cfc_log_spectral_eq {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.IsHermitian) : - matrixLog A hA = + cfc Real.log A = (hA.eigenvectorUnitary : Matrix m m ℂ) * diagonal (fun i => ((Real.log (hA.eigenvalues i) : ℝ) : ℂ)) * - (hA.eigenvectorUnitary : Matrix m m ℂ)ᴴ := by - unfold matrixLog matrixFunction - rfl + (hA.eigenvectorUnitary : Matrix m m ℂ)ᴴ := + cfc_spectral_eq hA Real.log -/-- Trace of matrix exponential equals sum of exp of eigenvalues. -/ -lemma matrixExp_trace {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) : - (matrixExp A hA).trace = ∑ i, (Real.exp (hA.eigenvalues i) : ℂ) := by - unfold matrixExp - rw [matrixFunction_trace] - -/-- Trace of matrix logarithm equals sum of log of eigenvalues. -/ -lemma matrixLog_trace {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) : - (matrixLog A hA).trace = ∑ i, (Real.log (hA.eigenvalues i) : ℂ) := by - unfold matrixLog - rw [matrixFunction_trace] - -/-- Matrix logarithm of a Hermitian matrix is Hermitian. -/ -lemma matrixLog_isHermitian {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) : - (matrixLog A hA).IsHermitian := by - unfold matrixLog matrixFunction IsHermitian - simp only [conjTranspose_mul, conjTranspose_conjTranspose] - have hDiag := IsHermitian.diagonal_real (fun i => Real.log (hA.eigenvalues i)) - rw [IsHermitian] at hDiag - rw [hDiag, Matrix.mul_assoc] - -/-- The matrix logarithm commutes with any `*-`algebra equivalence between complex matrix -algebras on PosDef matrices. Continuity is automatic in finite dimensions. -/ -theorem matrixLog_map_starAlgEquiv {m n : Type*} [Fintype m] [DecidableEq m] +/-- The matrix logarithm `cfc Real.log` commutes with any `*-`algebra equivalence between +complex matrix algebras on PosDef matrices. Continuity is automatic in finite dimensions. -/ +theorem cfc_log_map_starAlgEquiv {m n : Type*} [Fintype m] [DecidableEq m] [Fintype n] [DecidableEq n] {M : Matrix m m ℂ} (hM : M.PosDef) (φ : Matrix m m ℂ ≃⋆ₐ[ℂ] Matrix n n ℂ) : - matrixLog (φ M) ((hM.posSemidef.map_starAlgEquiv φ).isHermitian) = - φ (matrixLog M hM.1) := by + cfc Real.log (φ M) = φ (cfc Real.log M) := by letI : NormedRing (Matrix m m ℂ) := Matrix.linftyOpNormedRing letI : NormedAlgebra ℝ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra letI : NormedAlgebra ℂ (Matrix m m ℂ) := Matrix.linftyOpNormedAlgebra @@ -522,8 +210,6 @@ theorem matrixLog_map_starAlgEquiv {m n : Type*} [Fintype m] [DecidableEq m] letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra letI : CStarAlgebra (Matrix n n ℂ) := by simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) - unfold matrixLog - rw [matrixFunction_eq_cfc, matrixFunction_eq_cfc] -- View `φ` as an ℝ-`StarAlgHom` to apply `StarAlgHomClass.map_cfc`. let ψ : Matrix m m ℂ →⋆ₐ[ℝ] Matrix n n ℂ := { toAlgHom := (φ.toAlgEquiv.restrictScalars ℝ).toAlgHom @@ -558,24 +244,30 @@ theorem matrixLog_map_starAlgEquiv {m n : Type*} [Fintype m] [DecidableEq m] rw [h_ψ_apply, h_ψ_apply] at h_map exact h_map.symm -/-- Matrix inverse square root via functional calculus for PD matrices. -/ +/-- Matrix inverse square root via the continuous functional calculus for PD matrices. -/ noncomputable def matrixInvSqrt {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.PosDef) : Matrix m m ℂ := - matrixFunction (fun x => (Real.rpow x (-1 / 2 : ℝ) : ℂ)) A hA.1 + (A : Matrix m m ℂ) (_hA : A.PosDef) : Matrix m m ℂ := + cfc (fun x => Real.rpow x (-1 / 2 : ℝ)) A + +/-- `matrixInvSqrt A = A ^ (-1/2)` via `CFC.rpow`. -/ +lemma matrixInvSqrt_eq_rpow {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosDef) : + matrixInvSqrt A hA = A ^ (-1 / 2 : ℝ) := + (CFC.rpow_eq_cfc_real (a := A) (ha := by rw [Matrix.le_iff, sub_zero]; exact hA.posSemidef)).symm /-- The matrix inverse square root of a PD matrix is Hermitian. -/ lemma matrixInvSqrt_isHermitian {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosDef) : (matrixInvSqrt A hA).IsHermitian := by - unfold matrixInvSqrt - exact matrixFunction_isHermitian hA.1 (fun x => Real.rpow x (-1 / 2 : ℝ)) + rw [matrixInvSqrt] + exact cfc_isHermitian hA.1 (fun x => Real.rpow x (-1 / 2 : ℝ)) /-- For a positive definite matrix `A`, `A^{-1/2} * A * A^{-1/2} = I`. -/ lemma matrixInvSqrt_mul_self {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosDef) : matrixInvSqrt A hA * A * matrixInvSqrt A hA = 1 := by have hS : matrixInvSqrt A hA = A ^ (-1 / 2 : ℝ) := by - simpa [matrixInvSqrt] using (matrixFunction_rpow_eq hA.posSemidef (-1 / 2 : ℝ)) + exact matrixInvSqrt_eq_rpow hA have hAunit : IsUnit A := hA.isUnit have hnonneg : 0 ≤ A := by simpa [Matrix.le_iff] using hA.posSemidef @@ -599,17 +291,23 @@ lemma matrixInvSqrt_mul_self {m : Type*} [Fintype m] [DecidableEq m] ring_nf simpa using (CFC.rpow_zero (a := A) hnonneg) -/-- Matrix square root via functional calculus for PSD matrices. -/ +/-- Matrix square root via the continuous functional calculus for PSD matrices. -/ noncomputable def matrixSqrt {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.PosSemidef) : Matrix m m ℂ := - matrixFunction (fun x => (Real.rpow x (1 / 2 : ℝ) : ℂ)) A hA.1 + (A : Matrix m m ℂ) (_hA : A.PosSemidef) : Matrix m m ℂ := + cfc (fun x => Real.rpow x (1 / 2 : ℝ)) A + +/-- `matrixSqrt A = A ^ (1/2)` via `CFC.rpow`. -/ +lemma matrixSqrt_eq_rpow {m : Type*} [Fintype m] [DecidableEq m] + {A : Matrix m m ℂ} (hA : A.PosSemidef) : + matrixSqrt A hA = A ^ (1 / 2 : ℝ) := + (CFC.rpow_eq_cfc_real (a := A) (ha := by rw [Matrix.le_iff, sub_zero]; exact hA)).symm /-- The matrix square root of a PSD matrix is Hermitian. -/ lemma matrixSqrt_isHermitian {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosSemidef) : (matrixSqrt A hA).IsHermitian := by - unfold matrixSqrt - exact matrixFunction_isHermitian hA.1 (fun x => Real.rpow x (1 / 2 : ℝ)) + rw [matrixSqrt] + exact cfc_isHermitian hA.1 (fun x => Real.rpow x (1 / 2 : ℝ)) /-- For a positive semidefinite matrix `A`, `A^{1/2} * A^{1/2} = A`. -/ lemma matrixSqrt_mul_self_posSemidef {m : Type*} [Fintype m] [DecidableEq m] @@ -617,7 +315,7 @@ lemma matrixSqrt_mul_self_posSemidef {m : Type*} [Fintype m] [DecidableEq m] matrixSqrt A hA * matrixSqrt A hA = A := by classical -- Use the spectral decomposition and diagonal computation. - unfold matrixSqrt matrixFunction + rw [matrixSqrt, cfc_spectral_eq hA.1 (fun x => Real.rpow x (1 / 2 : ℝ))] set U : Matrix m m ℂ := (hA.1.eigenvectorUnitary : Matrix m m ℂ) set D : Matrix m m ℂ := diagonal (fun i => (Real.rpow (hA.1.eigenvalues i) (1 / 2 : ℝ) : ℂ)) @@ -650,7 +348,7 @@ lemma matrixSqrt_mul_self {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosDef) : matrixSqrt A hA.posSemidef * matrixSqrt A hA.posSemidef = A := by have hS : matrixSqrt A hA.posSemidef = A ^ (1 / 2 : ℝ) := by - simpa [matrixSqrt] using (matrixFunction_rpow_eq hA.posSemidef (1 / 2 : ℝ)) + exact matrixSqrt_eq_rpow hA.posSemidef have hAunit : IsUnit A := hA.isUnit calc matrixSqrt A hA.posSemidef * matrixSqrt A hA.posSemidef = @@ -670,9 +368,9 @@ lemma matrixSqrt_mul_matrixInvSqrt {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosDef) : matrixSqrt A hA.posSemidef * matrixInvSqrt A hA = 1 := by have hS : matrixSqrt A hA.posSemidef = A ^ (1 / 2 : ℝ) := by - simpa [matrixSqrt] using (matrixFunction_rpow_eq hA.posSemidef (1 / 2 : ℝ)) + exact matrixSqrt_eq_rpow hA.posSemidef have hSi : matrixInvSqrt A hA = A ^ (-1 / 2 : ℝ) := by - simpa [matrixInvSqrt] using (matrixFunction_rpow_eq hA.posSemidef (-1 / 2 : ℝ)) + exact matrixInvSqrt_eq_rpow hA have hAunit : IsUnit A := hA.isUnit have hnonneg : 0 ≤ A := by simpa [Matrix.le_iff] using hA.posSemidef @@ -692,9 +390,9 @@ lemma matrixInvSqrt_mul_matrixSqrt {m : Type*} [Fintype m] [DecidableEq m] {A : Matrix m m ℂ} (hA : A.PosDef) : matrixInvSqrt A hA * matrixSqrt A hA.posSemidef = 1 := by have hS : matrixSqrt A hA.posSemidef = A ^ (1 / 2 : ℝ) := by - simpa [matrixSqrt] using (matrixFunction_rpow_eq hA.posSemidef (1 / 2 : ℝ)) + exact matrixSqrt_eq_rpow hA.posSemidef have hSi : matrixInvSqrt A hA = A ^ (-1 / 2 : ℝ) := by - simpa [matrixInvSqrt] using (matrixFunction_rpow_eq hA.posSemidef (-1 / 2 : ℝ)) + exact matrixInvSqrt_eq_rpow hA have hAunit : IsUnit A := hA.isUnit have hnonneg : 0 ≤ A := by simpa [Matrix.le_iff] using hA.posSemidef @@ -711,7 +409,7 @@ lemma matrixInvSqrt_mul_matrixSqrt {m : Type*} [Fintype m] [DecidableEq m] /-- For commuting PSD L and PD R, matrixInvSqrt R commutes with L. This follows from the fact that L commutes with R, and CFC (hence rpow) preserves -commutativity. Since matrixInvSqrt R = R^{-1/2} (by matrixFunction_rpow_eq), and +commutativity. Since matrixInvSqrt R = R^{-1/2} (by CFC.rpow), and Commute.cfc_real gives that cfc g R commutes with L when L commutes with R, the result follows. -/ lemma matrixInvSqrt_commute_of_commute {n : Type*} [Fintype n] [DecidableEq n] @@ -723,8 +421,7 @@ lemma matrixInvSqrt_commute_of_commute {n : Type*} [Fintype n] [DecidableEq n] letI : NormedAlgebra ℂ (Matrix n n ℂ) := Matrix.linftyOpNormedAlgebra letI : CStarAlgebra (Matrix n n ℂ) := by simpa [CStarMatrix] using CStarMatrix.instCStarAlgebra (n := n) (A := ℂ) - have hRinv_eq : matrixInvSqrt R hR = R ^ (-1 / 2 : ℝ) := by - simpa [matrixInvSqrt] using matrixFunction_rpow_eq hR.posSemidef (-1 / 2 : ℝ) + have hRinv_eq : matrixInvSqrt R hR = R ^ (-1 / 2 : ℝ) := matrixInvSqrt_eq_rpow hR rw [hRinv_eq] -- R^{-1/2} = cfc(x^{-1/2}, R), so it commutes with L since L commutes with R have hR0 : (0 : Matrix n n ℂ) ≤ R := by simpa [Matrix.le_iff] using hR.posSemidef @@ -732,45 +429,6 @@ lemma matrixInvSqrt_commute_of_commute {n : Type*} [Fintype n] [DecidableEq n] have hcommute : Commute R L := hcomm.symm exact Commute.cfc_real hcommute _ -/-- For a PSD matrix `A`, `(A^{1/2})ᴴ * A^{1/2} = A` (since the square root is Hermitian). -/ -lemma matrixSqrt_conjTranspose_mul_self_posSemidef {m : Type*} [Fintype m] [DecidableEq m] - {A : Matrix m m ℂ} (hA : A.PosSemidef) : - (matrixSqrt A hA)ᴴ * matrixSqrt A hA = A := by - have hherm : (matrixSqrt A hA).IsHermitian := matrixSqrt_isHermitian hA - calc - (matrixSqrt A hA)ᴴ * matrixSqrt A hA = matrixSqrt A hA * matrixSqrt A hA := by - simp [hherm.eq] - _ = A := matrixSqrt_mul_self_posSemidef hA - -/-- At p = 0: `matrixFunction (fun x => x ^ 0) A = I`. -/ -lemma matrixFunction_cpow_zero {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) : - matrixFunction (fun x => x ^ (0 : ℂ)) A hA = 1 := by - unfold matrixFunction - have h_pow : (fun i => ((hA.eigenvalues i : ℝ) : ℂ) ^ (0 : ℂ)) = (fun _ => 1) := by - ext i - simp [Complex.cpow_zero] - simp only [h_pow, Matrix.diagonal_one, Matrix.mul_one] - have h := Unitary.coe_mul_star_self hA.eigenvectorUnitary - simp only [Unitary.coe_star, star_eq_conjTranspose] at h - exact h - -/-- `matrixFunction (fun x => x ^ 1) A = A`. -/ -lemma matrixFunction_cpow_one {m : Type*} [Fintype m] [DecidableEq m] - (A : Matrix m m ℂ) (hA : A.IsHermitian) : - matrixFunction (fun x => x ^ (1 : ℂ)) A hA = A := by - unfold matrixFunction - have h_pow : (fun i => ((hA.eigenvalues i : ℝ) : ℂ) ^ (1 : ℂ)) = - (fun i => ((hA.eigenvalues i : ℝ) : ℂ)) := by - ext i - simp [Complex.cpow_one] - have h_diag : diagonal (fun i => ((hA.eigenvalues i : ℝ) : ℂ)) = - diagonal (RCLike.ofReal ∘ hA.eigenvalues) := rfl - simp only [h_pow, h_diag] - have h_spec := hA.spectral_theorem - rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at h_spec - exact h_spec.symm - /-- CFC commutes with unitary conjugation using `Unitary.conjStarAlgAut`. -/ lemma cfc_unitary_conjugation' {m : Type*} [Fintype m] [DecidableEq m] (U : unitary (Matrix m m ℂ)) (M : Matrix m m ℂ) @@ -944,7 +602,7 @@ lemma compression_pow_eq {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [ _ = Vᴴ * M ^ k * V * (Vᴴ * M * V) := by simp only [Matrix.mul_assoc] _ = (Vᴴ * M * V) ^ k * (Vᴴ * M * V) := by rw [ih] --- Helper: matrixFunction f M can be expressed as a polynomial in M +-- Helper: cfc f M can be expressed as a polynomial in M -- (specifically, the Lagrange interpolant at the eigenvalues). -- Hence V†f(M)V = f(V†MV) when V†M^kV = (V†MV)^k. @@ -1058,14 +716,15 @@ lemma eigenvalues_compression_subset {n m : Type*} use j exact Complex.ofReal_injective hr -lemma matrixFunction_compression_of_commuting {n m : Type*} +/-- Compression commutes with the continuous functional calculus when `Vᴴ V = 1` and `M` +commutes with `V Vᴴ`: `Vᴴ · f(M) · V = f(Vᴴ M V)`. -/ +lemma cfc_compression_of_commuting {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] (V : Matrix n m ℂ) (M : Matrix n n ℂ) (hM : M.IsHermitian) (hVV : Vᴴ * V = (1 : Matrix m m ℂ)) (hcomm : M * (V * Vᴴ) = V * Vᴴ * M) (f : ℝ → ℝ) (hVM : (Vᴴ * M * V).IsHermitian) : - Vᴴ * matrixFunction (fun x => (f x : ℂ)) M hM * V = - matrixFunction (fun x => (f x : ℂ)) (Vᴴ * M * V) hVM := by + Vᴴ * cfc f M * V = cfc f (Vᴴ * M * V) := by classical -- The key insight: eigenvalues of V†MV are among eigenvalues of M have h_eig_subset := eigenvalues_compression_subset V M hM hVV hcomm hVM @@ -1107,11 +766,6 @@ lemma matrixFunction_compression_of_commuting {n m : Type*} rw [h1, ← Polynomial.aeval_def, Polynomial.aeval_algebraMap_apply_eq_algebraMap_eval, hp_interp_VM i] rfl - unfold matrixFunction - have h_mf_M_eq_cfc : matrixFunction (fun x => (f x : ℂ)) M hM = cfc f M := - matrixFunction_eq_cfc hM f - have h_mf_VM_eq_cfc : matrixFunction (fun x => (f x : ℂ)) (Vᴴ * M * V) hVM = cfc f (Vᴴ * M * V) := - matrixFunction_eq_cfc hVM f have h_cfc_M : cfc f M = hM.cfc f := Matrix.IsHermitian.cfc_eq hM f have h_cfc_VM : cfc f (Vᴴ * M * V) = hVM.cfc f := Matrix.IsHermitian.cfc_eq hVM f have h_cfc_f_eq_p_M : hM.cfc f = hM.cfc (fun x => p.eval x) := by @@ -1147,9 +801,8 @@ lemma matrixFunction_compression_of_commuting {n m : Type*} simp only [p_complex, Polynomial.aeval_map_algebraMap] rw [h1, h2] exact compression_aeval_eq V M hVV hcomm p_complex - calc Vᴴ * matrixFunction (fun x => (f x : ℂ)) M hM * V - = Vᴴ * cfc f M * V := by rw [h_mf_M_eq_cfc] - _ = Vᴴ * hM.cfc f * V := by rw [h_cfc_M] + calc Vᴴ * cfc f M * V + = Vᴴ * hM.cfc f * V := by rw [h_cfc_M] _ = Vᴴ * hM.cfc (fun x => p.eval x) * V := by rw [h_cfc_f_eq_p_M] _ = Vᴴ * cfc (fun x => p.eval x) M * V := by rw [← Matrix.IsHermitian.cfc_eq hM] _ = Vᴴ * Polynomial.aeval M p * V := by rw [h_cfc_p_eq_aeval_M] @@ -1158,11 +811,10 @@ lemma matrixFunction_compression_of_commuting {n m : Type*} _ = hVM.cfc (fun x => p.eval x) := by rw [Matrix.IsHermitian.cfc_eq hVM] _ = hVM.cfc f := by rw [← h_cfc_f_eq_p_VM] _ = cfc f (Vᴴ * M * V) := by rw [← h_cfc_VM] - _ = matrixFunction (fun x => (f x : ℂ)) (Vᴴ * M * V) hVM := by rw [← h_mf_VM_eq_cfc] /-- For an isometry V (V†V = I), PSD A, and s > 0: (VAV†)^s = V A^s V†. -**Proof**: Uses `matrixFunction_compression_of_commuting` to get V†(VAV†)^s V = A^s, +**Proof**: Uses `cfc_compression_of_commuting` to get V†(VAV†)^s V = A^s, then shows (VAV†)^s annihilates the complement (1 - VV†) via kernel preservation. -/ lemma rpow_conj_isometry {n m : Type*} [Fintype n] [Fintype m] [DecidableEq n] [DecidableEq m] @@ -1191,14 +843,11 @@ lemma rpow_conj_isometry {n m : Type*} [Fintype n] [Fintype m] -- Step 3: V†(M^s)V = A^s have hVM_herm : (Vᴴ * M * V).IsHermitian := by rw [hstep1]; exact hA.1 have hVMA_rpow : Vᴴ * (M ^ s) * V = A ^ s := by - have h1 := matrixFunction_rpow_eq hM_psd s - have h2 := matrixFunction_compression_of_commuting V M hM_psd.1 hV hcomm (· ^ s) hVM_herm - rw [h1] at h2 - rw [h2] - have h3 : matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) (Vᴴ * M * V) hVM_herm = - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 := by - congr 1 - rw [h3, matrixFunction_rpow_eq hA] + have hM0 : (0 : Matrix m m ℂ) ≤ M := by rw [Matrix.le_iff, sub_zero]; exact hM_psd + have hA0 : (0 : Matrix n n ℂ) ≤ A := by rw [Matrix.le_iff, sub_zero]; exact hA + rw [CFC.rpow_eq_cfc_real (a := M) (ha := hM0), + cfc_compression_of_commuting V M hM_psd.1 hV hcomm (fun x : ℝ => x ^ s) hVM_herm, + hstep1, ← CFC.rpow_eq_cfc_real (a := A) (ha := hA0)] -- Step 4: M * (1 - P) = 0 have hM_annihilate : M * (1 - P) = 0 := by rw [mul_sub, mul_one, hM_def, hP_def, @@ -1274,8 +923,8 @@ lemma rpow_conj_isometry {n m : Type*} [Fintype n] [Fintype m] have hP_herm : Pᴴ = P := by simp [hP_def, Matrix.conjTranspose_mul, conjTranspose_conjTranspose] have hMs_herm : (M ^ s).IsHermitian := by - rw [← matrixFunction_rpow_eq hM_psd] - exact matrixFunction_isHermitian hM_psd.1 (· ^ s) + rw [CFC.rpow_eq_cfc_real (a := M) (ha := by rw [Matrix.le_iff, sub_zero]; exact hM_psd)] + exact cfc_isHermitian hM_psd.1 (fun x : ℝ => x ^ s) -- M^s = M^s * P (from M^s*(1-P)=0) have hMsP_eq : M ^ s = M ^ s * P := by have h := hMs_annihilate @@ -1327,8 +976,8 @@ lemma spectral_expand (A : Matrix n n ℂ) (hA : A.IsHermitian) : A = (hA.eigenvectorUnitary : Matrix n n ℂ) * diagonal (fun i => (hA.eigenvalues i : ℂ)) * (hA.eigenvectorUnitary : Matrix n n ℂ)ᴴ := by - have h := (matrixFunction_id hA).symm - unfold matrixFunction at h + have h := hA.spectral_theorem + rw [Unitary.conjStarAlgAut_apply, star_eq_conjTranspose] at h simpa [Function.comp] using h /-- The j-th column of the eigenvector unitary satisfies the eigenvalue equation: diff --git a/QuantumSystem/Analysis/Matrix/LiebConcavity.lean b/QuantumSystem/Analysis/Matrix/LiebConcavity.lean index 46b111b..d82ce71 100644 --- a/QuantumSystem/Analysis/Matrix/LiebConcavity.lean +++ b/QuantumSystem/Analysis/Matrix/LiebConcavity.lean @@ -83,31 +83,6 @@ namespace QuantumInfo scoped notation "⟪" X ", " Y "⟫_HS" => Matrix.hsInnerProduct X Y end QuantumInfo -/-- Hilbert-Schmidt inner product is related to liebJointFunction via left/right multiplication. -For positive semidefinite A, B and real p: - ⟨A^p · K† · B^{1-p}, K†⟩_HS = Tr(A^p · K† · B^{1-p} · K) -This connects the operator-level perspective to the trace-level Lieb function. -/ -private lemma hsInnerProduct_leftMul_rightMul {m : Type*} [Fintype m] [DecidableEq m] - (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) - (K : Matrix m m ℂ) (p : ℝ) : - hsInnerProduct ((A ^ p) * Kᴴ * (B ^ (1 - p))) Kᴴ = liebJointFunction K p A hA B hB := by - simp only [hsInnerProduct, liebJointFunction] - -- (A^p * K† * B^{1-p})† = B^{1-p}† * K * (A^p)† - -- Since A^p and B^{1-p} are Hermitian (rpow of PSD is PSD hence Hermitian): - have hAp_herm : (A ^ p)ᴴ = A ^ p := by - rw [← matrixFunction_rpow_eq hA p] - exact matrixFunction_isHermitian hA.1 (fun x => x ^ p) - have hBp_herm : (B ^ (1 - p))ᴴ = B ^ (1 - p) := by - rw [← matrixFunction_rpow_eq hB (1 - p)] - exact matrixFunction_isHermitian hB.1 (fun x => x ^ (1 - p)) - simp only [Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, - hAp_herm, hBp_herm, Matrix.mul_assoc] - -- LHS: Tr(B^{1-p} * K * A^p * K†), RHS: Tr(A^p * K† * B^{1-p} * K) - -- By trace cyclicity (two shifts) these are equal - simp only [← Matrix.mul_assoc] - rw [trace_mul_cycle, trace_mul_cycle] - simp only [Matrix.mul_assoc] - /-- The HS inner product ⟨v, matrixPerspective(f, L_A, R_B) v⟩ for f(t) = -t^p and v = vec(K†) equals -Tr(A^p K† B^{1-p} K). @@ -123,8 +98,8 @@ The proof is technical but the key insight is: - Combining: ⟨v, (-L^p R^{1-p}) v⟩ = -Tr(A^p K† B^{1-p} K) = -liebJointFunction For full generality this requires functional calculus on Kronecker products, -but the result follows from the trace identity hsInnerProduct_leftMul_rightMul -and the perspective structure. -/ +but the result follows from the underlying trace identity and the perspective +structure. -/ private lemma matrixPerspective_inner_eq_neg_liebJointFunction {m : Type*} [Fintype m] [DecidableEq m] (K : Matrix m m ℂ) (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) (A B : Matrix m m ℂ) (hA : A.PosDef) (hB : B.PosDef) @@ -181,7 +156,8 @@ private lemma lieb_concavity_effros {m : Type*} [Fintype m] [DecidableEq m] w₂ * (liebJointFunction K p A₂ hA₂.posSemidef B₂ hB₂.posSemidef).re ≤ (liebJointFunction K p (w₁ • A₁ + w₂ • A₂) ((hA₁.posSemidef.real_smul hw₁).add (hA₂.posSemidef.real_smul hw₂)) - (w₁ • B₁ + w₂ • B₂) ((hB₁.posSemidef.real_smul hw₁).add (hB₂.posSemidef.real_smul hw₂))).re := by + (w₁ • B₁ + w₂ • B₂) + ((hB₁.posSemidef.real_smul hw₁).add (hB₂.posSemidef.real_smul hw₂))).re := by classical /- Proof by Effros's Matrix Perspective Approach -/ -- 1. Setup the function f(x) = -x^p, which is Matrix Convex. @@ -226,12 +202,16 @@ private lemma lieb_concavity_effros {m : Type*} [Fintype m] [DecidableEq m] simp only [dotProduct_sub, dotProduct_add] at h_vec_nonneg -- Connect to Lieb function -- Use the spectral identity: ⟨v, matrixPerspective(f, L_A, R_B) v⟩ = -liebJointFunction(K, p, A, B) - have h_ident1 : (star v ⬝ᵥ (term1 *ᵥ v)).re = -(liebJointFunction K p A₁ hA₁.posSemidef B₁ hB₁.posSemidef).re := by + have h_ident1 : (star v ⬝ᵥ (term1 *ᵥ v)).re = + -(liebJointFunction K p A₁ hA₁.posSemidef B₁ hB₁.posSemidef).re := by simpa [term1, f] using - matrixPerspective_inner_eq_neg_liebJointFunction K p (le_of_lt hp0) (le_of_lt hp1) A₁ B₁ hA₁ hB₁ hL₁_psd hR₁_pd - have h_ident2 : (star v ⬝ᵥ (term2 *ᵥ v)).re = -(liebJointFunction K p A₂ hA₂.posSemidef B₂ hB₂.posSemidef).re := by + matrixPerspective_inner_eq_neg_liebJointFunction K p (le_of_lt hp0) (le_of_lt hp1) A₁ B₁ + hA₁ hB₁ hL₁_psd hR₁_pd + have h_ident2 : (star v ⬝ᵥ (term2 *ᵥ v)).re = + -(liebJointFunction K p A₂ hA₂.posSemidef B₂ hB₂.posSemidef).re := by simpa [term2, f] using - matrixPerspective_inner_eq_neg_liebJointFunction K p (le_of_lt hp0) (le_of_lt hp1) A₂ B₂ hA₂ hB₂ hL₂_psd hR₂_pd + matrixPerspective_inner_eq_neg_liebJointFunction K p (le_of_lt hp0) (le_of_lt hp1) A₂ B₂ + hA₂ hB₂ hL₂_psd hR₂_pd have hA_comb : (w₁ • A₁ + w₂ • A₂).PosDef := PosDef.convex_comb_nonneg hA₁ hA₂ hw₁ hw₂ hw have hB_comb : (w₁ • B₁ + w₂ • B₂).PosDef := PosDef.convex_comb_nonneg hB₁ hB₂ hw₁ hw₂ hw -- The combined identity follows from matrixPerspective_inner_eq_neg_liebJointFunction @@ -277,7 +257,8 @@ private lemma lieb_concavity_effros {m : Type*} [Fintype m] [DecidableEq m] -- After substitution, the goal matches h_apply. -- The matrices are definitionally equal after applying hLlin and hRlin. -- term_comb = matrixPerspective f L R _ _ - -- = matrixPerspective f (leftMulMatrix (w₁•A₁+w₂•A₂)) (rightMulMatrix (w₁•B₁+w₂•B₂)) _ _ + -- = matrixPerspective f (leftMulMatrix (w₁•A₁+w₂•A₂)) + -- (rightMulMatrix (w₁•B₁+w₂•B₂)) _ _ -- The proof terms may differ but the matrices are equal by proof irrelevance. -- Since both sides compute the same quadratic form value, they are equal. -- We establish this by showing the matrixPerspective matrices are equal. @@ -327,7 +308,8 @@ private lemma lieb_joint_concavity {m : Type*} [Fintype m] [DecidableEq m] w₂ * (liebJointFunction K p A₂ hA₂.posSemidef B₂ hB₂.posSemidef).re ≤ (liebJointFunction K p (w₁ • A₁ + w₂ • A₂) ((hA₁.posSemidef.real_smul hw₁).add (hA₂.posSemidef.real_smul hw₂)) - (w₁ • B₁ + w₂ • B₂) ((hB₁.posSemidef.real_smul hw₁).add (hB₂.posSemidef.real_smul hw₂))).re := by + (w₁ • B₁ + w₂ • B₂) + ((hB₁.posSemidef.real_smul hw₁).add (hB₂.posSemidef.real_smul hw₂))).re := by -- Handle boundary cases p = 0 and p = 1 separately rcases eq_or_lt_of_le hp0 with rfl | hp0' · -- p = 0: Tr(K†BK) is linear in B, so equality holds @@ -369,14 +351,14 @@ private lemma rpow_tendsto_smul_one {m : Type*} [Fintype m] [DecidableEq m] Filter.Tendsto (fun ε : ℝ => (A + (ε : ℂ) • (1 : Matrix m m ℂ)) ^ p) (nhdsWithin 0 (Set.Ioi 0)) (nhds (A ^ p)) := by -- Express A^p and (A + ε•1)^p via the continuous functional calculus. - -- Using matrixFunction and cfc, reduce to pointwise convergence of x^p as ε → 0+. - have hA_eq : A ^ p = cfc (fun x : ℝ => x ^ p) A := by - rw [← matrixFunction_rpow_eq hA, matrixFunction_eq_cfc hA.1] + -- Using cfc, reduce to pointwise convergence of x^p as ε → 0+. + have hA_eq : A ^ p = cfc (fun x : ℝ => x ^ p) A := + CFC.rpow_eq_cfc_real (a := A) (ha := by rw [Matrix.le_iff, sub_zero]; exact hA) have hshift_eq : ∀ ε : ℝ, 0 < ε → (A + (ε : ℂ) • (1 : Matrix m m ℂ)) ^ p = cfc (fun x : ℝ => (x + ε) ^ p) A := by intro ε hε - have hcfc_shift : cfc (fun x : ℝ => x + ε) A = A + (ε : ℂ) • (1 : Matrix m m ℂ) := by - rw [← matrixFunction_eq_cfc hA.1]; exact matrixFunction_add_const hA.1 ε + have hcfc_shift : cfc (fun x : ℝ => x + ε) A = A + (ε : ℂ) • (1 : Matrix m m ℂ) := + cfc_add_const_eq hA.1 ε have hcont_p : ContinuousOn (fun x : ℝ => x ^ p) ((fun x : ℝ => x + ε) '' spectrum ℝ A) := by apply ContinuousOn.rpow_const continuousOn_id rintro x ⟨_, hy_spec, rfl⟩ diff --git a/QuantumSystem/Analysis/Matrix/Order.lean b/QuantumSystem/Analysis/Matrix/Order.lean index b9d1502..873cd1c 100644 --- a/QuantumSystem/Analysis/Matrix/Order.lean +++ b/QuantumSystem/Analysis/Matrix/Order.lean @@ -49,33 +49,33 @@ open scoped MatrixOrder ComplexOrder NNReal A ≤ B (in the Löwner order) implies f(A) ≤ f(B). -/ def IsLownerMonotone (f : ℝ → ℝ) : Prop := ∀ (m : Type*) [Fintype m] [DecidableEq m] - (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef), + (A B : Matrix m m ℂ) (_hA : A.PosSemidef) (_hB : B.PosSemidef), A ≤ B → - let fA := matrixFunction (fun x => (f x : ℂ)) A hA.1 - let fB := matrixFunction (fun x => (f x : ℂ)) B hB.1 + let fA := cfc f A + let fB := cfc f B fA ≤ fB /-- A real function f is Löwner convex if f(tA + (1-t)B) ≤ t · f(A) + (1-t) · f(B) in the Löwner order for all t ∈ [0,1]. -/ def IsLownerConvex (f : ℝ → ℝ) : Prop := ∀ (m : Type*) [Fintype m] [DecidableEq m] - (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) (t : ℝ), + (A B : Matrix m m ℂ) (_hA : A.PosSemidef) (_hB : B.PosSemidef) (t : ℝ), 0 ≤ t → t ≤ 1 → - ∀ (hC : (t • A + (1 - t) • B).IsHermitian), - let fA := matrixFunction (fun x => (f x : ℂ)) A hA.1 - let fB := matrixFunction (fun x => (f x : ℂ)) B hB.1 - let fC := matrixFunction (fun x => (f x : ℂ)) (t • A + (1 - t) • B) hC + ∀ (_hC : (t • A + (1 - t) • B).IsHermitian), + let fA := cfc f A + let fB := cfc f B + let fC := cfc f (t • A + (1 - t) • B) fC ≤ t • fA + (1 - t) • fB /-- A real function f is Löwner concave if −f is Löwner convex. -/ def IsLownerConcave (f : ℝ → ℝ) : Prop := ∀ (m : Type*) [Fintype m] [DecidableEq m] - (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) (t : ℝ), + (A B : Matrix m m ℂ) (_hA : A.PosSemidef) (_hB : B.PosSemidef) (t : ℝ), 0 ≤ t → t ≤ 1 → - ∀ (hC : (t • A + (1 - t) • B).IsHermitian), - let fA := matrixFunction (fun x => Complex.ofReal (-f x)) A hA.1 - let fB := matrixFunction (fun x => Complex.ofReal (-f x)) B hB.1 - let fC := matrixFunction (fun x => Complex.ofReal (-f x)) (t • A + (1 - t) • B) hC + ∀ (_hC : (t • A + (1 - t) • B).IsHermitian), + let fA := cfc (fun x => -f x) A + let fB := cfc (fun x => -f x) B + let fC := cfc (fun x => -f x) (t • A + (1 - t) • B) fC ≤ t • fA + (1 - t) • fB /-- Jensen convexity (HPJ sense): compression inequality for two terms. @@ -84,24 +84,24 @@ f(A† T₁ A + B† T₂ B) ≤ A† f(T₁) A + B† f(T₂) B. -/ def IsJensenConvex (f : ℝ → ℝ) : Prop := ∀ (m : Type*) [Fintype m] [DecidableEq m] (A B T₁ T₂ : Matrix m m ℂ) - (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) + (_hT₁ : T₁.PosSemidef) (_hT₂ : T₂.PosSemidef) (_hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) - (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian), - let fT₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 - let fT₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 - let fC := matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + (_hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian), + let fT₁ := cfc f T₁ + let fT₂ := cfc f T₂ + let fC := cfc f (Aᴴ * T₁ * A + Bᴴ * T₂ * B) fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B /-- Jensen concavity in the HPJ sense: −f is Jensen convex. -/ def IsJensenConcave (f : ℝ → ℝ) : Prop := ∀ (m : Type*) [Fintype m] [DecidableEq m] (A B T₁ T₂ : Matrix m m ℂ) - (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) + (_hT₁ : T₁.PosSemidef) (_hT₂ : T₂.PosSemidef) (_hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) - (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian), - let fT₁ := matrixFunction (fun x => Complex.ofReal (-f x)) T₁ hT₁.1 - let fT₂ := matrixFunction (fun x => Complex.ofReal (-f x)) T₂ hT₂.1 - let fC := matrixFunction (fun x => Complex.ofReal (-f x)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + (_hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian), + let fT₁ := cfc (fun x => -f x) T₁ + let fT₂ := cfc (fun x => -f x) T₂ + let fC := cfc (fun x => -f x) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B /-- Block diagonal matrix is positive semidefinite if blocks are positive semidefinite. -/ @@ -156,9 +156,7 @@ lemma lownerConvex_compression_le.{v} {n : Type v} {m : Type v} [Fintype n] [Fin {f : ℝ → ℝ} (hconv : IsLownerConvex.{v} f) (hf0 : f 0 ≤ 0) (V : Matrix n m ℂ) (hVV : Vᴴ * V ≤ 1) (T : Matrix n n ℂ) (hT : T.PosSemidef) : - matrixFunction (fun x => (f x : ℂ)) (Vᴴ * T * V) - (isHermitian_conjTranspose_mul_mul (B := V) (A := T) hT.1) ≤ - Vᴴ * matrixFunction (fun x => (f x : ℂ)) T hT.1 * V := by + cfc f (Vᴴ * T * V) ≤ Vᴴ * cfc f T * V := by -- The proof uses the defect technique and the block diagonal CFC formula. classical -- Step 1: Setup the defect matrix D = √(I - V†V) @@ -191,9 +189,6 @@ lemma lownerConvex_compression_le.{v} {n : Type v} {m : Type v} [Fintype n] [Fin simp only [W, T'] at h ⊢ rw [h] simp only [Matrix.mul_zero, Matrix.zero_mul, add_zero] - -- Step 5: Relate matrixFunction to CFC - have hfT_eq : matrixFunction (fun x => (f x : ℂ)) T hT.1 = cfc f T := - matrixFunction_eq_cfc hT.1 f -- Step 6-7: W†f(T')W = V†f(T)V + f(0)·D†D have hWfTW : Wᴴ * cfc f T' * W = Vᴴ * cfc f T * V + (f 0 : ℂ) • (Dᴴ * D) := by @@ -231,10 +226,6 @@ lemma lownerConvex_compression_le.{v} {n : Type v} {m : Type v} [Fintype n] [Fin rw [heq] exact h have hWfTW' : Wᴴ * cfc f T' * W = Vᴴ * cfc f T * V + (f 0 : ℂ) • (Dᴴ * D) := hWfTW - rw [hfT_eq] - have hfVTV_eq : matrixFunction (fun x => (f x : ℂ)) (Vᴴ * T * V) hVTV_herm = - cfc f (Vᴴ * T * V) := matrixFunction_eq_cfc hVTV_herm f - rw [hfVTV_eq] have h_jensen : cfc f (Wᴴ * T' * W) ≤ Wᴴ * cfc f T' * W := by set P : Matrix (n ⊕ m) (n ⊕ m) ℂ := W * Wᴴ with hP_def have hP_sq : P * P = P := by @@ -287,16 +278,7 @@ lemma lownerConvex_compression_le.{v} {n : Type v} {m : Type v} [Fintype n] [Fin have hM_herm' : M.IsHermitian := by rw [hM_eq]; exact hM_herm have hconv_app := hconv (n ⊕ m) T' (S * T' * S) hT'_psd hST'S_psd (1/2) (by norm_num) (by norm_num) hM_herm - have hfT'_eq : matrixFunction (fun x => (f x : ℂ)) T' hT'_herm = cfc f T' := - matrixFunction_eq_cfc hT'_herm f - have hfST'S_eq : matrixFunction (fun x => (f x : ℂ)) (S * T' * S) hST'S_herm = - cfc f (S * T' * S) := matrixFunction_eq_cfc hST'S_herm f - rw [hfT'_eq, hfST'S_eq] at hconv_app - have hfM_conv : matrixFunction (fun x => (f x : ℂ)) - ((1 / 2 : ℝ) • T' + (1 - 1 / 2 : ℝ) • (S * T' * S)) hM_herm = cfc f M := - (matrixFunction_congr _ hM_herm hM_herm' hM_eq.symm).trans - (matrixFunction_eq_cfc hM_herm' f) - rw [hfM_conv] at hconv_app + rw [← hM_eq] at hconv_app have hT'_sa : IsSelfAdjoint T' := by rwa [IsSelfAdjoint, star_eq_conjTranspose] have hcfc_conj : S * cfc f T' * S = cfc f (S * T' * S) := by @@ -334,8 +316,8 @@ lemma lownerConvex_compression_le.{v} {n : Type v} {m : Type v} [Fintype n] [Fin module have hWMW_herm : (Wᴴ * M * W).IsHermitian := isHermitian_conjTranspose_mul_mul (B := W) (A := M) hM_herm' - have h_comp := matrixFunction_compression_of_commuting W M hM_herm' hWW hM_comm f hWMW_herm - rw [matrixFunction_eq_cfc hM_herm' f, matrixFunction_eq_cfc hWMW_herm f, hWMW] at h_comp + have h_comp := cfc_compression_of_commuting W M hM_herm' hWW hM_comm f hWMW_herm + rw [hWMW] at h_comp have h_compress := compression_le hconv_app W rw [h_comp] at h_compress have h_half : (1 - 1 / 2 : ℝ) = (1 / 2 : ℝ) := by norm_num @@ -389,47 +371,24 @@ private lemma compression_of_fromBlocks_cfc {m : Type*} [Fintype m] [DecidableEq (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) (f : ℝ → ℝ) : let V := Matrix.fromRows A B let T := Matrix.fromBlocks T₁ 0 0 T₂ - let hT_herm : T.IsHermitian := by - simpa using Matrix.IsHermitian.fromBlocks hT₁.1 (by simp : (0 : Matrix m m ℂ).IsHermitian) hT₂.1 - Vᴴ * matrixFunction (fun x => (f x : ℂ)) T hT_herm * V = - Aᴴ * matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A + - Bᴴ * matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * B := by + Vᴴ * cfc f T * V = Aᴴ * cfc f T₁ * A + Bᴴ * cfc f T₂ * B := by classical - intro V T hT_herm + intro V T -- Use the CFC block diagonal formula. - have hT_cfc : - matrixFunction (fun x => (f x : ℂ)) T hT_herm = - Matrix.fromBlocks - (matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1) 0 0 - (matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1) := by - have hT_cfc' : matrixFunction (fun x => (f x : ℂ)) T hT_herm = cfc f T := - matrixFunction_eq_cfc hT_herm f - have hT₁_cfc : matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 = cfc f T₁ := - matrixFunction_eq_cfc hT₁.1 f - have hT₂_cfc : matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 = cfc f T₂ := - matrixFunction_eq_cfc hT₂.1 f - have hT_sa : IsSelfAdjoint T := by - simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT_herm - have hT₁_sa : IsSelfAdjoint T₁ := by - simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT₁.1 - have hT₂_sa : IsSelfAdjoint T₂ := by - simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT₂.1 - have hfinite : (spectrum ℝ T₁ ∪ spectrum ℝ T₂).Finite := - (Matrix.finite_real_spectrum (A := T₁)).union (Matrix.finite_real_spectrum (A := T₂)) - have hcont : ContinuousOn f (spectrum ℝ T₁ ∪ spectrum ℝ T₂) := - Set.Finite.continuousOn hfinite f - have hblock := cfc_fromBlocks_diag (m := m) (A := T₁) (D := T₂) hT₁_sa hT₂_sa f hcont - calc - matrixFunction (fun x => (f x : ℂ)) T hT_herm = cfc f T := hT_cfc' - _ = Matrix.fromBlocks (cfc f T₁) 0 0 (cfc f T₂) := by simpa [T] using hblock - _ = Matrix.fromBlocks - (matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1) 0 0 - (matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1) := by simp [hT₁_cfc, hT₂_cfc] + have hT₁_sa : IsSelfAdjoint T₁ := by + simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT₁.1 + have hT₂_sa : IsSelfAdjoint T₂ := by + simpa [IsSelfAdjoint, Matrix.IsHermitian, star_eq_conjTranspose] using hT₂.1 + have hfinite : (spectrum ℝ T₁ ∪ spectrum ℝ T₂).Finite := + (Matrix.finite_real_spectrum (A := T₁)).union (Matrix.finite_real_spectrum (A := T₂)) + have hcont : ContinuousOn f (spectrum ℝ T₁ ∪ spectrum ℝ T₂) := + Set.Finite.continuousOn hfinite f + have hblock := cfc_fromBlocks_diag (m := m) (A := T₁) (D := T₂) hT₁_sa hT₂_sa f hcont + have hT_cfc : cfc f T = Matrix.fromBlocks (cfc f T₁) 0 0 (cfc f T₂) := by + simpa [T] using hblock rw [hT_cfc] simpa [V] using fromRows_compress_blockDiag - (A := A) (B := B) - (T₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1) - (T₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1) + (A := A) (B := B) (T₁ := cfc f T₁) (T₂ := cfc f T₂) /-- IsLownerConvex + f(0) ≤ 0 implies HPJ inequality (Matrix Convexity). Theorem 3.1 in Effros 2008, originally Hansen-Pedersen 1981 Theorem 2.1 (i)⟹(iii). @@ -453,10 +412,7 @@ lemma isJensenConvex_of_isLownerConvex.{v} have hVTV : Vᴴ * T * V = Aᴴ * T₁ * A + Bᴴ * T₂ * B := fromRows_compress_blockDiag A B T₁ T₂ -- Step 3: V†f(T)V = A†f(T₁)A + B†f(T₂)B (block diagonal CFC) - have hVfTV : Vᴴ * matrixFunction (fun x => (f x : ℂ)) T - (by simpa using Matrix.IsHermitian.fromBlocks hT₁.1 (by simp : (0 : Matrix m m ℂ).IsHermitian) hT₂.1) * V = - Aᴴ * matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A + - Bᴴ * matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * B := + have hVfTV : Vᴴ * cfc f T * V = Aᴴ * cfc f T₁ * A + Bᴴ * cfc f T₂ * B := compression_of_fromBlocks_cfc A B T₁ T₂ hT₁ hT₂ f have hΔ := Matrix.PosSemidef.one_sub_fromRows A B hAB let Δ := (1 : Matrix m m ℂ) - Vᴴ * V @@ -468,19 +424,12 @@ lemma isJensenConvex_of_isLownerConvex.{v} have : (Dᴴ * D).PosSemidef := by rw [hDD]; exact hΔ exact Matrix.PosSemidef.smul_nonpos hf0 this - have hfC := matrixFunction_congr (fun x => (f x : ℂ)) hC - (isHermitian_conjTranspose_mul_mul (B := V) (A := T) hT_psd.1) hVTV.symm - have hT_herm : T.IsHermitian := by - simpa using Matrix.IsHermitian.fromBlocks hT₁.1 - (by simp : (0 : Matrix m m ℂ).IsHermitian) hT₂.1 - calc matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC - = matrixFunction (fun x => (f x : ℂ)) (Vᴴ * T * V) - (isHermitian_conjTranspose_mul_mul (B := V) (A := T) hT_psd.1) := hfC - _ ≤ Vᴴ * matrixFunction (fun x => (f x : ℂ)) T hT_herm * V := by + calc cfc f (Aᴴ * T₁ * A + Bᴴ * T₂ * B) + = cfc f (Vᴴ * T * V) := by rw [hVTV] + _ ≤ Vᴴ * cfc f T * V := by have hVV : Vᴴ * V ≤ 1 := by simpa [V, fromRows_conjTranspose_mul_self] using hAB exact lownerConvex_compression_le hconv hf0 V hVV T hT_psd - _ = Aᴴ * matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 * A + - Bᴴ * matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 * B := hVfTV + _ = Aᴴ * cfc f T₁ * A + Bᴴ * cfc f T₂ * B := hVfTV /-- Matrix convexity of matrix inverse in the Löwner order. -/ private lemma inv_lowner_convex_le {m : Type*} [Fintype m] [DecidableEq m] @@ -619,19 +568,22 @@ for any PSD matrices A, B and t ∈ [0,1]. 1. Use the integral representation of xˢ via `exists_measure_nnrpow_eq_integral_cfcₙ_rpowIntegrand₀₁`. 2. Identify the integrand with the resolvent form `1 - u * (x + u)⁻¹` using CFC. 3. Apply the resolvent operator concavity inequality pointwise in u. -4. Integrate and rewrite with `matrixFunction_rpow_eq` to conclude the inequality. -/ +4. Integrate and rewrite with `CFC.rpow_eq_cfc_real` to conclude the inequality. -/ private lemma rpow_operator_concave_le {m : Type*} [Fintype m] [DecidableEq m] {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) (t : ℝ) (ht0 : 0 ≤ t) (ht1 : t ≤ 1) (hC : (t • A + (1 - t) • B).IsHermitian) : - t • matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 + - (1 - t) • matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) B hB.1 ≤ - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) (t • A + (1 - t) • B) hC := by + t • A ^ s + (1 - t) • B ^ s ≤ (t • A + (1 - t) • B) ^ s := by classical by_cases hs_eq : s = 1 · subst hs_eq - simp [Real.rpow_one, matrixFunction_id] + have hA0 : (0 : Matrix m m ℂ) ≤ A := by simpa [Matrix.le_iff] using hA + have hB0 : (0 : Matrix m m ℂ) ≤ B := by simpa [Matrix.le_iff] using hB + have hC0 : (0 : Matrix m m ℂ) ≤ t • A + (1 - t) • B := by + simpa [Matrix.le_iff] using (hA.real_smul ht0).add (hB.real_smul (by linarith)) + simp only [CFC.rpow_one (a := A) hA0, CFC.rpow_one (a := B) hB0, + CFC.rpow_one (a := t • A + (1 - t) • B) hC0, le_refl] -- The `attribute [local instance]` directives at the top of this -- `RpowOperatorConcaveAux` section activate the linfty operator-norm tower -- on `Matrix _ _ ℂ`. We additionally need to pin a few non-instance @@ -734,42 +686,36 @@ private lemma rpow_operator_concave_le {m : Type*} [Fintype m] [DecidableEq m] have hC_spec : quasispectrum ℝ C ⊆ Ici 0 := by intro x hx exact (StarOrderedRing.nonneg_iff_quasispectrum_nonneg (A := Matrix m m ℂ) C).1 hC0 x hx + have hcont_res : ContinuousOn (fun x : ℝ => 1 - u * (x + u)⁻¹) (Ici 0) := by + have hcont_add : ContinuousOn (fun x : ℝ => x + u) (Ici 0) := by fun_prop + have hne : ∀ x ∈ Ici (0 : ℝ), x + u ≠ 0 := by + intro x hx; have hx' : 0 ≤ x := hx; linarith + exact continuousOn_const.sub + (continuousOn_const.mul (ContinuousOn.inv₀ hcont_add hne)) + have hAspec' : spectrum ℝ A ⊆ Ici 0 := by + rw [hA.1.spectrum_real_eq_range_eigenvalues] + rintro _ ⟨i, rfl⟩; exact hA.eigenvalues_nonneg i + have hBspec' : spectrum ℝ B ⊆ Ici 0 := by + rw [hB.1.spectrum_real_eq_range_eigenvalues] + rintro _ ⟨i, rfl⟩; exact hB.eigenvalues_nonneg i + have hCspec' : spectrum ℝ C ⊆ Ici 0 := by + rw [hCpsd.1.spectrum_real_eq_range_eigenvalues] + rintro _ ⟨i, rfl⟩; exact hCpsd.eigenvalues_nonneg i have hA_eq : - cfcₙ (rpowIntegrand₀₁ q u) A = - matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) A hA.1 := by - calc - cfcₙ (rpowIntegrand₀₁ q u) A = - cfc (rpowIntegrand₀₁ (q : ℝ) u) A := by - simpa [Real.rpowIntegrand₀₁_zero_right] using - (cfcₙ_eq_cfc (a := A) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) - (hf := hcont_Ici.mono hA_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) - _ = _ := by - symm - exact matrixFunction_eq_cfc hA.1 (fun x => rpowIntegrand₀₁ (q : ℝ) u x) + cfcₙ (rpowIntegrand₀₁ q u) A = cfc (rpowIntegrand₀₁ (q : ℝ) u) A := by + simpa [Real.rpowIntegrand₀₁_zero_right] using + (cfcₙ_eq_cfc (a := A) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) + (hf := hcont_Ici.mono hA_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) have hB_eq : - cfcₙ (rpowIntegrand₀₁ q u) B = - matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) B hB.1 := by - calc - cfcₙ (rpowIntegrand₀₁ q u) B = - cfc (rpowIntegrand₀₁ (q : ℝ) u) B := by - simpa [Real.rpowIntegrand₀₁_zero_right] using - (cfcₙ_eq_cfc (a := B) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) - (hf := hcont_Ici.mono hB_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) - _ = _ := by - symm - exact matrixFunction_eq_cfc hB.1 (fun x => rpowIntegrand₀₁ (q : ℝ) u x) + cfcₙ (rpowIntegrand₀₁ q u) B = cfc (rpowIntegrand₀₁ (q : ℝ) u) B := by + simpa [Real.rpowIntegrand₀₁_zero_right] using + (cfcₙ_eq_cfc (a := B) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) + (hf := hcont_Ici.mono hB_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) have hC_eq : - cfcₙ (rpowIntegrand₀₁ q u) C = - matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) C hCpsd.1 := by - calc - cfcₙ (rpowIntegrand₀₁ q u) C = - cfc (rpowIntegrand₀₁ (q : ℝ) u) C := by - simpa [Real.rpowIntegrand₀₁_zero_right] using - (cfcₙ_eq_cfc (a := C) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) - (hf := hcont_Ici.mono hC_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) - _ = _ := by - symm - exact matrixFunction_eq_cfc hCpsd.1 (fun x => rpowIntegrand₀₁ (q : ℝ) u x) + cfcₙ (rpowIntegrand₀₁ q u) C = cfc (rpowIntegrand₀₁ (q : ℝ) u) C := by + simpa [Real.rpowIntegrand₀₁_zero_right] using + (cfcₙ_eq_cfc (a := C) (f := fun x => rpowIntegrand₀₁ (q : ℝ) u x) + (hf := hcont_Ici.mono hC_spec) (hf0 := Real.rpowIntegrand₀₁_zero_right)) have hfun : (fun x : ℝ => rpowIntegrand₀₁ (q : ℝ) u x) = fun x => u ^ (s - 1) * (1 - u * (x + u)⁻¹) := by @@ -792,54 +738,32 @@ private lemma rpow_operator_concave_le {m : Type*} [Fintype m] [DecidableEq m] simp [hu0, hx] simp [hmul] have hA_res : - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 = + cfc (fun x => 1 - u * (x + u)⁻¹) A = (1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹ := - matrixFunction_resolvent (m := m) hA (r := u) hu' + cfc_resolvent (m := m) hA hu' have hB_res : - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1 = + cfc (fun x => 1 - u * (x + u)⁻¹) B = (1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹ := - matrixFunction_resolvent (m := m) hB (r := u) hu' + cfc_resolvent (m := m) hB hu' have hC_res : - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 = + cfc (fun x => 1 - u * (x + u)⁻¹) C = (1 : Matrix m m ℂ) - (u : ℂ) • (C + (u : ℂ) • 1)⁻¹ := - matrixFunction_resolvent (m := m) hCpsd (r := u) hu' + cfc_resolvent (m := m) hCpsd hu' have hA_int' : - matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) A hA.1 = - ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 := by - have hsmul : - matrixFunction (fun x => (((u ^ (s - 1)) * (1 - u * (x + u)⁻¹) : ℝ) : ℂ)) A hA.1 = - ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 := by - simpa using - (matrixFunction_smul hA.1 ((u ^ (s - 1) : ℝ) : ℂ) - (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ))) - simpa [hfun] using hsmul + cfc (fun x => rpowIntegrand₀₁ (q : ℝ) u x) A = + (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) A := by + rw [hfun, cfc_const_mul (R := ℝ) (u ^ (s - 1)) (fun x => 1 - u * (x + u)⁻¹) A + (hcont_res.mono hAspec')] have hB_int' : - matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) B hB.1 = - ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1 := by - have hsmul : - matrixFunction (fun x => (((u ^ (s - 1)) * (1 - u * (x + u)⁻¹) : ℝ) : ℂ)) B hB.1 = - ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1 := by - simpa - using - (matrixFunction_smul hB.1 ((u ^ (s - 1) : ℝ) : ℂ) - (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ))) - simpa [hfun] using hsmul + cfc (fun x => rpowIntegrand₀₁ (q : ℝ) u x) B = + (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) B := by + rw [hfun, cfc_const_mul (R := ℝ) (u ^ (s - 1)) (fun x => 1 - u * (x + u)⁻¹) B + (hcont_res.mono hBspec')] have hC_int' : - matrixFunction (fun x => ((rpowIntegrand₀₁ (q : ℝ) u x : ℝ) : ℂ)) C hCpsd.1 = - ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 := by - have hsmul : - matrixFunction (fun x => (((u ^ (s - 1)) * (1 - u * (x + u)⁻¹) : ℝ) : ℂ)) C hCpsd.1 = - ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 := by - simpa using - (matrixFunction_smul hCpsd.1 ((u ^ (s - 1) : ℝ) : ℂ) - (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ))) - simpa [hfun] using hsmul + cfc (fun x => rpowIntegrand₀₁ (q : ℝ) u x) C = + (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) C := by + rw [hfun, cfc_const_mul (R := ℝ) (u ^ (s - 1)) (fun x => 1 - u * (x + u)⁻¹) C + (hcont_res.mono hCspec')] have hres_le : t • ((1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹) + (1 - t) • ((1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹) @@ -848,45 +772,36 @@ private lemma rpow_operator_concave_le {m : Type*} [Fintype m] [DecidableEq m] (resolvent_lowner_concave_le (m := m) hA hB ht0 ht1 (r := u) hu') -- Scale the resolvent inequality by the positive factor u^(s-1). have hscale : - ((u ^ (s - 1) : ℝ) : ℂ) • + (u ^ (s - 1) : ℝ) • (t • ((1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹) + (1 - t) • ((1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹)) - ≤ ((u ^ (s - 1) : ℝ) : ℂ) • + ≤ (u ^ (s - 1) : ℝ) • ((1 : Matrix m m ℂ) - (u : ℂ) • (C + (u : ℂ) • 1)⁻¹) := by - have hnonneg : 0 ≤ u ^ (s - 1) := by - exact Real.rpow_nonneg (le_of_lt hu') _ - have hnonneg_C : (0 : ℂ) ≤ ((u ^ (s - 1) : ℝ) : ℂ) := by - rw [show (0 : ℂ) = ((0 : ℝ) : ℂ) from rfl, Complex.real_le_real] - exact hnonneg - rw [Matrix.le_iff] at hres_le ⊢ - have hpsd : - (((u ^ (s - 1) : ℝ) : ℂ) • - ((1 : Matrix m m ℂ) - (u : ℂ) • (C + (u : ℂ) • 1)⁻¹) - - ((u ^ (s - 1) : ℝ) : ℂ) • - (t • ((1 : Matrix m m ℂ) - (u : ℂ) • (A + (u : ℂ) • 1)⁻¹) + - (1 - t) • ((1 : Matrix m m ℂ) - (u : ℂ) • (B + (u : ℂ) • 1)⁻¹))).PosSemidef := by - simpa [smul_sub] using hres_le.smul hnonneg_C - simpa [smul_sub] using hpsd - -- Replace with the matrixFunction form. + have hnonneg : 0 ≤ u ^ (s - 1) := by positivity + rw [Matrix.le_iff] + have h := (Matrix.le_iff.mp hres_le).real_smul hnonneg + convert h using 1 + exact (smul_sub _ _ _).symm + -- Replace with the cfc resolvent form. have hscale' : - ((u ^ (s - 1) : ℝ) : ℂ) • - (t • matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 + - (1 - t) • matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1) - ≤ ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 := by + (u ^ (s - 1) : ℝ) • + (t • cfc (fun x => 1 - u * (x + u)⁻¹) A + + (1 - t) • cfc (fun x => 1 - u * (x + u)⁻¹) B) + ≤ (u ^ (s - 1) : ℝ) • + cfc (fun x => 1 - u * (x + u)⁻¹) C := by have hscale' := hscale rw [hA_res.symm, hB_res.symm, hC_res.symm] at hscale' exact hscale' have hscale'' : - t • ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) A hA.1 + - (1 - t) • ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) B hB.1 ≤ - ((u ^ (s - 1) : ℝ) : ℂ) • - matrixFunction (fun x => ((1 - u * (x + u)⁻¹ : ℝ) : ℂ)) C hCpsd.1 := by - rw [smul_comm t ((u ^ (s - 1) : ℝ) : ℂ), - smul_comm (1 - t) ((u ^ (s - 1) : ℝ) : ℂ), ← smul_add] - exact hscale' + t • (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) A + + (1 - t) • (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) B ≤ + (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) C := by + have e : (u ^ (s - 1) : ℝ) • (t • cfc (fun x => 1 - u * (x + u)⁻¹) A + + (1 - t) • cfc (fun x => 1 - u * (x + u)⁻¹) B) = + t • (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) A + + (1 - t) • (u ^ (s - 1) : ℝ) • cfc (fun x => 1 - u * (x + u)⁻¹) B := by + module + rw [← e]; exact hscale' simpa [hA_eq, hB_eq, hC_eq, hA_int', hB_int', hC_int'] using hscale'' have hle_integral : t • (∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) A ∂μ) + @@ -953,67 +868,13 @@ private lemma rpow_operator_concave_le {m : Type*} [Fintype m] [DecidableEq m] CFC.nnrpow_eq_rpow (A := Matrix m m ℂ) (a := C) (x := q) hq_pos have h2 : C ^ q = ∫ u in Ioi 0, cfcₙ (rpowIntegrand₀₁ q u) C ∂μ := (hμ C hC0).2 rw [← hqs, ← h1]; exact h2 - have hC_eq_mf : - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hC = C ^ s := by - have hC' : - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hC = - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hCpsd.1 := by - exact - (matrixFunction_congr (A := C) (B := C) - (f := fun x => ((x ^ s : ℝ) : ℂ)) (hA := hC) (hB := hCpsd.1) rfl) - calc - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hC = - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) C hCpsd.1 := hC' - _ = C ^ s := matrixFunction_rpow_eq hCpsd s - have hA_eq_mf : - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) A hA.1 = A ^ s := - matrixFunction_rpow_eq hA s - have hB_eq_mf : - matrixFunction (fun x => ((x ^ s : ℝ) : ℂ)) B hB.1 = B ^ s := - matrixFunction_rpow_eq hB s - -- Rewrite the integral inequality to the matrixFunction statement. + -- Conclude from the integral inequality. have hfinal : t • A ^ s + (1 - t) • B ^ s ≤ C ^ s := by simpa [hA_eq_int, hB_eq_int, hC_eq_int] using hle_integral - simpa [hA_eq_mf, hB_eq_mf, hC_eq_mf, C] using hfinal + exact hfinal end RpowOperatorConcaveAux -/-- Helper: The difference in quadratic forms for operator concavity. -/ -private lemma rpow_concavity_quadform_nonneg {m : Type*} [Fintype m] [DecidableEq m] - {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) - (A B : Matrix m m ℂ) (hA : A.PosSemidef) (hB : B.PosSemidef) - (t : ℝ) (ht0 : 0 ≤ t) (ht1 : t ≤ 1) - (hC : (t • A + (1 - t) • B).IsHermitian) (v : m → ℂ) : - 0 ≤ (star v ⬝ᵥ ( - matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - - t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 - - (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1) *ᵥ v).re := by - have hle : - t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 + - (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1 ≤ - matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC := - rpow_operator_concave_le hs0 hs1 A B hA hB t ht0 ht1 hC - have hpsd : - (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - - (t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 + - (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1)).PosSemidef := by - simpa [Matrix.le_iff] using hle - have hpsd' : - (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - - t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 - - (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1).PosSemidef := by - have hcalc : - (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - - (t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 + - (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1)) = - (matrixFunction (fun x => Complex.ofReal (x ^ s)) (t • A + (1 - t) • B) hC - - t • matrixFunction (fun x => Complex.ofReal (x ^ s)) A hA.1 - - (1 - t) • matrixFunction (fun x => Complex.ofReal (x ^ s)) B hB.1) := by - module - simpa [hcalc] using hpsd - have hnonneg := hpsd'.dotProduct_mulVec_nonneg v - exact (Complex.nonneg_iff.mp hnonneg).1 - /-- The power function t^s (0 < s ≤ 1) is Löwner concave. This means: (λA + (1-λ)B)^s ≥ λ·A^s + (1-λ)·B^s in Löwner order. @@ -1026,65 +887,23 @@ lemma rpow_isLownerConcave {s : ℝ} (hs0 : 0 < s) (hs1 : s ≤ 1) : IsLownerConcave (fun t => t ^ s) := by unfold IsLownerConcave intro m _ _ A B hA hB t ht0 ht1 hC - rw [Matrix.le_iff] - -- Define the power function - let f : ℝ → ℂ := fun x => Complex.ofReal (x ^ s) - let neg_f : ℝ → ℂ := fun x => Complex.ofReal (-(x ^ s)) - -- Relate neg_f to -f - have hfunc : neg_f = fun x => -f x := by - funext x; exact Complex.ofReal_neg (x ^ s) - -- The matrixFunction of neg_f equals -matrixFunction of f - have hA_mf : matrixFunction neg_f A hA.1 = -matrixFunction f A hA.1 := by - rw [hfunc]; exact matrixFunction_neg hA.1 f - have hB_mf : matrixFunction neg_f B hB.1 = -matrixFunction f B hB.1 := by - rw [hfunc]; exact matrixFunction_neg hB.1 f - have hC_mf : matrixFunction neg_f (t • A + (1 - t) • B) hC = - -matrixFunction f (t • A + (1 - t) • B) hC := by - rw [hfunc]; exact matrixFunction_neg hC f - -- The goal's function equals neg_f - have hgoal_A : matrixFunction (fun x : ℝ => (((fun y => -(y ^ s)) x : ℝ) : ℂ)) A hA.1 = - matrixFunction neg_f A hA.1 := rfl - have hgoal_B : matrixFunction (fun x : ℝ => (((fun y => -(y ^ s)) x : ℝ) : ℂ)) B hB.1 = - matrixFunction neg_f B hB.1 := rfl - have hgoal_C : matrixFunction (fun x : ℝ => (((fun y => -(y ^ s)) x : ℝ) : ℂ)) - (t • A + (1 - t) • B) hC = matrixFunction neg_f (t • A + (1 - t) • B) hC := rfl - simp only [hgoal_A, hgoal_B, hgoal_C, hA_mf, hB_mf, hC_mf] - -- Simplify: t•(-A^s) + (1-t)•(-B^s) - (-C^s) = C^s - t•A^s - (1-t)•B^s - have halg : t • -matrixFunction f A hA.1 + (1 - t) • -matrixFunction f B hB.1 - - -matrixFunction f (t • A + (1 - t) • B) hC = - matrixFunction f (t • A + (1 - t) • B) hC - - t • matrixFunction f A hA.1 - (1 - t) • matrixFunction f B hB.1 := by module - rw [halg] - -- Show PosSemidef via Hermitian and quadratic form characterization - -- Use PosSemidef.of_dotProduct_mulVec_nonneg which works with (n → R) instead of Finsupp - apply PosSemidef.of_dotProduct_mulVec_nonneg - -- First show Hermitian - · have hC_herm : (matrixFunction f (t • A + (1 - t) • B) hC).IsHermitian := by - simpa [f] using matrixFunction_isHermitian hC (fun x => x ^ s) - have hA_herm : (t • matrixFunction f A hA.1).IsHermitian := by - simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hA.1 (fun x => x ^ s)) t - have hB_herm : ((1 - t) • matrixFunction f B hB.1).IsHermitian := by - simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hB.1 (fun x => x ^ s)) (1 - t) - exact IsHermitian.sub (IsHermitian.sub hC_herm hA_herm) hB_herm - -- Then show the quadratic form is nonneg for all vectors - · intro v - -- The helper lemma gives us the real part is nonneg - have h_re : 0 ≤ (star v ⬝ᵥ (matrixFunction f (t • A + (1 - t) • B) hC - - t • matrixFunction f A hA.1 - (1 - t) • matrixFunction f B hB.1) *ᵥ v).re := - rpow_concavity_quadform_nonneg hs0 hs1 A B hA hB t ht0 ht1 hC v - -- The result is real (imaginary part is 0), so nonneg iff real part is nonneg - have hreal : (star v ⬝ᵥ (matrixFunction f (t • A + (1 - t) • B) hC - - t • matrixFunction f A hA.1 - (1 - t) • matrixFunction f B hB.1) *ᵥ v).im = 0 := by - apply IsHermitian.quadForm_im_eq_zero - have hC_herm : (matrixFunction f (t • A + (1 - t) • B) hC).IsHermitian := by - simpa [f] using matrixFunction_isHermitian hC (fun x => x ^ s) - have hA_herm : (t • matrixFunction f A hA.1).IsHermitian := by - simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hA.1 (fun x => x ^ s)) t - have hB_herm : ((1 - t) • matrixFunction f B hB.1).IsHermitian := by - simpa [f] using IsHermitian.smul_real (matrixFunction_isHermitian hB.1 (fun x => x ^ s)) (1 - t) - exact IsHermitian.sub (IsHermitian.sub hC_herm hA_herm) hB_herm - rw [Complex.nonneg_iff] - exact ⟨h_re, hreal.symm⟩ + have hA0 : (0 : Matrix m m ℂ) ≤ A := by simpa [Matrix.le_iff] using hA + have hB0 : (0 : Matrix m m ℂ) ≤ B := by simpa [Matrix.le_iff] using hB + have hC0 : (0 : Matrix m m ℂ) ≤ t • A + (1 - t) • B := by + simpa [Matrix.le_iff] using (hA.real_smul ht0).add (hB.real_smul (by linarith)) + change cfc (fun x : ℝ => -(x ^ s)) (t • A + (1 - t) • B) ≤ + t • cfc (fun x : ℝ => -(x ^ s)) A + (1 - t) • cfc (fun x : ℝ => -(x ^ s)) B + have eA : cfc (fun x : ℝ => -(x ^ s)) A = -(A ^ s) := by + rw [cfc_neg, ← CFC.rpow_eq_cfc_real (a := A) (ha := hA0)] + have eB : cfc (fun x : ℝ => -(x ^ s)) B = -(B ^ s) := by + rw [cfc_neg, ← CFC.rpow_eq_cfc_real (a := B) (ha := hB0)] + have eC : cfc (fun x : ℝ => -(x ^ s)) (t • A + (1 - t) • B) = -((t • A + (1 - t) • B) ^ s) := by + rw [cfc_neg, ← CFC.rpow_eq_cfc_real (a := t • A + (1 - t) • B) (ha := hC0)] + rw [eA, eB, eC] + have key := rpow_operator_concave_le hs0 hs1 A B hA hB t ht0 ht1 hC + rw [Matrix.le_iff] at key ⊢ + convert key using 1 + module /-- The negated power function -t^s (0 < s ≤ 1) is Löwner convex. This is the dual statement of rpow_isLownerConcave. -/ @@ -1111,9 +930,9 @@ lemma hpj_subhomogeneous.{v} {f : ℝ → ℝ} (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) (hAB : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ)) (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian) : - let fT₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 - let fT₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 - let fC := matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + let fT₁ := cfc f T₁ + let fT₂ := cfc f T₂ + let fC := cfc f (Aᴴ * T₁ * A + Bᴴ * T₂ * B) fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B := by have _ := hf0 exact hconv m A B T₁ T₂ hT₁ hT₂ hAB hC @@ -1126,9 +945,9 @@ lemma hpj_affine.{v} {f : ℝ → ℝ} (hT₁ : T₁.PosSemidef) (hT₂ : T₂.PosSemidef) (hAB : Aᴴ * A + Bᴴ * B = (1 : Matrix m m ℂ)) (hC : (Aᴴ * T₁ * A + Bᴴ * T₂ * B).IsHermitian) : - let fT₁ := matrixFunction (fun x => (f x : ℂ)) T₁ hT₁.1 - let fT₂ := matrixFunction (fun x => (f x : ℂ)) T₂ hT₂.1 - let fC := matrixFunction (fun x => (f x : ℂ)) (Aᴴ * T₁ * A + Bᴴ * T₂ * B) hC + let fT₁ := cfc f T₁ + let fT₂ := cfc f T₂ + let fC := cfc f (Aᴴ * T₁ * A + Bᴴ * T₂ * B) fC ≤ Aᴴ * fT₁ * A + Bᴴ * fT₂ * B := by have hAB' : Aᴴ * A + Bᴴ * B ≤ (1 : Matrix m m ℂ) := by simp [hAB] diff --git a/QuantumSystem/Analysis/Matrix/PartialTrace.lean b/QuantumSystem/Analysis/Matrix/PartialTrace.lean deleted file mode 100644 index de3f0dc..0000000 --- a/QuantumSystem/Analysis/Matrix/PartialTrace.lean +++ /dev/null @@ -1,436 +0,0 @@ -module - -public import QuantumSystem.Algebra.LocalNet - -/-! -# Partial trace as restriction (matrix-level) - -The **restriction** (Schrödinger-picture partial trace) on the matrix algebra of a local -net. Given regions `Λ ⊆ Λ_total` of a `LocalNet`, the restriction of a matrix on -`𝔄(Λ_total)` to `𝔄(Λ)` is defined as the partial trace over the complementary region -`Λ_total \ Λ`. This is the Schrödinger-picture dual of the algebra -inclusion `𝔄(Λ) ↪ 𝔄(Λ_total)`. There is no positional ("left/right") concept — the -operation is parameterised by the region itself. - -The matrix-level operation `Matrix.restrict` is defined as a linear map factoring through -`LocalNet.combineIdx` (which factors `regionIdx Λ_total ≃ regionIdx Λ × regionIdx (Λ_total \ Λ)`). -The bundled quantum-channel structure, Kraus operators, trace preservation, and -Heisenberg-picture duality are also provided. - -## Main definitions - -* `Matrix.restrict` — linear restriction map (matrix-level partial trace) -* `Matrix.restrictKraus` — Kraus operators indexed by the complementary region -* `Matrix.QuantumChannel.restrict` — bundled quantum channel -* `DensityMatrix.restrict` — restriction applied to density matrices -* `ρ ↾ Λ` — paper-style notation for `DensityMatrix.restrict` - -## Main results - -* `Matrix.restrict_eq_sum_kraus` — Kraus form -* `Matrix.isCompletelyPositive_restrict`, `Matrix.isTracePreserving_restrict`, - `Matrix.isQuantumChannel_restrict` — channel properties -* `Matrix.restrict_restrict` — iterated marginalisation = direct marginalisation -* `Matrix.trace_mul_includeAlgebra` — Heisenberg-picture trace identity - -## References - -* Sorce 2024 (`https://arxiv.org/abs/2408.07994`) -* Verch 2025 (`https://arxiv.org/abs/2507.00900`) -* Naaijkens 2012 (`https://repository.ubn.ru.nl/handle/2066/92737`) --/ - -@[expose] public section - -namespace Matrix - -variable {L : LocalNet} - -/-! ### Linear restriction map -/ - -/-- **Restriction of a matrix to a sub-region** (Schrödinger-picture partial trace). - Sums over indices of the complementary region `Λ_total \ Λ`. -/ -noncomputable def restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - L.localAlgebra Λ_total →ₗ[ℂ] L.localAlgebra Λ where - toFun M := Matrix.of fun a a' => - ∑ b : L.regionIdx (Λ_total \ Λ), - M (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) - map_add' M N := by - ext a a' - simp only [Matrix.of_apply, Matrix.add_apply, Finset.sum_add_distrib] - map_smul' c M := by - ext a a' - simp only [Matrix.of_apply, Matrix.smul_apply, smul_eq_mul, - RingHom.id_apply, Finset.mul_sum] - -@[simp] lemma restrict_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (M : L.localAlgebra Λ_total) (a a' : L.regionIdx Λ) : - restrict h M a a' = - ∑ b : L.regionIdx (Λ_total \ Λ), - M (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) := rfl - -/-! ### Trace preservation -/ - -/-- `Tr(restrict h M) = Tr M`: the restriction preserves the global trace. -/ -theorem trace_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (M : L.localAlgebra Λ_total) : - Tr (restrict h M) = Tr M := by - unfold Matrix.trace - simp_rw [Matrix.diag_apply, restrict_apply] - -- LHS: ∑ a, ∑ b, M (combineIdx (a,b)) (combineIdx (a,b)) - -- RHS: ∑ s : regionIdx Λ_total, M s s - -- Step 1: combine the double sum into a sum over the product type - rw [show (∑ a : L.regionIdx Λ, ∑ b : L.regionIdx (Λ_total \ Λ), - M ((L.combineIdx h) (a, b)) ((L.combineIdx h) (a, b))) = - ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), - M ((L.combineIdx h) p) ((L.combineIdx h) p) from - (Fintype.sum_prod_type - (fun p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ) => - M ((L.combineIdx h) p) ((L.combineIdx h) p))).symm] - -- Step 2: reindex via combineIdx - exact (L.combineIdx h).sum_comp (fun s => M s s) - -theorem isTracePreserving_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - IsTracePreserving (restrict (L := L) h) := - trace_restrict h - -/-! ### Kraus operators and complete positivity -/ - -/-- Kraus operator for `restrict h`, indexed by `b : regionIdx (Λ_total \ Λ)`: - `K_b a a_total = [a_total = combineIdx h (a, b)]`. -/ -noncomputable def restrictKraus {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (b : L.regionIdx (Λ_total \ Λ)) : - Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ := - Matrix.of fun a a_total => - if a_total = L.combineIdx h (a, b) then (1 : ℂ) else 0 - -private lemma restrictKraus_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (b : L.regionIdx (Λ_total \ Λ)) (a : L.regionIdx Λ) (a_total : L.regionIdx Λ_total) : - restrictKraus h b a a_total = - if a_total = L.combineIdx h (a, b) then (1 : ℂ) else 0 := rfl - -/-- Entry-wise: `(K_b * M * K_bᴴ) a a' = M (combineIdx (a, b)) (combineIdx (a', b))`. -/ -private lemma restrictKraus_mul_mul_apply {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (M : L.localAlgebra Λ_total) (b : L.regionIdx (Λ_total \ Λ)) - (a a' : L.regionIdx Λ) : - ((restrictKraus h b * M : Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ) * - (restrictKraus h b)ᴴ : L.localAlgebra Λ) a a' = - M (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) := by - rw [Matrix.mul_apply] - simp_rw [Matrix.mul_apply, Matrix.conjTranspose_apply] - -- Outer sum: ∑ p, (K_b * M)(a, p) * star (K_b a' p) - -- = ∑ p, (∑ q, K_b a q * M q p) * star (K_b a' p) - -- K_b a' p = 1 iff p = combineIdx (a', b), so outer sum collapses at p = combineIdx (a', b) - rw [Finset.sum_eq_single (L.combineIdx h (a', b))] - · -- inner sum collapses at q = combineIdx (a, b) - rw [Finset.sum_eq_single (L.combineIdx h (a, b))] - · simp [restrictKraus_apply] - · intro q _ hq - simp only [restrictKraus_apply] - rw [if_neg hq]; ring - · simp - · intro p _ hp - simp only [restrictKraus_apply, apply_ite (star · : ℂ → ℂ), - star_one, star_zero] - rw [if_neg hp] - simp - · simp - -/-- Kraus form of the restriction. -/ -theorem restrict_eq_sum_kraus {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (M : L.localAlgebra Λ_total) : - restrict h M = - ∑ b : L.regionIdx (Λ_total \ Λ), - ((restrictKraus h b * M : Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ) * - (restrictKraus h b)ᴴ : L.localAlgebra Λ) := by - ext a a' - rw [restrict_apply, Matrix.sum_apply] - refine Finset.sum_congr rfl fun b _ => ?_ - exact (restrictKraus_mul_mul_apply h M b a a').symm - -theorem isCompletelyPositive_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - IsCompletelyPositive (restrict (L := L) h) := by - classical - refine ⟨Fintype.card (L.regionIdx (Λ_total \ Λ)), - fun i => restrictKraus h ((Fintype.equivFin (L.regionIdx (Λ_total \ Λ))).symm i), ?_⟩ - intro M - rw [restrict_eq_sum_kraus] - -- Reindex the regionIdx-sum via (equivFin _).symm : Fin r ≃ regionIdx - exact ((Fintype.equivFin (L.regionIdx (Λ_total \ Λ))).symm.sum_comp - (fun b : L.regionIdx (Λ_total \ Λ) => - ((restrictKraus h b * M : Matrix (L.regionIdx Λ) (L.regionIdx Λ_total) ℂ) * - (restrictKraus h b)ᴴ : L.localAlgebra Λ))).symm - -theorem isQuantumChannel_restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - IsQuantumChannel (restrict (L := L) h) where - completelyPositive := isCompletelyPositive_restrict h - tracePreserving := isTracePreserving_restrict h - -/-- Restriction as a bundled `QuantumChannel`. -/ -noncomputable def QuantumChannel.restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - Matrix.QuantumChannel (L.regionIdx Λ_total) (L.regionIdx Λ) := - ⟨Matrix.restrict h, isQuantumChannel_restrict h⟩ - -/-! ### Basic algebraic identities for `restrict` -/ - -/-- `restrict h 1 = card • 1`: restricting the identity matrix scales by the cardinality - of the traced-out region. AQFT analogue of `partialTraceRight 1 = card • 1`. -/ -lemma restrict_one {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) : - restrict h (1 : L.localAlgebra Λ_total) = - (Fintype.card (L.regionIdx (Λ_total \ Λ)) : ℂ) • (1 : L.localAlgebra Λ) := by - ext a a' - simp only [restrict_apply, Matrix.smul_apply, smul_eq_mul, Matrix.one_apply] - by_cases hab : a = a' - · subst hab - simp [Finset.card_univ, Finset.sum_const] - · rw [if_neg hab, mul_zero] - refine Finset.sum_eq_zero fun b _ => ?_ - rw [if_neg] - intro hcontra - exact hab ((Prod.mk.injEq _ _ _ _).mp ((L.combineIdx h).injective hcontra) |>.1) - -/-- `restrict h (c • M) = c • restrict h M` (linearity over `ℂ`). -/ -lemma restrict_smul {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) (c : ℂ) - (M : L.localAlgebra Λ_total) : - restrict h (c • M) = c • restrict h M := - (Matrix.restrict h).map_smul c M - -/-- `restrict h` distributes over addition. -/ -private lemma restrict_add {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (M N : L.localAlgebra Λ_total) : - restrict h (M + N) = restrict h M + restrict h N := - (Matrix.restrict h).map_add M N - -/-! ### Iterated restriction (transitivity of marginalisation) - -Restricting first to an intermediate region and then to a sub-sub-region equals -restricting directly: `restrict h₂ (restrict h₁ M) = restrict (h₂.trans h₁) M`. -/ - -/-- For `Λ' ⊆ Λ ⊆ Λ_total`, the index combiner factors through the intermediate - region: starting from `(a', γ', β'') ∈ regionIdx Λ' × regionIdx (Λ \ Λ') × - regionIdx (Λ_total \ Λ)`, combining `(a', γ')` to give `regionIdx Λ` and then - pairing with `β''` agrees with combining `(a', γ' & β'')` directly to give - `regionIdx Λ_total`. This is the key identity behind `restrict_restrict`. -/ -lemma combineIdx_assoc_aux - {Λ' Λ Λ_total : Finset L.sites} (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) - (a' : L.regionIdx Λ') (γ : L.regionIdx (Λ \ Λ')) - (β : L.regionIdx (Λ_total \ Λ)) - (s : ↥Λ_total) : - L.combineIdx h₁ (L.combineIdx h₂ (a', γ), β) s = - if hsΛ : s.val ∈ Λ then - if hsΛ' : s.val ∈ Λ' then a' ⟨s.val, hsΛ'⟩ - else γ ⟨s.val, Finset.mem_sdiff.mpr ⟨hsΛ, hsΛ'⟩⟩ - else β ⟨s.val, Finset.mem_sdiff.mpr ⟨s.property, hsΛ⟩⟩ := by - by_cases hsΛ : s.val ∈ Λ - · rw [LocalNet.combineIdx_apply_mem h₁ _ _ s hsΛ, dif_pos hsΛ] - by_cases hsΛ' : s.val ∈ Λ' - · rw [LocalNet.combineIdx_apply_mem h₂ _ _ ⟨s.val, hsΛ⟩ hsΛ', dif_pos hsΛ'] - · rw [LocalNet.combineIdx_apply_not_mem h₂ _ _ ⟨s.val, hsΛ⟩ hsΛ', dif_neg hsΛ'] - · rw [LocalNet.combineIdx_apply_not_mem h₁ _ _ s hsΛ, dif_neg hsΛ] - -/-- Splitting a `regionIdx (Λ_total \ Λ')` into its `(Λ \ Λ')` and `(Λ_total \ Λ)` parts. - Used by `restrict_restrict` to convert iterated marginalisation into a single one, - and by callers (e.g. SSA) that need to commute `combineIdx h₁ ∘ combineIdx h₂` with - `combineIdx (h₂.trans h₁)` (see `combineIdx_assoc_eq`). -/ -def restrictAssocEquiv {Λ' Λ Λ_total : Finset L.sites} - (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) : - L.regionIdx (Λ \ Λ') × L.regionIdx (Λ_total \ Λ) ≃ L.regionIdx (Λ_total \ Λ') where - toFun gb := fun ⟨s, hs⟩ => - if hsΛ : s ∈ Λ then - gb.1 ⟨s, Finset.mem_sdiff.mpr ⟨hsΛ, (Finset.mem_sdiff.mp hs).2⟩⟩ - else - gb.2 ⟨s, Finset.mem_sdiff.mpr ⟨(Finset.mem_sdiff.mp hs).1, hsΛ⟩⟩ - invFun δ := - (fun ⟨s, hs⟩ => δ ⟨s, Finset.mem_sdiff.mpr - ⟨h₁ (Finset.mem_sdiff.mp hs).1, (Finset.mem_sdiff.mp hs).2⟩⟩, - fun ⟨s, hs⟩ => δ ⟨s, Finset.mem_sdiff.mpr - ⟨(Finset.mem_sdiff.mp hs).1, fun h_in_Λ' => - (Finset.mem_sdiff.mp hs).2 (h₂ h_in_Λ')⟩⟩) - left_inv := by - rintro ⟨γ, β⟩ - ext1 - · funext ⟨s, hs⟩ - have h_in_Λ : s ∈ Λ := (Finset.mem_sdiff.mp hs).1 - simp [h_in_Λ] - · funext ⟨s, hs⟩ - have h_not_Λ : s ∉ Λ := (Finset.mem_sdiff.mp hs).2 - simp [h_not_Λ] - right_inv := by - intro δ - funext ⟨s, hs⟩ - by_cases h_in_Λ : s ∈ Λ <;> simp [h_in_Λ] - -/-- **`combineIdx` associativity**: For nested subsets `Λ' ⊆ Λ ⊆ Λ_total`, combining - `(combineIdx h₂ (a', γ), β)` via the outer subset agrees with combining - `(a', restrictAssocEquiv (γ, β))` via the composed subset `h₂.trans h₁`. - AQFT analogue of the tensor-product associativity - `(ℋ_Λ' ⊗ ℋ_{Λ\Λ'}) ⊗ ℋ_{Λ_total\Λ} ≃ ℋ_Λ' ⊗ (ℋ_{Λ\Λ'} ⊗ ℋ_{Λ_total\Λ})`. -/ -lemma combineIdx_assoc_eq {Λ' Λ Λ_total : Finset L.sites} - (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) (a' : L.regionIdx Λ') - (γ : L.regionIdx (Λ \ Λ')) (β : L.regionIdx (Λ_total \ Λ)) : - L.combineIdx h₁ (L.combineIdx h₂ (a', γ), β) = - L.combineIdx (h₂.trans h₁) (a', restrictAssocEquiv h₁ h₂ (γ, β)) := by - funext s - rw [combineIdx_assoc_aux h₁ h₂] - by_cases hsΛ' : s.val ∈ Λ' - · have hsΛ : s.val ∈ Λ := h₂ hsΛ' - rw [LocalNet.combineIdx_apply_mem (h₂.trans h₁) _ _ s hsΛ', - dif_pos hsΛ, dif_pos hsΛ'] - · rw [LocalNet.combineIdx_apply_not_mem (h₂.trans h₁) _ _ s hsΛ'] - by_cases hsΛ : s.val ∈ Λ - · rw [dif_pos hsΛ, dif_neg hsΛ'] - simp [restrictAssocEquiv, hsΛ] - · rw [dif_neg hsΛ] - simp [restrictAssocEquiv, hsΛ] - -/-- **Iterated restriction equals direct restriction**: - `restrict h₂ (restrict h₁ M) = restrict (h₂.trans h₁) M`. - - Marginalising first to `Λ` then to `Λ' ⊆ Λ` agrees with marginalising directly to `Λ'`. - AQFT statement of the partial-trace transitivity property. -/ -theorem restrict_restrict {Λ' Λ Λ_total : Finset L.sites} - (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) (M : L.localAlgebra Λ_total) : - restrict h₂ (restrict h₁ M) = restrict (h₂.trans h₁) M := by - ext a' a'' - rw [restrict_apply] - -- Reindex RHS sum via restrictAssocEquiv to a sum over the product type. - rw [show restrict (h₂.trans h₁) M a' a'' = - ∑ p : L.regionIdx (Λ \ Λ') × L.regionIdx (Λ_total \ Λ), - M (L.combineIdx (h₂.trans h₁) (a', restrictAssocEquiv h₁ h₂ p)) - (L.combineIdx (h₂.trans h₁) (a'', restrictAssocEquiv h₁ h₂ p)) from by - rw [restrict_apply] - exact ((restrictAssocEquiv h₁ h₂).sum_comp _).symm] - -- Convert the product sum to nested sums. - rw [Fintype.sum_prod_type] - -- Pointwise: rewrite the inner sums via combineIdx_assoc_eq. - refine Finset.sum_congr rfl fun γ _ => ?_ - refine Finset.sum_congr rfl fun β _ => ?_ - congr 1 - · exact combineIdx_assoc_eq h₁ h₂ a' γ β - · exact combineIdx_assoc_eq h₁ h₂ a'' γ β - -/-! ### Heisenberg-picture duality - -The matrix-level dual of the restriction: tracing `ρ` against an embedded observable -`includeAlgebra h X` equals tracing the marginal `restrict h ρ` against `X`. -/ - -/-- Entry-wise behaviour of `includeAlgebra` at combined indices: the off-diagonal - components in the complementary region vanish, leaving `X a a'` on the diagonal. -/ -@[simp] private lemma includeAlgebra_apply_combineIdx - {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) (X : L.localAlgebra Λ) - (a a' : L.regionIdx Λ) (b b' : L.regionIdx (Λ_total \ Λ)) : - L.includeAlgebra h X (L.combineIdx h (a, b)) (L.combineIdx h (a', b')) = - if b = b' then X a a' else 0 := by - simp [LocalNet.includeAlgebra_apply, Equiv.symm_apply_apply] - -/-- **Heisenberg-picture trace identity**: -`Tr(ρ · includeAlgebra h X) = Tr((restrict h ρ) · X)`. -This is the AQFT-natural form of `trace_mul_kronecker_one`. -/ -theorem trace_mul_includeAlgebra {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (ρ : L.localAlgebra Λ_total) (X : L.localAlgebra Λ) : - Tr (ρ * L.includeAlgebra h X) = Tr ((restrict h ρ) * X) := by - -- RHS expansion: ∑ a, ∑ a', ∑ b, ρ(combine (a,b), combine (a',b)) · X a' a - have rhs_expand : - Tr ((restrict h ρ) * X) = - ∑ a : L.regionIdx Λ, ∑ a' : L.regionIdx Λ, ∑ b : L.regionIdx (Λ_total \ Λ), - ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) * X a' a := by - unfold Matrix.trace - simp_rw [Matrix.diag_apply, Matrix.mul_apply, restrict_apply, Finset.sum_mul] - -- LHS: reindex outer sum via combineIdx, expand mul - have lhs_expand : - Tr (ρ * L.includeAlgebra h X) = - ∑ a : L.regionIdx Λ, ∑ b : L.regionIdx (Λ_total \ Λ), - ∑ a' : L.regionIdx Λ, - ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) * X a' a := by - unfold Matrix.trace - -- Reindex Tr over Λ_total via combineIdx⁻¹: ∑ s, M s s = ∑ (a,b), M (combine (a,b)) (combine (a,b)) - rw [show (∑ s : L.regionIdx Λ_total, (ρ * L.includeAlgebra h X).diag s) = - ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), - (ρ * L.includeAlgebra h X).diag (L.combineIdx h p) from - ((L.combineIdx h).sum_comp _).symm] - rw [Fintype.sum_prod_type] - -- Goal: ∑ a, ∑ b, (ρ * includeAlgebra h X).diag (combineIdx (a, b)) = ... - refine Finset.sum_congr rfl fun a _ => Finset.sum_congr rfl fun b _ => ?_ - -- Expand mul, then reindex inner sum (over s' : regionIdx Λ_total) via combineIdx - rw [Matrix.diag_apply, Matrix.mul_apply, - show (∑ s' : L.regionIdx Λ_total, - ρ (L.combineIdx h (a, b)) s' * - L.includeAlgebra h X s' (L.combineIdx h (a, b))) = - ∑ p : L.regionIdx Λ × L.regionIdx (Λ_total \ Λ), - ρ (L.combineIdx h (a, b)) (L.combineIdx h p) * - L.includeAlgebra h X (L.combineIdx h p) (L.combineIdx h (a, b)) from - ((L.combineIdx h).sum_comp _).symm] - rw [Fintype.sum_prod_type] - -- Now: ∑ a', ∑ b', ρ ... * (includeAlgebra h X) (combineIdx (a', b')) (combineIdx (a, b)) - -- After applying `includeAlgebra_apply_combineIdx`, the b'-sum collapses on b' = b. - refine Finset.sum_congr rfl fun a' _ => ?_ - rw [show (∑ b' : L.regionIdx (Λ_total \ Λ), - ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b')) * - L.includeAlgebra h X (L.combineIdx h (a', b')) (L.combineIdx h (a, b))) = - ρ (L.combineIdx h (a, b)) (L.combineIdx h (a', b)) * X a' a from by - rw [Finset.sum_eq_single b] - · rw [includeAlgebra_apply_combineIdx]; simp - · intro b' _ hb' - rw [includeAlgebra_apply_combineIdx, if_neg hb'] - ring - · simp] - -- Combine: LHS = ∑ a, ∑ b, ∑ a', ... = ∑ a, ∑ a', ∑ b, ... = RHS - rw [lhs_expand, rhs_expand] - refine Finset.sum_congr rfl fun a _ => ?_ - rw [Finset.sum_comm] - -end Matrix - -namespace DensityMatrix - -variable {L : LocalNet} - -/-- Restriction of a density matrix to a sub-region (= partial trace over the complement). -/ -noncomputable def restrict {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (ρ : L.densityMatrix Λ_total) : L.densityMatrix Λ := - (Matrix.QuantumChannel.restrict h : Matrix.QuantumChannel _ _) ρ - -@[simp] lemma restrict_toMatrix {Λ Λ_total : Finset L.sites} (h : Λ ⊆ Λ_total) - (ρ : L.densityMatrix Λ_total) : - (restrict h ρ).toMatrix = Matrix.restrict h ρ.toMatrix := rfl - -/-- **Iterated restriction equals direct restriction** (density-matrix form). - Marginalising first to `Λ` then to `Λ' ⊆ Λ` agrees with marginalising directly to `Λ'`. -/ -theorem restrict_restrict {Λ' Λ Λ_total : Finset L.sites} - (h₁ : Λ ⊆ Λ_total) (h₂ : Λ' ⊆ Λ) (ρ : L.densityMatrix Λ_total) : - restrict h₂ (restrict h₁ ρ) = restrict (h₂.trans h₁) ρ := by - apply DensityMatrix.ext - rw [restrict_toMatrix, restrict_toMatrix, restrict_toMatrix, - Matrix.restrict_restrict] - -end DensityMatrix - -/-! ## Paper notation: `ρ ↾ Λ` - -`ρ ↾ Λ` is the **restriction of a density matrix to a sub-region** — equivalently, the -partial trace over the complementary region. This is the AQFT-natural -form of partial trace: parameterised by the region `Λ` rather than by left/right position. - -The subset proof is auto-resolved by trying, in order: `Finset.subset_univ _` -(marginalising from the full system), `Finset.Subset.refl _` (identity), then `decide` -(explicit closed finsets). For complex hypotheses, write `DensityMatrix.restrict h ρ` directly. - -For raw `Matrix`-level work, use `Matrix.restrict h M` (no notation provided to keep `↾` -unambiguous on the density-matrix surface). -/ - -namespace LocalNet -namespace QuantumInfo - -scoped syntax:65 term:65 " ↾ " term:66 : term -scoped syntax:65 term:65 " ↾[" term "]" : term - -scoped macro_rules - | `($ρ ↾ $Λ) => - `(DensityMatrix.restrict (Λ := $Λ) - (by first | exact Finset.subset_univ _ | exact Finset.Subset.refl _ - | decide - | assumption) $ρ) - | `($ρ ↾[$h]) => `(DensityMatrix.restrict $h $ρ) - -end QuantumInfo -end LocalNet diff --git a/QuantumSystem/Channel.lean b/QuantumSystem/Channel.lean index a9a9a07..afc81bc 100644 --- a/QuantumSystem/Channel.lean +++ b/QuantumSystem/Channel.lean @@ -1,6 +1,7 @@ module public import QuantumSystem.State +public import QuantumSystem.ForMathlib.LinearAlgebra.Matrix.PartialTrace /-! # Quantum Channels (Completely Positive Trace-Preserving Maps) @@ -10,11 +11,19 @@ their basic properties. A quantum channel is a linear map Φ: M_n(ℂ) → M_m( 1. Completely positive (CP): Has a Kraus representation Φ(ρ) = Σᵢ Kᵢ ρ Kᵢ† 2. Trace-preserving (TP): Tr(Φ(A)) = Tr(A) for all A, equivalently Σᵢ Kᵢ† Kᵢ = I +It also packages the **partial trace** on product index types as a quantum channel: tracing out a +factor of a matrix on `X × Y` (or `A × B × C`) is completely positive and trace preserving, and the +trace-out-`C` channel `QuantumChannel.traceOutC` realises the `lean-eval` marginal map. + ## Main definitions * `IsTracePreserving`: A linear map preserves trace. * `IsCompletelyPositive`: A linear map has a Kraus representation. * `IsQuantumChannel`: A linear map is both CP and TP. +* `Matrix.partialTraceRightₗ` — right partial trace as a `ℂ`-linear map. +* `Matrix.QuantumChannel.partialTraceRight` — bundled quantum channel tracing out `Y`. +* `Matrix.QuantumChannel.reindex` — conjugation by an index equivalence, as a channel. +* `Matrix.QuantumChannel.traceOutC` — trace out the `C` factor of `A × B × C`. ## Mathematical Background @@ -70,7 +79,7 @@ abbrev QuantumChannel (n : Type*) (m : Type*) [Fintype n] [Fintype m] := { Φ : Matrix n n ℂ →ₗ[ℂ] Matrix m m ℂ // IsQuantumChannel Φ } /-- The identity map is a quantum channel. -/ -theorem isQuantumChannel_id : IsQuantumChannel (LinearMap.id : Matrix n n ℂ →ₗ[ℂ] Matrix n n ℂ) where +lemma isQuantumChannel_id : IsQuantumChannel (LinearMap.id : Matrix n n ℂ →ₗ[ℂ] Matrix n n ℂ) where completelyPositive := by classical -- id has Kraus representation with single operator K = I @@ -81,7 +90,7 @@ theorem isQuantumChannel_id : IsQuantumChannel (LinearMap.id : Matrix n n ℂ tracePreserving := fun _ => rfl /-- Composition of quantum channels is a quantum channel. -/ -theorem QuantumChannel.comp +lemma QuantumChannel.comp (Φ : QuantumChannel n m) (Ψ : QuantumChannel m k) : IsQuantumChannel (Ψ.val.comp Φ.val) where completelyPositive := by @@ -118,7 +127,7 @@ theorem QuantumChannel.comp omit [Fintype m] in /-- A completely positive map preserves Hermitianity of matrices. If Φ(A) = Σᵢ Kᵢ A Kᵢ† and A is Hermitian, then Φ(A) is Hermitian. -/ -theorem IsCompletelyPositive.map_isHermitian +lemma IsCompletelyPositive.map_isHermitian {Φ : Matrix n n ℂ →ₗ[ℂ] Matrix m m ℂ} (hΦ : IsCompletelyPositive Φ) {A : Matrix n n ℂ} (hA : A.IsHermitian) : (Φ A).IsHermitian := by classical @@ -160,7 +169,7 @@ private lemma matrix_eq_one_of_trace_mul [DecidableEq n] Matrix.ext_iff_trace_mul_right.mpr fun A => by rw [one_mul]; exact h A /-- Trace-preserving Kraus channels satisfy the completeness relation: ∑ₖ Kₖ† Kₖ = I. -/ -theorem QuantumChannel.kraus_sum_eq_one [DecidableEq n] +lemma QuantumChannel.kraus_sum_eq_one [DecidableEq n] (Φ : QuantumChannel n m) {r : ℕ} {K : Fin r → Matrix m n ℂ} (hK : ∀ A, Φ.val A = ∑ i, K i * A * (K i)ᴴ) : ∑ i, (K i)ᴴ * K i = 1 := by @@ -196,4 +205,186 @@ lemma stinespringIsometry_conjTranspose_mul {r : ℕ} [DecidableEq n] = (1 : Matrix n n ℂ) a b := by rw [hK] _ = if a = b then 1 else 0 := Matrix.one_apply +/-! ### Partial trace as a quantum channel (product index types) -/ + +/-! #### Positive semidefiniteness and trace facts for the partial trace -/ + +/-- The right partial trace preserves positive semidefiniteness (it is a sum of principal +submatrices). -/ +lemma traceRight_posSemidef {l n : Type*} [Fintype n] + {M : Matrix (l × n) (l × n) ℂ} (hM : M.PosSemidef) : (Matrix.traceRight M).PosSemidef := by + have hsum : Matrix.traceRight M + = ∑ k : n, M.submatrix (fun i : l => (i, k)) (fun j : l => (j, k)) := by + ext i j; simp [Matrix.traceRight_apply, Matrix.sum_apply, Matrix.submatrix_apply] + rw [hsum] + exact Matrix.posSemidef_sum _ (fun k _ => hM.submatrix _) + +/-- The left partial trace preserves positive semidefiniteness. -/ +lemma traceLeft_posSemidef {l n : Type*} [Fintype n] + {M : Matrix (n × l) (n × l) ℂ} (hM : M.PosSemidef) : (Matrix.traceLeft M).PosSemidef := by + have hsum : Matrix.traceLeft M + = ∑ k : n, M.submatrix (fun i : l => (k, i)) (fun j : l => (k, j)) := by + ext i j; simp [Matrix.traceLeft_apply, Matrix.sum_apply, Matrix.submatrix_apply] + rw [hsum] + exact Matrix.posSemidef_sum _ (fun k _ => hM.submatrix _) + +/-- Reindexing by an index equivalence preserves the trace. -/ +@[simp] lemma trace_reindex_self {n m : Type*} [Fintype n] [Fintype m] (e : n ≃ m) + (M : Matrix n n ℂ) : (M.reindex e e).trace = M.trace := by + simp only [Matrix.trace, Matrix.diag_apply, Matrix.reindex_apply, Matrix.submatrix_apply] + exact Equiv.sum_comp e.symm (fun i => M i i) + +/-- The right partial trace of the identity scales by the cardinality of the traced factor. -/ +lemma traceRight_one {X Y : Type*} [DecidableEq X] [Fintype Y] [DecidableEq Y] : + Matrix.traceRight (1 : Matrix (X × Y) (X × Y) ℂ) = (Fintype.card Y : ℂ) • (1 : Matrix X X ℂ) := by + ext i j + simp only [traceRight_apply, Matrix.smul_apply, smul_eq_mul, Matrix.one_apply, Prod.mk.injEq] + by_cases hij : i = j + · subst hij; simp [Finset.card_univ] + · simp [hij] + +/-- The left partial trace of the identity scales by the cardinality of the traced factor. -/ +lemma traceLeft_one {X Y : Type*} [Fintype X] [DecidableEq X] [DecidableEq Y] : + Matrix.traceLeft (1 : Matrix (X × Y) (X × Y) ℂ) = (Fintype.card X : ℂ) • (1 : Matrix Y Y ℂ) := by + ext i j + simp only [traceLeft_apply, Matrix.smul_apply, smul_eq_mul, Matrix.one_apply, Prod.mk.injEq] + by_cases hij : i = j + · subst hij; simp [Finset.card_univ] + · simp [hij] + +/-! #### Right partial trace as a linear map -/ + +/-- `Matrix.traceRight` as a `ℂ`-linear map `Matrix (X × Y) (X × Y) ℂ →ₗ[ℂ] Matrix X X ℂ`. -/ +noncomputable def partialTraceRightₗ {X Y : Type*} [Fintype Y] : + Matrix (X × Y) (X × Y) ℂ →ₗ[ℂ] Matrix X X ℂ where + toFun M := Matrix.traceRight M + map_add' M N := by + ext i j; simp only [traceRight_apply, Matrix.add_apply, Finset.sum_add_distrib] + map_smul' c M := by + ext i j + simp only [traceRight_apply, Matrix.smul_apply, smul_eq_mul, RingHom.id_apply, Finset.mul_sum] + +@[simp] lemma partialTraceRightₗ_apply {X Y : Type*} [Fintype Y] (M : Matrix (X × Y) (X × Y) ℂ) : + partialTraceRightₗ M = Matrix.traceRight M := rfl + +/-- `Matrix.traceLeft` as a `ℂ`-linear map `Matrix (X × Y) (X × Y) ℂ →ₗ[ℂ] Matrix Y Y ℂ`. -/ +noncomputable def partialTraceLeftₗ {X Y : Type*} [Fintype X] : + Matrix (X × Y) (X × Y) ℂ →ₗ[ℂ] Matrix Y Y ℂ where + toFun M := Matrix.traceLeft M + map_add' M N := by + ext i j; simp only [traceLeft_apply, Matrix.add_apply, Finset.sum_add_distrib] + map_smul' c M := by + ext i j + simp only [traceLeft_apply, Matrix.smul_apply, smul_eq_mul, RingHom.id_apply, Finset.mul_sum] + +@[simp] lemma partialTraceLeftₗ_apply {X Y : Type*} [Fintype X] (M : Matrix (X × Y) (X × Y) ℂ) : + partialTraceLeftₗ M = Matrix.traceLeft M := rfl + +/-! #### Kraus operators and complete positivity -/ + +/-- Kraus operator for the right partial trace, indexed by `y : Y`: `K_y x p = [p = (x, y)]`. -/ +def traceRightKraus {X Y : Type*} [DecidableEq X] [DecidableEq Y] (y : Y) : + Matrix X (X × Y) ℂ := + Matrix.of fun x p => if p = (x, y) then (1 : ℂ) else 0 + +lemma isCompletelyPositive_partialTraceRight {X Y : Type*} [Fintype X] [Fintype Y] : + IsCompletelyPositive (partialTraceRightₗ (X := X) (Y := Y)) := by + classical + refine ⟨Fintype.card Y, fun i => traceRightKraus ((Fintype.equivFin Y).symm i), fun M => ?_⟩ + rw [partialTraceRightₗ_apply] + ext i j + rw [traceRight_apply, Matrix.sum_apply, ← (Fintype.equivFin Y).symm.sum_comp + (fun y => M (i, y) (j, y))] + refine Finset.sum_congr rfl fun p _ => ?_ + -- Goal: M (i, y) (j, y) = (K_y * M * K_yᴴ) i j with `y = (equivFin Y).symm p` + -- (mul is `Matrix.mul` from `IsCompletelyPositive`). + symm + rw [Matrix.mul_apply, Finset.sum_eq_single (j, (Fintype.equivFin Y).symm p)] + · rw [Matrix.mul_apply, Finset.sum_eq_single (i, (Fintype.equivFin Y).symm p)] + · simp [traceRightKraus, Matrix.conjTranspose_apply] + · intro q _ hq + simp only [traceRightKraus, Matrix.of_apply] + rw [if_neg hq]; ring + · simp + · intro q _ hq + simp only [traceRightKraus, Matrix.conjTranspose_apply, Matrix.of_apply, + apply_ite (star · : ℂ → ℂ), star_one, star_zero] + rw [if_neg hq]; simp + · simp + +lemma isTracePreserving_partialTraceRight {X Y : Type*} [Fintype X] [Fintype Y] : + IsTracePreserving (partialTraceRightₗ (X := X) (Y := Y)) := + fun M => by rw [partialTraceRightₗ_apply]; exact trace_traceRight M + +/-- Right partial trace (trace out `Y`) as a bundled `QuantumChannel`. -/ +noncomputable def QuantumChannel.partialTraceRight {X Y : Type*} [Fintype X] [Fintype Y] : + Matrix.QuantumChannel (X × Y) X := + ⟨partialTraceRightₗ, isCompletelyPositive_partialTraceRight, isTracePreserving_partialTraceRight⟩ + +/-! #### Conjugation by an index equivalence as a channel -/ + +/-- Conjugation by a reindex `e : Z ≃ W`: `M ↦ M.submatrix e.symm e.symm`, as a linear map. -/ +noncomputable def reindexₗ {Z W : Type*} (e : Z ≃ W) : + Matrix Z Z ℂ →ₗ[ℂ] Matrix W W ℂ where + toFun M := M.submatrix e.symm e.symm + map_add' M N := by ext w w'; simp [Matrix.submatrix_apply] + map_smul' c M := by ext w w'; simp [Matrix.submatrix_apply] + +@[simp] lemma reindexₗ_apply {Z W : Type*} (e : Z ≃ W) (M : Matrix Z Z ℂ) : + reindexₗ e M = M.submatrix e.symm e.symm := rfl + +/-- Kraus operator (permutation matrix) for `reindexₗ e`: `P w z = [z = e.symm w]`. -/ +def reindexKraus {Z W : Type*} [DecidableEq Z] (e : Z ≃ W) : Matrix W Z ℂ := + Matrix.of fun w z => if z = e.symm w then (1 : ℂ) else 0 + +lemma isCompletelyPositive_reindexₗ {Z W : Type*} [Fintype Z] (e : Z ≃ W) : + IsCompletelyPositive (reindexₗ e) := by + classical + refine ⟨1, fun _ => reindexKraus e, fun M => ?_⟩ + simp only [Finset.univ_unique, Fin.default_eq_zero, Finset.sum_singleton] + ext w w' + rw [reindexₗ_apply, Matrix.submatrix_apply] + symm + rw [Matrix.mul_apply, Finset.sum_eq_single (e.symm w')] + · rw [Matrix.mul_apply, Finset.sum_eq_single (e.symm w)] + · simp [reindexKraus] + · intro q _ hq + simp only [reindexKraus, Matrix.of_apply] + rw [if_neg hq]; ring + · simp + · intro z _ hz + simp only [reindexKraus, Matrix.conjTranspose_apply, Matrix.of_apply, + apply_ite (star · : ℂ → ℂ), star_one, star_zero] + rw [if_neg hz]; simp + · simp + +lemma isTracePreserving_reindexₗ {Z W : Type*} [Fintype Z] [Fintype W] (e : Z ≃ W) : + IsTracePreserving (reindexₗ e) := by + intro M + rw [reindexₗ_apply] + exact trace_reindex_self e M + +/-- Conjugation by an index equivalence as a bundled `QuantumChannel`. -/ +noncomputable def QuantumChannel.reindex {Z W : Type*} [Fintype Z] [Fintype W] (e : Z ≃ W) : + Matrix.QuantumChannel Z W := + ⟨reindexₗ e, isCompletelyPositive_reindexₗ e, isTracePreserving_reindexₗ e⟩ + +/-! #### Trace-out-`C` channel for `A × B × C` -/ + +/-- Trace out the `C` factor of `A × B × C`, landing on `A × B`. Its action is the `lean-eval` +marginal map `M ↦ traceRight (M.reindex (prodAssoc).symm (prodAssoc).symm)`. -/ +noncomputable def QuantumChannel.traceOutC {A B C : Type*} [Fintype A] [Fintype B] [Fintype C] : + Matrix.QuantumChannel (A × B × C) (A × B) := + ⟨(partialTraceRightₗ (X := A × B) (Y := C)).comp (reindexₗ (Equiv.prodAssoc A B C).symm), + QuantumChannel.comp (QuantumChannel.reindex (Equiv.prodAssoc A B C).symm) + QuantumChannel.partialTraceRight⟩ + +@[simp] lemma QuantumChannel.traceOutC_val_apply {A B C : Type*} + [Fintype A] [Fintype B] [Fintype C] (M : Matrix (A × B × C) (A × B × C) ℂ) : + (QuantumChannel.traceOutC (A := A) (B := B) (C := C)).val M + = Matrix.traceRight + (M.reindex (Equiv.prodAssoc A B C).symm (Equiv.prodAssoc A B C).symm) := by + change partialTraceRightₗ (reindexₗ (Equiv.prodAssoc A B C).symm M) = _ + rw [partialTraceRightₗ_apply, reindexₗ_apply, Matrix.reindex_apply] + end Matrix diff --git a/QuantumSystem/ForMathlib/Algebra/Colimit/DirectLimitStar.lean b/QuantumSystem/ForMathlib/Algebra/Colimit/DirectLimitStar.lean new file mode 100644 index 0000000..b1d8f5d --- /dev/null +++ b/QuantumSystem/ForMathlib/Algebra/Colimit/DirectLimitStar.lean @@ -0,0 +1,94 @@ +module + +public import Mathlib.Algebra.Algebra.Basic +public import Mathlib.Algebra.Colimit.DirectLimit +public import Mathlib.Algebra.Star.Basic + +/-! +# Star and algebra structure on a direct limit + +The direct limit of a directed system of `*`-rings (resp. `𝕜`-algebras) with `*`-homomorphism +(resp. algebra-homomorphism) connecting maps is again a `*`-ring (resp. `𝕜`-algebra), with the +operations acting componentwise. Mathlib already provides the `Ring`/`Module` structure on +`DirectLimit`; this file adds the `Star`, `StarRing`, `Algebra` and `StarModule` instances. + +These are general facts about direct limits and are candidates for upstreaming to Mathlib. +-/ + +@[expose] public section + +namespace DirectLimit + +variable {ι : Type*} [Preorder ι] {F : ι → Type*} +variable {T : ∀ ⦃i j : ι⦄, i ≤ j → Type*} {f : ∀ _ _ h, T h} +variable [∀ i j (h : i ≤ j), FunLike (T h) (F i) (F j)] [DirectedSystem F (f · · ·)] +variable [IsDirectedOrder ι] + +section Star + +variable [∀ i, Star (F i)] [∀ i j (h : i ≤ j), StarHomClass (T h) (F i) (F j)] + +/-- Componentwise involution on a direct limit of `*`-magmas. -/ +noncomputable instance : Star (DirectLimit F f) where + star := DirectLimit.map (F₁ := F) (F₂ := F) f f (fun _ => star) + (fun _ _ h x => map_star (f _ _ h) x) + +@[simp] lemma star_mk (i) (x : F i) : + star (⟦⟨i, x⟩⟧ : DirectLimit F f) = ⟦⟨i, star x⟩⟧ := rfl + +end Star + +section StarRing + +variable [Nonempty ι] [∀ i, Ring (F i)] [∀ i, StarRing (F i)] + [∀ i j (h : i ≤ j), RingHomClass (T h) (F i) (F j)] + [∀ i j (h : i ≤ j), StarHomClass (T h) (F i) (F j)] + +/-- The direct limit of a directed system of `*`-rings is a `*`-ring. -/ +noncomputable instance : StarRing (DirectLimit F f) where + star_involutive z := by + induction z using DirectLimit.induction with + | _ i x => simp only [star_mk, star_star] + star_mul a b := by + induction a, b using DirectLimit.induction₂ with + | _ i x y => simp only [DirectLimit.mul_def, star_mk, star_mul] + star_add a b := by + induction a, b using DirectLimit.induction₂ with + | _ i x y => simp only [DirectLimit.add_def, star_mk, star_add] + +end StarRing + +section Algebra + +variable {𝕜 : Type*} [Nonempty ι] [CommSemiring 𝕜] [∀ i, Semiring (F i)] [∀ i, Algebra 𝕜 (F i)] + [∀ i j (h : i ≤ j), RingHomClass (T h) (F i) (F j)] + [∀ i j (h : i ≤ j), LinearMapClass (T h) 𝕜 (F i) (F j)] + +/-- The direct limit of a directed system of `𝕜`-algebras is a `𝕜`-algebra. -/ +noncomputable instance : Algebra 𝕜 (DirectLimit F f) := + Algebra.ofModule + (fun _ a b => by + induction a, b using DirectLimit.induction₂ with + | _ i x y => simp only [DirectLimit.smul_def, DirectLimit.mul_def, smul_mul_assoc]) + (fun _ a b => by + induction a, b using DirectLimit.induction₂ with + | _ i x y => simp only [DirectLimit.smul_def, DirectLimit.mul_def, mul_smul_comm]) + +end Algebra + +section StarModule + +variable {𝕜 : Type*} [Nonempty ι] [CommSemiring 𝕜] [Star 𝕜] [∀ i, Semiring (F i)] + [∀ i, Algebra 𝕜 (F i)] [∀ i, Star (F i)] [∀ i, StarModule 𝕜 (F i)] + [∀ i j (h : i ≤ j), LinearMapClass (T h) 𝕜 (F i) (F j)] + [∀ i j (h : i ≤ j), StarHomClass (T h) (F i) (F j)] + +/-- The involution on a direct limit of `𝕜`-`*`-algebras is conjugate-linear. -/ +instance : StarModule 𝕜 (DirectLimit F f) where + star_smul c z := by + induction z using DirectLimit.induction with + | _ i x => simp only [DirectLimit.smul_def, star_mk, star_smul] + +end StarModule + +end DirectLimit diff --git a/QuantumSystem/ForMathlib/Algebra/Star/PartialIsometry.lean b/QuantumSystem/ForMathlib/Algebra/Star/PartialIsometry.lean new file mode 100644 index 0000000..15e2238 --- /dev/null +++ b/QuantumSystem/ForMathlib/Algebra/Star/PartialIsometry.lean @@ -0,0 +1,137 @@ +module + +public import Mathlib.Algebra.Star.StarProjection +public import Mathlib.Analysis.CStarAlgebra.Basic + +/-! +# Partial isometries in a star semigroup + +A **partial isometry** in a semigroup with involution is an element `v` with `v * v⋆ * v = v`. +Its *source projection* `v⋆ * v` and *range projection* `v * v⋆` are then star projections. +Over a C⋆-ring the converse also holds: if *either* `v⋆ * v` or `v * v⋆` is a star projection, +then `v` is a partial isometry, so being a partial isometry is equivalent to each projection +condition. This equivalence is the algebraic backbone of Murray–von Neumann comparison theory +for von Neumann algebras. + +## Main definitions + +* `IsPartialIsometry v` — `v * star v * v = v`. + +## Main results + +* `IsPartialIsometry.isStarProjection_star_mul_self` / `isStarProjection_mul_star_self` — the + source and range projections `v⋆v`, `vv⋆` are star projections. +* `IsPartialIsometry.star` — the adjoint of a partial isometry is a partial isometry. +* `isPartialIsometry_of_isStarProjection_star_mul_self` / + `isPartialIsometry_of_isStarProjection_mul_star_self` — the C⋆-ring converse: a star projection + source or range projection forces `v` to be a partial isometry. +* `isPartialIsometry_iff_isStarProjection_star_mul_self` / + `isPartialIsometry_iff_isStarProjection_mul_star_self` — the resulting equivalences. +-/ + +@[expose] public section + +section Mul + +variable {R : Type*} [Mul R] [Star R] + +/-- An element `v` of a semigroup with involution is a **partial isometry** when `v * v⋆ * v = v`. +For operators on a Hilbert space this is the usual notion: `v` restricts to an isometry on the +orthogonal complement of its kernel. -/ +def IsPartialIsometry (v : R) : Prop := v * star v * v = v + +/-- Every star projection is a partial isometry (with itself as source and range). -/ +lemma IsStarProjection.isPartialIsometry {p : R} (hp : IsStarProjection p) : + IsPartialIsometry p := by + unfold IsPartialIsometry + rw [hp.isSelfAdjoint.star_eq, hp.isIdempotentElem.eq, hp.isIdempotentElem.eq] + +end Mul + +section Semigroup + +variable {R : Type*} [Semigroup R] [StarMul R] + +namespace IsPartialIsometry + +/-- The source projection `v⋆ * v` of a partial isometry is a star projection. -/ +theorem isStarProjection_star_mul_self {v : R} (h : IsPartialIsometry v) : + IsStarProjection (star v * v) := + ⟨by calc (star v * v) * (star v * v) = star v * (v * star v * v) := by simp only [mul_assoc] + _ = star v * v := by rw [h], + IsSelfAdjoint.star_mul_self v⟩ + +/-- The range projection `v * v⋆` of a partial isometry is a star projection. -/ +theorem isStarProjection_mul_star_self {v : R} (h : IsPartialIsometry v) : + IsStarProjection (v * star v) := + ⟨by calc (v * star v) * (v * star v) = (v * star v * v) * star v := by simp only [mul_assoc] + _ = v * star v := by rw [h], + IsSelfAdjoint.mul_star_self v⟩ + +end IsPartialIsometry + +/-- The adjoint of a partial isometry is a partial isometry. -/ +protected theorem IsPartialIsometry.star {v : R} (h : IsPartialIsometry v) : + IsPartialIsometry (star v) := by + unfold IsPartialIsometry at * + rw [star_star] + calc Star.star v * v * Star.star v = Star.star (v * Star.star v * v) := by + rw [star_mul, star_mul, star_star, mul_assoc] + _ = Star.star v := by rw [h] + +end Semigroup + +section CStarRing + +variable {R : Type*} [NonUnitalNormedRing R] [StarRing R] [CStarRing R] + +/-- The C⋆-ring converse to `IsPartialIsometry.isStarProjection_star_mul_self`: if the source +projection `v⋆ * v` is a star projection, then `v` is a partial isometry. With `a := v - v v⋆ v` +the idempotence of `v⋆ * v` gives `a⋆ * a = 0`, and the C⋆-identity `‖a‖² = ‖a⋆ a‖` forces +`a = 0`. -/ +theorem isPartialIsometry_of_isStarProjection_star_mul_self {v : R} + (h : IsStarProjection (star v * v)) : IsPartialIsometry v := by + have hidem : star v * v * (star v * v) = star v * v := h.isIdempotentElem.eq + have hstar : star (v * star v * v) = star v * v * star v := by + rw [star_mul, star_mul, star_star, mul_assoc] + have key : star (v - v * star v * v) * (v - v * star v * v) = 0 := by + rw [star_sub, hstar] + have expand : (star v - star v * v * star v) * (v - v * star v * v) = + star v * v - star v * v * (star v * v) - star v * v * (star v * v) + + star v * v * (star v * v) * (star v * v) := by + simp only [mul_sub, sub_mul, mul_assoc] + abel + rw [expand] + simp only [hidem] + abel + have hnorm : ‖v - v * star v * v‖ = 0 := by + have hmul := CStarRing.norm_star_mul_self (x := v - v * star v * v) + rw [key, norm_zero] at hmul + exact mul_self_eq_zero.mp hmul.symm + have hzero : v - v * star v * v = 0 := norm_eq_zero.mp hnorm + exact (sub_eq_zero.mp hzero).symm + +/-- The C⋆-ring converse to `IsPartialIsometry.isStarProjection_mul_star_self`: if the range +projection `v * v⋆` is a star projection, then `v` is a partial isometry. This is the source +statement applied to `v⋆`. -/ +theorem isPartialIsometry_of_isStarProjection_mul_star_self {v : R} + (h : IsStarProjection (v * star v)) : IsPartialIsometry v := by + have h' : IsStarProjection (star (star v) * star v) := by rwa [star_star] + have hv := IsPartialIsometry.star (isPartialIsometry_of_isStarProjection_star_mul_self h') + rwa [star_star] at hv + +/-- In a C⋆-ring, `v` is a partial isometry iff its source projection `v⋆ * v` is a star +projection. -/ +theorem isPartialIsometry_iff_isStarProjection_star_mul_self {v : R} : + IsPartialIsometry v ↔ IsStarProjection (star v * v) := + ⟨IsPartialIsometry.isStarProjection_star_mul_self, + isPartialIsometry_of_isStarProjection_star_mul_self⟩ + +/-- In a C⋆-ring, `v` is a partial isometry iff its range projection `v * v⋆` is a star +projection. -/ +theorem isPartialIsometry_iff_isStarProjection_mul_star_self {v : R} : + IsPartialIsometry v ↔ IsStarProjection (v * star v) := + ⟨IsPartialIsometry.isStarProjection_mul_star_self, + isPartialIsometry_of_isStarProjection_mul_star_self⟩ + +end CStarRing diff --git a/QuantumSystem/ForMathlib/Analysis/CStarAlgebra/DirectLimit.lean b/QuantumSystem/ForMathlib/Analysis/CStarAlgebra/DirectLimit.lean new file mode 100644 index 0000000..3a7bb20 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/CStarAlgebra/DirectLimit.lean @@ -0,0 +1,73 @@ +module + +public import Mathlib.Algebra.Colimit.DirectLimit +public import Mathlib.Analysis.CStarAlgebra.Hom +public import Mathlib.Analysis.Normed.Unbundled.RingSeminorm + +/-! +# The C⋆-inductive limit of a directed system of C⋆-algebras + +For a directed system of (complex) C⋆-algebras whose connecting maps are *injective* unital +`*`-homomorphisms, the connecting maps are isometric (`NonUnitalStarAlgHom.norm_map`), so the +algebraic direct limit carries a well-defined C⋆-norm `‖⟦⟨i, x⟩⟧‖ = ‖x‖`. Its completion is the +**C⋆-inductive limit** (Bratteli–Robinson Vol.1 §2.6), a `CStarAlgebra`. + +This file provides the norm `cstarNorm`, the bundled `cstarRingNorm`, and the resulting +`NormedRing` structure (`cstarNormedRing`) on `DirectLimit F f`, all parametrised by injectivity +of the connecting maps. The `*`-algebra-over-`ℂ` structure is supplied by `DirectLimitStar`. + +These are general facts and are candidates for upstreaming to Mathlib. +-/ + +@[expose] public section + +namespace DirectLimit + +variable {ι : Type*} [Preorder ι] [IsDirectedOrder ι] [Nonempty ι] {F : ι → Type*} + {T : ∀ ⦃i j : ι⦄, i ≤ j → Type*} {f : ∀ _ _ h, T h} + [∀ i j (h : i ≤ j), FunLike (T h) (F i) (F j)] [DirectedSystem F (f · · ·)] + [∀ i, CStarAlgebra (F i)] [∀ i j (h : i ≤ j), AlgHomClass (T h) ℂ (F i) (F j)] + [∀ i j (h : i ≤ j), StarHomClass (T h) (F i) (F j)] + (hf : ∀ i j (h : i ≤ j), Function.Injective (f i j h)) + +/-- The C⋆-norm on the direct limit: `‖⟦⟨i, x⟩⟧‖ = ‖x‖`, well defined since the connecting maps — + being injective unital `*`-homomorphisms of C⋆-algebras — are isometric + (`NonUnitalStarAlgHom.norm_map`). -/ +noncomputable def cstarNorm : DirectLimit F f → ℝ := + DirectLimit.lift f (fun _ x => ‖x‖) + (fun i j h x => (NonUnitalStarAlgHom.norm_map (f i j h) (hf i j h) x).symm) + +omit [Nonempty ι] in +@[simp] lemma cstarNorm_mk (i) (x : F i) : + cstarNorm hf (⟦⟨i, x⟩⟧ : DirectLimit F f) = ‖x‖ := rfl + +/-- The bundled ring norm on the direct limit. -/ +noncomputable def cstarRingNorm : RingNorm (DirectLimit F f) where + toFun := cstarNorm hf + map_zero' := by + rw [show (0 : DirectLimit F f) = ⟦⟨Classical.arbitrary _, 0⟩⟧ from + DirectLimit.zero_def _, cstarNorm_mk, norm_zero] + add_le' a b := by + induction a, b using DirectLimit.induction₂ with + | _ i x y => rw [DirectLimit.add_def, cstarNorm_mk, cstarNorm_mk, cstarNorm_mk] + exact norm_add_le x y + neg' a := by + induction a using DirectLimit.induction with + | _ i x => rw [DirectLimit.neg_def, cstarNorm_mk, cstarNorm_mk, norm_neg] + mul_le' a b := by + induction a, b using DirectLimit.induction₂ with + | _ i x y => rw [DirectLimit.mul_def, cstarNorm_mk, cstarNorm_mk, cstarNorm_mk] + exact norm_mul_le x y + eq_zero_of_map_eq_zero' a := by + induction a using DirectLimit.induction with + | _ i x => + intro hx + rw [cstarNorm_mk, norm_eq_zero] at hx + rw [hx] + exact (DirectLimit.zero_def i).symm + +/-- The `NormedRing` structure on the direct limit induced by the C⋆-norm. -/ +@[reducible] noncomputable def cstarNormedRing : NormedRing (DirectLimit F f) := + (cstarRingNorm hf).toNormedRing + +end DirectLimit diff --git a/QuantumSystem/ForMathlib/Analysis/CStarAlgebra/HilbertSpace.lean b/QuantumSystem/ForMathlib/Analysis/CStarAlgebra/HilbertSpace.lean index 0b950bc..de84600 100644 --- a/QuantumSystem/ForMathlib/Analysis/CStarAlgebra/HilbertSpace.lean +++ b/QuantumSystem/ForMathlib/Analysis/CStarAlgebra/HilbertSpace.lean @@ -4,6 +4,8 @@ public import Mathlib.Analysis.CStarAlgebra.ContinuousLinearMap @[expose] public section +open scoped InnerProductSpace + /-- A complex *pre*-Hilbert space: a normed space with a complex inner product. -/ class ComplexPreHilbertSpace (H : Type*) extends NormedAddCommGroup H, InnerProductSpace ℂ H @@ -18,8 +20,11 @@ variable (H : Type*) [ComplexHilbertSpace H] /-- The space of bounded linear operators on a complex Hilbert space. -/ abbrev BoundedLinearOperator := H →L[ℂ] H -/-- Notation for bounded linear operators on a Hilbert space. -/ -notation:50 "𝓑(" H ")" => BoundedLinearOperator H +/-- Notation `𝓑(H)` for the bounded linear operators on a Hilbert space, living in the opt-in +`ComplexHilbertSpace` scope; activate it with `open scoped ComplexHilbertSpace`. This is the +type-level counterpart of the von Neumann algebra `𝓑(H)` of `Algebra.VonNeumannAlgebra.Basic` +(they denote the same object B(H) at different levels; see `boundedLinearOperators.starAlgEquiv`). -/ +scoped notation:max "𝓑(" H ")" => BoundedLinearOperator H noncomputable instance : NonUnitalCStarAlgebra (𝓑(H)) := inferInstance @@ -30,6 +35,11 @@ noncomputable instance instPreComplexHilbertSpace [ComplexHilbertSpace H] : Comp end ComplexHilbertSpace +/-- `ℂ` is a complex Hilbert space over itself — the one-dimensional one. It is the smallest +nondegenerate space on which the bundled `ComplexHilbertSpace` interfaces can be exercised, and +the class has no instance for it otherwise, since its three parents are found separately. -/ +noncomputable instance : ComplexHilbertSpace ℂ where + section UnitaryMap variable {H K : Type*} [ComplexHilbertSpace H] [ComplexHilbertSpace K] @@ -57,9 +67,34 @@ noncomputable def asUnitary (U : H ≃ₗᵢ[ℂ] K) : UnitaryMap H K where ext y simp [LinearIsometryEquiv.adjoint_eq_symm] +namespace UnitaryMap + /-- Unitary maps preserve the inner product. -/ -lemma inner_map_eq (U : UnitaryMap H K) (x y : H) : inner ℂ (U.toContinuousLinearMap x) (U.toContinuousLinearMap y) = inner ℂ x y := by +lemma inner_map_eq (U : UnitaryMap H K) (x y : H) : + ⟪U.toContinuousLinearMap x, U.toContinuousLinearMap y⟫_ℂ = ⟪x, y⟫_ℂ := by simp only [← ContinuousLinearMap.adjoint_inner_right, ← ContinuousLinearMap.comp_apply, U.adjoint_comp, ContinuousLinearMap.one_apply] +/-- A unitary map between complex Hilbert spaces, viewed as a linear isometric +equivalence. The forward map is `U.toContinuousLinearMap` and its inverse is the +adjoint `U.toContinuousLinearMap.adjoint`; the relations `U†U = 1` and `UU† = 1` +make these mutually inverse, and `inner_map_eq` makes the map an isometry. -/ +noncomputable def toLinearIsometryEquiv (U : UnitaryMap H K) : H ≃ₗᵢ[ℂ] K := + LinearEquiv.isometryOfInner + { U.toContinuousLinearMap.toLinearMap with + invFun := U.toContinuousLinearMap.adjoint + left_inv := fun x => by + simpa using congrArg (fun f : H →L[ℂ] H => f x) U.adjoint_comp + right_inv := fun y => by + simpa using congrArg (fun f : K →L[ℂ] K => f y) U.comp_adjoint } + U.inner_map_eq + +@[simp] lemma toLinearIsometryEquiv_apply (U : UnitaryMap H K) (x : H) : + U.toLinearIsometryEquiv x = U.toContinuousLinearMap x := rfl + +@[simp] lemma toLinearIsometryEquiv_symm_apply (U : UnitaryMap H K) (y : K) : + U.toLinearIsometryEquiv.symm y = U.toContinuousLinearMap.adjoint y := rfl + +end UnitaryMap + end UnitaryMap diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/Adjoint.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/Adjoint.lean deleted file mode 100644 index b8fe2f5..0000000 --- a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/Adjoint.lean +++ /dev/null @@ -1,30 +0,0 @@ -module - -public import Mathlib.Analysis.InnerProductSpace.Adjoint - -/-! -# Self-adjoint operators on inner product spaces - -This file contains results about self-adjoint operators on inner product spaces. --/ - -@[expose] public section - -open scoped InnerProductSpace - -variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [CompleteSpace E] - -namespace ContinuousLinearMap - -/-- For a self-adjoint operator T, if ⟪Tx, x⟫ = 0 for all x, then T = 0. -This is a corollary of the polarization identity `LinearMap.IsSymmetric.inner_map_self_eq_zero`. -/ -theorem IsSelfAdjoint.eq_zero_of_inner_map_self_eq_zero {T : E →L[ℂ] E} - (hsa : IsSelfAdjoint T) (h : ∀ x, ⟪T x, x⟫_ℂ = 0) : T = 0 := by - have hT_sym : (T : E →ₗ[ℂ] E).IsSymmetric := hsa.isSymmetric - have h' : (T : E →ₗ[ℂ] E) = 0 := hT_sym.inner_map_self_eq_zero.mp h - ext x - have := congrFun (congrArg DFunLike.coe h') x - simp only [LinearMap.zero_apply, ContinuousLinearMap.coe_coe] at this - exact this - -end ContinuousLinearMap diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/AdjointNotation.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/AdjointNotation.lean new file mode 100644 index 0000000..af41e72 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/AdjointNotation.lean @@ -0,0 +1,35 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.Adjoint + +@[expose] public section + +/-! +# Dagger notation for the adjoint of a continuous linear map + +This module introduces the postfix notation `T†` for `ContinuousLinearMap.adjoint T`, +the Hilbert-space adjoint of a continuous linear map between complex (or `RCLike`) +inner product spaces. + +The dagger `†` is the standard symbol for the adjoint throughout the operator-algebra +and quantum-physics literature (Bratteli–Robinson, Takesaki, Haag), where one writes +`T†` rather than the long-form `ContinuousLinearMap.adjoint T`. Keeping the formal +statements in this notation lets them read like the source texts. + +The notation lives in the dedicated `Adjoint` scope, so it is opt-in: activate it with +`open scoped Adjoint`. + +| Symbol | Expansion | How to activate | +|---|---|---| +| `T†` | `ContinuousLinearMap.adjoint T` | `open scoped Adjoint` | + +`†` binds at maximum precedence, so it attaches to the immediately preceding atom: +write `(f ∘L g)†` and `(R.π a)†` with explicit parentheses, exactly as for `⁻¹`. +-/ + +namespace Adjoint + +/-- `T†` denotes the Hilbert-space adjoint `ContinuousLinearMap.adjoint T`. -/ +scoped postfix:max "†" => ContinuousLinearMap.adjoint + +end Adjoint diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/DiagonalAmplification.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/DiagonalAmplification.lean index cc7ac56..f4edae8 100644 --- a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/DiagonalAmplification.lean +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/DiagonalAmplification.lean @@ -89,7 +89,7 @@ lemma single_apply {n : ℕ} (i : Fin n) (v : H) (k : Fin n) : /-- Diagonal action of an operator `T : H →L[ℂ] H` on `H^n`. -/ noncomputable def diagonal {n : ℕ} (T : H →L[ℂ] H) : Hn (H := H) n →L[ℂ] Hn (H := H) n := by classical - exact ∑ i : Fin n, (single (H := H) (n := n) i).comp (T.comp (proj (H := H) (n := n) i)) + exact ∑ i : Fin n, single (H := H) (n := n) i ∘L (T ∘L proj (H := H) (n := n) i) @[simp] lemma diagonal_apply {n : ℕ} (T : H →L[ℂ] H) (x : Hn (H := H) n) (i : Fin n) : @@ -109,7 +109,7 @@ lemma diagonal_apply {n : ℕ} (T : H →L[ℂ] H) (x : Hn (H := H) n) (i : Fin /-- The projection of an operator `S` on `H^n` to its `(i, j)`-th component in `B(H)`. -/ noncomputable def matrixComponent {n : ℕ} (S : Hn (H := H) n →L[ℂ] Hn (H := H) n) (i j : Fin n) : H →L[ℂ] H := - (proj (H := H) (n := n) i).comp (S.comp (single (H := H) (n := n) j)) + proj (H := H) (n := n) i ∘L (S ∘L single (H := H) (n := n) j) @[simp] lemma matrixComponent_apply {n : ℕ} (S : Hn (H := H) n →L[ℂ] Hn (H := H) n) @@ -225,7 +225,8 @@ lemma diagonal_star {n : ℕ} (T : H →L[ℂ] H) : (ContinuousLinearMap.adjoint_inner_left (A := T) (x := y.ofLp i) (y := x.ofLp i)) /-- `diagonal` as a `StarAlgHom` (so we can map `StarSubalgebra`s). -/ -noncomputable def diagonalStarAlgHom (n : ℕ) : (H →L[ℂ] H) →⋆ₐ[ℂ] (Hn (H := H) n →L[ℂ] Hn (H := H) n) where +noncomputable def diagonalStarAlgHom (n : ℕ) : + (H →L[ℂ] H) →⋆ₐ[ℂ] (Hn (H := H) n →L[ℂ] Hn (H := H) n) where toFun := fun T => diagonal (H := H) (n := n) T map_one' := by ext x i diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/FiniteRank.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/FiniteRank.lean new file mode 100644 index 0000000..8e878fb --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/FiniteRank.lean @@ -0,0 +1,150 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.Adjoint + +/-! +# Finite-rank operators as a non-unital `*`-subalgebra + +An operator on a Hilbert space has *finite rank* if its range is finite-dimensional. The +finite-rank operators form a `*`-subalgebra `F(H)` of `B(H)` which, on an infinite-dimensional +`H`, does **not** contain `1` and yet acts non-degenerately: it is the standard witness that +non-degeneracy is strictly weaker than unitality, and hence that the non-unital form of the +bicommutant theorem has content the unital form does not reach. + +The only step that is not immediate is closure under the adjoint. It uses that `T†` annihilates +`(ran T)ᗮ` (`ContinuousLinearMap.orthogonal_range`), so that `ran T† = T†(ran T)` is the image of +a finite-dimensional subspace. + +## Main definitions + +* `InnerProductSpace.IsFiniteRank`: the operator has finite-dimensional range. +* `InnerProductSpace.finiteRankOperators`: the finite-rank operators, bundled as a + `NonUnitalStarSubalgebra ℂ (H →L[ℂ] H)`. + +## Main results + +* `InnerProductSpace.rankOne_mem_finiteRankOperators`: every rank-one operator `|x⟩⟨y|` lies in + `F(H)`. +* `InnerProductSpace.one_notMem_finiteRankOperators`: on an infinite-dimensional `H`, `1 ∉ F(H)`. + +The results combining `F(H)` with the rest of the bicommutant development — that it acts +non-degenerately, and that `F(H)' = ℂ1` and hence `F(H)'' = B(H)` — live in +`QuantumSystem.Algebra.Star.DoubleCommutant.TFAE`: they consume declarations from other +`ForMathlib` files, and `ForMathlib` files import Mathlib only. +-/ + +@[expose] public section + +namespace InnerProductSpace + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] + +/-- An operator has **finite rank** if its range is finite-dimensional. -/ +def IsFiniteRank (T : H →L[ℂ] H) : Prop := + FiniteDimensional ℂ (LinearMap.range (T : H →ₗ[ℂ] H)) + +lemma isFiniteRank_zero : IsFiniteRank (0 : H →L[ℂ] H) := by + have h : LinearMap.range ((0 : H →L[ℂ] H) : H →ₗ[ℂ] H) = ⊥ := by simp + rw [IsFiniteRank, h] + infer_instance + +lemma IsFiniteRank.add {S T : H →L[ℂ] H} (hS : IsFiniteRank S) (hT : IsFiniteRank T) : + IsFiniteRank (S + T) := by + haveI : FiniteDimensional ℂ (LinearMap.range (S : H →ₗ[ℂ] H)) := hS + haveI : FiniteDimensional ℂ (LinearMap.range (T : H →ₗ[ℂ] H)) := hT + refine Submodule.finiteDimensional_of_le (S₂ := LinearMap.range (S : H →ₗ[ℂ] H) ⊔ + LinearMap.range (T : H →ₗ[ℂ] H)) ?_ + rintro _ ⟨x, rfl⟩ + exact Submodule.add_mem_sup ⟨x, rfl⟩ ⟨x, rfl⟩ + +/-- Multiplying a finite-rank operator on the left keeps the rank finite: the range only shrinks +under the image. -/ +lemma IsFiniteRank.mul_left {T : H →L[ℂ] H} (hT : IsFiniteRank T) (S : H →L[ℂ] H) : + IsFiniteRank (S * T) := by + haveI : FiniteDimensional ℂ (LinearMap.range (T : H →ₗ[ℂ] H)) := hT + refine Submodule.finiteDimensional_of_le + (S₂ := (LinearMap.range (T : H →ₗ[ℂ] H)).map (S : H →ₗ[ℂ] H)) ?_ + rintro _ ⟨x, rfl⟩ + exact ⟨T x, ⟨x, rfl⟩, rfl⟩ + +/-- Multiplying a finite-rank operator on the right keeps the rank finite: the range is unchanged +or smaller. -/ +lemma IsFiniteRank.mul_right {T : H →L[ℂ] H} (hT : IsFiniteRank T) (S : H →L[ℂ] H) : + IsFiniteRank (T * S) := by + haveI : FiniteDimensional ℂ (LinearMap.range (T : H →ₗ[ℂ] H)) := hT + refine Submodule.finiteDimensional_of_le (S₂ := LinearMap.range (T : H →ₗ[ℂ] H)) ?_ + rintro _ ⟨x, rfl⟩ + exact ⟨S x, rfl⟩ + +lemma IsFiniteRank.smul {T : H →L[ℂ] H} (hT : IsFiniteRank T) (c : ℂ) : + IsFiniteRank (c • T) := by + haveI : FiniteDimensional ℂ (LinearMap.range (T : H →ₗ[ℂ] H)) := hT + refine Submodule.finiteDimensional_of_le (S₂ := LinearMap.range (T : H →ₗ[ℂ] H)) ?_ + rintro _ ⟨x, rfl⟩ + exact Submodule.smul_mem _ _ ⟨x, rfl⟩ + +/-- The rank-one operator `|x⟩⟨y| : z ↦ ⟪y, z⟫ • x` has finite rank: its range lies in +`span {x}`. -/ +lemma isFiniteRank_rankOne (x y : H) : IsFiniteRank (rankOne ℂ x y : H →L[ℂ] H) := by + refine Submodule.finiteDimensional_of_le (S₂ := Submodule.span ℂ {x}) ?_ + rintro _ ⟨z, rfl⟩ + simp only [ContinuousLinearMap.coe_coe, rankOne_apply] + exact Submodule.smul_mem _ _ (Submodule.mem_span_singleton_self x) + +variable [CompleteSpace H] + +/-- The adjoint of a finite-rank operator has finite rank. Since `T†` annihilates `(ran T)ᗮ`, its +range is the image of the finite-dimensional subspace `ran T`. -/ +lemma IsFiniteRank.adjoint {T : H →L[ℂ] H} (hT : IsFiniteRank T) : + IsFiniteRank (ContinuousLinearMap.adjoint T) := by + haveI : FiniteDimensional ℂ (LinearMap.range (T : H →ₗ[ℂ] H)) := hT + refine Submodule.finiteDimensional_of_le + (S₂ := (LinearMap.range (T : H →ₗ[ℂ] H)).map + ((ContinuousLinearMap.adjoint T : H →L[ℂ] H) : H →ₗ[ℂ] H)) ?_ + rintro _ ⟨x, rfl⟩ + obtain ⟨p, hp, q, hq, rfl⟩ := + Submodule.exists_add_mem_mem_orthogonal (K := LinearMap.range (T : H →ₗ[ℂ] H)) x + have hq0 : ContinuousLinearMap.adjoint T q = 0 := by + have hmem : q ∈ LinearMap.ker ((ContinuousLinearMap.adjoint T : H →L[ℂ] H) : H →ₗ[ℂ] H) := by + rw [← ContinuousLinearMap.orthogonal_range] + exact hq + simpa using hmem + exact ⟨p, hp, by simp [map_add, hq0]⟩ + +/-- **The finite-rank operators `F(H)`**, as a non-unital `*`-subalgebra of `B(H)`. It is a +two-sided ideal, but only the `*`-subalgebra structure is recorded here — that is what the +bicommutant theorem consumes. -/ +noncomputable def finiteRankOperators : NonUnitalStarSubalgebra ℂ (H →L[ℂ] H) where + carrier := {T | IsFiniteRank T} + add_mem' := IsFiniteRank.add + zero_mem' := isFiniteRank_zero + mul_mem' := fun {S _} _ hT => hT.mul_left S + smul_mem' := fun c _ hT => IsFiniteRank.smul hT c + star_mem' := fun hT => IsFiniteRank.adjoint hT + +@[simp] lemma mem_finiteRankOperators_iff {T : H →L[ℂ] H} : + T ∈ finiteRankOperators (H := H) ↔ IsFiniteRank T := Iff.rfl + +/-- Every rank-one operator `|x⟩⟨y|` lies in `F(H)`. -/ +lemma rankOne_mem_finiteRankOperators (x y : H) : + (rankOne ℂ x y : H →L[ℂ] H) ∈ finiteRankOperators (H := H) := + isFiniteRank_rankOne x y + +/-- **`F(H)` is not unital** when `H` is infinite-dimensional: `1` has range `H`. + +Together with `InnerProductSpace.actsNondegenerately_finiteRankOperators` (proved in +`QuantumSystem.Algebra.Star.DoubleCommutant.TFAE`) this exhibits a `*`-subalgebra to which the +non-unital bicommutant theorem applies and the unital one does not. -/ +theorem one_notMem_finiteRankOperators (h : ¬ FiniteDimensional ℂ H) : + (1 : H →L[ℂ] H) ∉ finiteRankOperators (H := H) := by + intro hmem + refine h ?_ + have hr : LinearMap.range ((1 : H →L[ℂ] H) : H →ₗ[ℂ] H) = ⊤ := by + ext y + simp only [Submodule.mem_top, iff_true, LinearMap.mem_range] + exact ⟨y, rfl⟩ + haveI : FiniteDimensional ℂ (LinearMap.range ((1 : H →L[ℂ] H) : H →ₗ[ℂ] H)) := hmem + rw [hr] at this + exact (Submodule.topEquiv (R := ℂ) (M := H)).finiteDimensional + +end InnerProductSpace diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/InvariantSubspace.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/InvariantSubspace.lean index 31a5205..80ee6f4 100644 --- a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/InvariantSubspace.lean +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/InvariantSubspace.lean @@ -26,6 +26,8 @@ This file provides a minimal API for invariant / reducing subspaces for a set of `Kᗮ` is invariant under `T`. * `starProjection_mem_centralizer_of_isReducing`: if `K` is reducing for `S`, then `K.starProjection ∈ Set.centralizer S`. +* `ActsNondegenerately S`: no nonzero vector is annihilated by every element of `S`; the + hypothesis of the non-unital double commutant theorem. -/ @[expose] public section @@ -33,6 +35,7 @@ This file provides a minimal API for invariant / reducing subspaces for a set of namespace InnerProductSpace local notation "⟪" x ", " y "⟫" => inner ℂ x y +local postfix:max "†" => ContinuousLinearMap.adjoint variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] @@ -137,25 +140,25 @@ variable [CompleteSpace H] /-- A subspace `K` is reducing for a set of operators `S` if it is invariant under every operator in `S` and also invariant under every adjoint operator. -/ def IsReducing (S : Set (H →L[ℂ] H)) (K : Submodule ℂ H) : Prop := - ∀ T ∈ S, IsInvariant T K ∧ IsInvariant (ContinuousLinearMap.adjoint T) K + ∀ T ∈ S, IsInvariant T K ∧ IsInvariant (T†) K /-- If `K` is invariant under `T†`, then `Kᗮ` is invariant under `T`. -/ lemma orthogonalComplement_invariant_of_adjoint_invariant {T : H →L[ℂ] H} {K : Submodule ℂ H} - (hK : IsInvariant (ContinuousLinearMap.adjoint T) K) : IsInvariant T Kᗮ := by + (hK : IsInvariant (T†) K) : IsInvariant T Kᗮ := by -- Unfold to the pointwise characterization. refine (IsInvariant.iff_forall_mem (T := T) (K := Kᗮ)).2 ?_ intro y hy -- Show `T y ∈ Kᗮ` via the inner-product characterization. refine (K.mem_orthogonal (T y)).2 ?_ intro x hx - have hx' : (ContinuousLinearMap.adjoint T) x ∈ K := - (IsInvariant.iff_forall_mem (T := ContinuousLinearMap.adjoint T) (K := K)).1 hK x hx + have hx' : (T†) x ∈ K := + (IsInvariant.iff_forall_mem (T := T†) (K := K)).1 hK x hx -- `y ∈ Kᗮ` implies `⟪(T†) x, y⟫ = 0`, hence also `⟪x, T y⟫ = 0`. - have hy0 : ⟪(ContinuousLinearMap.adjoint T) x, y⟫ = 0 := - (K.mem_orthogonal y).1 hy ((ContinuousLinearMap.adjoint T) x) hx' + have hy0 : ⟪(T†) x, y⟫ = 0 := + (K.mem_orthogonal y).1 hy ((T†) x) hx' -- Use adjointness: `⟪(T†) x, y⟫ = ⟪x, T y⟫`. - have hAdj : ⟪x, T y⟫ = ⟪(ContinuousLinearMap.adjoint T) x, y⟫ := by + have hAdj : ⟪x, T y⟫ = ⟪(T†) x, y⟫ := by -- `adjoint_inner_left` is: `⟪(T†) y, x⟫ = ⟪y, T x⟫`. simpa using (ContinuousLinearMap.adjoint_inner_left (A := T) (x := y) (y := x)).symm exact hAdj.trans hy0 @@ -167,11 +170,37 @@ lemma starProjection_mem_centralizer_of_isReducing (hK : IsReducing S K) : K.starProjection ∈ Set.centralizer S := by intro T hT have hInv : IsInvariant T K := (hK T hT).1 - have hInvAdj : IsInvariant (ContinuousLinearMap.adjoint T) K := (hK T hT).2 + have hInvAdj : IsInvariant (T†) K := (hK T hT).2 have hInvOrth : IsInvariant T Kᗮ := orthogonalComplement_invariant_of_adjoint_invariant (T := T) hInvAdj exact commutes_starProjection_of_invariant (T := T) (K := K) hInv hInvOrth end WithComplete +section Nondegenerate + +/-- A set of operators `S` *acts non-degenerately* on `H` if the only vector annihilated by +every element of `S` is `0`. + +This is strictly weaker than `1 ∈ S` (see `actsNondegenerately_of_one_mem`): for example the +compact operators act non-degenerately on an infinite-dimensional `H` without containing `1`. +It is the hypothesis under which the double commutant theorem holds for a possibly non-unital +`*`-subalgebra. -/ +def ActsNondegenerately (S : Set (H →L[ℂ] H)) : Prop := + ∀ x : H, (∀ T ∈ S, T x = 0) → x = 0 + +/-- A set of operators containing the identity acts non-degenerately. -/ +lemma actsNondegenerately_of_one_mem {S : Set (H →L[ℂ] H)} + (h : (1 : H →L[ℂ] H) ∈ S) : ActsNondegenerately S := by + intro x hx + simpa using hx 1 h + +/-- Non-degeneracy is monotone: it passes from a set to any superset. -/ +lemma ActsNondegenerately.mono {S S' : Set (H →L[ℂ] H)} (hSS' : S ⊆ S') + (hS : ActsNondegenerately S) : ActsNondegenerately S' := by + intro x hx + exact hS x fun T hT => hx T (hSS' hT) + +end Nondegenerate + end InnerProductSpace diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/RankOne.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/RankOne.lean new file mode 100644 index 0000000..2bc06b2 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/RankOne.lean @@ -0,0 +1,46 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.LinearMap + +/-! +# Operators commuting with all rank-one operators are scalar + +A continuous linear operator on an inner product space that commutes with every rank-one operator +`|x⟩⟨y|` is a scalar multiple of the identity. This is the elementary computation behind +"the centre of `B(H)` is trivial" and, downstream, behind the factor property of the tensor +von Neumann algebras `B(H₁) ⊗̄ 1` and `1 ⊗̄ B(H₂)`. That a rank-one operator has finite- (in fact +at most one-) dimensional range is recorded as +`InnerProductSpace.isFiniteRank_rankOne` in +`QuantumSystem.ForMathlib.Analysis.InnerProductSpace.FiniteRank`. + +## Notation + +`⊗̄` in the prose above is documentation shorthand for the von Neumann (spatial) tensor product of +algebras; that convention is stated in full in `QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`, +downstream of this file, where the algebras it names are defined. +-/ + +@[expose] public section + +/-- A continuous linear operator commuting with every rank-one operator `|x⟩⟨y|` is a scalar +multiple of the identity: testing the commutation relation on a fixed nonzero vector `y` yields +`⟪y, y⟫ • S x = ⟪y, S y⟫ • x` for *every* `x`, so `S = (⟪y, S y⟫ / ⟪y, y⟫) • 1`. -/ +theorem ContinuousLinearMap.exists_eq_smul_one_of_forall_rankOne_comm + {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] {S : H →L[ℂ] H} + (h : ∀ x y : H, S ∘L InnerProductSpace.rankOne ℂ x y + = InnerProductSpace.rankOne ℂ x y ∘L S) : + ∃ c : ℂ, S = c • 1 := by + by_cases hH : ∀ y : H, y = 0 + · refine ⟨0, ContinuousLinearMap.ext fun x => ?_⟩ + rw [hH x] + simp + push Not at hH + obtain ⟨y, hy⟩ := hH + have hyy : (inner ℂ y y : ℂ) ≠ 0 := inner_self_ne_zero.mpr hy + refine ⟨inner ℂ y (S y) / inner ℂ y y, ContinuousLinearMap.ext fun x => ?_⟩ + have h1 := congrArg (fun L : H →L[ℂ] H => L y) (h x y) + simp only [ContinuousLinearMap.comp_apply, InnerProductSpace.rankOne_apply, map_smul] at h1 + rw [ContinuousLinearMap.smul_apply, ContinuousLinearMap.one_apply] + calc S x = (inner ℂ y y)⁻¹ • ((inner ℂ y y : ℂ) • S x) := (inv_smul_smul₀ hyy _).symm + _ = (inner ℂ y y)⁻¹ • (inner ℂ y (S y) • x) := by rw [h1] + _ = (inner ℂ y (S y) / inner ℂ y y) • x := by rw [smul_smul, div_eq_inv_mul] diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProduct.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProduct.lean index 6859b7c..eb12cdb 100644 --- a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProduct.lean +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProduct.lean @@ -89,3 +89,75 @@ private lemma tensor_add_right (A : H →L[ℂ] H) (B₁ B₂ : K →L[ℂ] K) : | add a b ha hb => simp [map_add, ha, hb] end ContinuousLinearMap + +/-! ### Tensor factorisation of Euclidean spaces along an index bijection + +For an index bijection `e : m × n ≃ p`, the Hilbert-space tensor product +`EuclideanSpace 𝕜 m ⊗ EuclideanSpace 𝕜 n` is isometrically the Euclidean space +`EuclideanSpace 𝕜 p`. This realises, at the level of Hilbert spaces, the factorisation +underlying any bijective splitting of the index set. -/ + +section EuclideanTensor + +open WithLp + +variable {𝕜 : Type*} [RCLike 𝕜] {m n p : Type*} + [Fintype m] [Fintype n] [Fintype p] [DecidableEq m] [DecidableEq n] [DecidableEq p] + +/-- The Hilbert-space tensor factorisation along an index bijection `e : m × n ≃ p`: the tensor +product of `EuclideanSpace 𝕜 m` and `EuclideanSpace 𝕜 n`, transported along `e`, as a linear +isometry equivalence onto `EuclideanSpace 𝕜 p`. Built as the orthonormal-basis representation of +the (reindexed) tensor of the standard bases. -/ +noncomputable def EuclideanSpace.tensorEquiv (e : (m × n) ≃ p) : + EuclideanSpace 𝕜 m ⊗[𝕜] EuclideanSpace 𝕜 n ≃ₗᵢ[𝕜] EuclideanSpace 𝕜 p := + (((EuclideanSpace.basisFun m 𝕜).tensorProduct (EuclideanSpace.basisFun n 𝕜)).reindex e).repr + +/-- The tensor factorisation sends a pure tensor of standard basis vectors to the standard basis +vector at the combined index. -/ +@[simp] +lemma EuclideanSpace.tensorEquiv_single_tmul (e : (m × n) ≃ p) (i : m) (j : n) : + EuclideanSpace.tensorEquiv (𝕜 := 𝕜) e + (EuclideanSpace.single i (1 : 𝕜) ⊗ₜ[𝕜] EuclideanSpace.single j (1 : 𝕜)) + = EuclideanSpace.single (e (i, j)) (1 : 𝕜) := by + have hb : + (((EuclideanSpace.basisFun m 𝕜).tensorProduct (EuclideanSpace.basisFun n 𝕜)).reindex e) + (e (i, j)) + = EuclideanSpace.single i (1 : 𝕜) ⊗ₜ[𝕜] EuclideanSpace.single j (1 : 𝕜) := by + rw [OrthonormalBasis.reindex_apply, Equiv.symm_apply_apply, + OrthonormalBasis.tensorProduct_apply, EuclideanSpace.basisFun_apply, + EuclideanSpace.basisFun_apply] + change (((EuclideanSpace.basisFun m 𝕜).tensorProduct (EuclideanSpace.basisFun n 𝕜)).reindex e).repr + (EuclideanSpace.single i (1 : 𝕜) ⊗ₜ[𝕜] EuclideanSpace.single j (1 : 𝕜)) = _ + rw [← hb, OrthonormalBasis.repr_self] + +end EuclideanTensor + +section EuclideanTensorCoord + +open WithLp + +variable {𝕜 : Type*} [RCLike 𝕜] {m n p : Type*} + [Fintype m] [Fintype n] [Fintype p] + +/-- Coordinate formula for the tensor factorisation on a pure tensor: the `k`-coordinate of +`tensorEquiv e (w₁ ⊗ w₂)` is the product of the `(e.symm k).1`-coordinate of `w₁` and the +`(e.symm k).2`-coordinate of `w₂`. -/ +lemma EuclideanSpace.ofLp_tensorEquiv_tmul (e : (m × n) ≃ p) (w₁ : EuclideanSpace 𝕜 m) + (w₂ : EuclideanSpace 𝕜 n) (k : p) : + ofLp (EuclideanSpace.tensorEquiv (𝕜 := 𝕜) e (w₁ ⊗ₜ[𝕜] w₂)) k + = ofLp w₁ (e.symm k).1 * ofLp w₂ (e.symm k).2 := by + classical + have hk : EuclideanSpace.single k (1 : 𝕜) + = EuclideanSpace.tensorEquiv (𝕜 := 𝕜) e + (EuclideanSpace.single (e.symm k).1 (1 : 𝕜) ⊗ₜ[𝕜] + EuclideanSpace.single (e.symm k).2 (1 : 𝕜)) := by + rw [EuclideanSpace.tensorEquiv_single_tmul, Prod.mk.eta, Equiv.apply_symm_apply] + have hofLp : ofLp (EuclideanSpace.tensorEquiv (𝕜 := 𝕜) e (w₁ ⊗ₜ[𝕜] w₂)) k + = inner 𝕜 (EuclideanSpace.single k (1 : 𝕜)) + (EuclideanSpace.tensorEquiv (𝕜 := 𝕜) e (w₁ ⊗ₜ[𝕜] w₂)) := by + rw [EuclideanSpace.inner_single_left, map_one, one_mul] + rw [hofLp, hk, LinearIsometryEquiv.inner_map_map, TensorProduct.inner_tmul, + EuclideanSpace.inner_single_left, EuclideanSpace.inner_single_left] + simp + +end EuclideanTensorCoord diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProductCompletion.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProductCompletion.lean new file mode 100644 index 0000000..0a71984 --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/TensorProductCompletion.lean @@ -0,0 +1,854 @@ +module + +public import Mathlib.Analysis.InnerProductSpace.Completion +public import Mathlib.Analysis.InnerProductSpace.TensorProduct +public import Mathlib.Analysis.InnerProductSpace.l2Space +public import Mathlib.RingTheory.TensorProduct.Finite +public import Mathlib.Analysis.Normed.Module.FiniteDimension +public import Mathlib.Topology.Algebra.LinearMapCompletion + +/-! +# The completed Hilbert-space tensor product and operator amplification + +For complex inner product spaces `H₁` and `H₂`, Mathlib equips the *algebraic* tensor product +`H₁ ⊗[ℂ] H₂` with an inner product (`TensorProduct.instInnerProductSpace`), with +`⟪a ⊗ₜ b, c ⊗ₜ d⟫ = ⟪a, c⟫ * ⟪b, d⟫`, but it is left incomplete (an explicit `TODO` in +`Mathlib/Analysis/InnerProductSpace/TensorProduct.lean`). This file packages its completion + +`HilbertTensor H₁ H₂ := UniformSpace.Completion (H₁ ⊗[ℂ] H₂)`, + +which inherits an `InnerProductSpace ℂ` structure from +`UniformSpace.Completion.innerProductSpace` and is complete, hence a genuine complex Hilbert +space — the object physicists write `H₁ ⊗ H₂` for Hilbert spaces. + +It then constructs the **amplifications** of bounded operators, + +`amplifyLeft : B(H₁) → B(HilbertTensor H₁ H₂)`, `A ↦ A ⊗̂ 1`, +`amplifyRight : B(H₂) → B(HilbertTensor H₁ H₂)`, `B ↦ 1 ⊗̂ B`, + +obtained by extending the bounded operator `A ⊗ 1` (resp. `1 ⊗ B`) from the dense algebraic +tensor product to the completion. The key analytic input is the cross-norm bound +`‖(A ⊗ 1) z‖ ≤ ‖A‖ * ‖z‖`, proved by writing `z` along an orthonormal family of the second +factor. + +## Notation + +The textbook symbols `⊗̂` (completed tensor) and `⊗ₕ` (pure tensor) live in the opt-in +`HilbertTensor` scope; activate them with `open scoped HilbertTensor`. + +| Symbol | Expansion | How to activate | +|---|---|---| +| `H₁ ⊗̂ H₂` | `HilbertTensor H₁ H₂` | `open scoped HilbertTensor` | +| `x ⊗ₕ y` | `HilbertTensor.tmul x y` | `open scoped HilbertTensor` | + +## Main definitions + +* `HilbertTensor H₁ H₂` — the completed Hilbert-space tensor product. +* `HilbertTensor.tmul x y` — the pure tensor `x ⊗ y` viewed in the completion. +* `HilbertTensor.amplifyLeft A`, `HilbertTensor.amplifyRight B` — the amplified operators. +* `HilbertTensor.amplifyLeftₐ`, `HilbertTensor.amplifyRightₐ` — the amplifications bundled as + unital `⋆`-algebra homomorphisms (on genuine Hilbert spaces). + +## Main results + +* `HilbertTensor.inner_tmul` — the inner product on pure tensors factorises. +* `HilbertTensor.add_tmul` / `tmul_add` / `tmul_smul_left` / `smul_tmul_right` — `tmul` is + bilinear. +* `TensorProduct.norm_map_left_le` / `norm_map_right_le` — the cross-norm bounds on the + algebraic tensor product. +* `HilbertTensor.amplifyLeft_tmul` / `amplifyRight_tmul` — the action on pure tensors. +* `HilbertTensor.amplifyLeft_one` / `amplifyLeft_mul` / `amplifyLeft_add` / `amplifyLeft_smul` + (and the right analogues) — the amplifications are unital `ℂ`-algebra homomorphisms. +-/ + +@[expose] public section + +open scoped TensorProduct + +/-! ### Completion of a linear isometric equivalence and of a finite-dimensional space + +These are general facts about `UniformSpace.Completion` phrased for normed spaces; they are the +analytic inputs to the constructions below. The completion of a linear isometric equivalence +extends it to the completions, and the completion of a finite-dimensional space is again +finite-dimensional. -/ + +section Completion + +open UniformSpace UniformSpace.Completion + +namespace LinearIsometryEquiv + +variable {𝕜 E F : Type*} [NontriviallyNormedField 𝕜] + [NormedAddCommGroup E] [NormedSpace 𝕜 E] [NormedAddCommGroup F] [NormedSpace 𝕜 F] + +/-- The completed forward map of a linear isometric equivalence, as a continuous linear map. -/ +noncomputable def completionCLM (f : E ≃ₗᵢ[𝕜] F) : Completion E →L[𝕜] Completion F := + f.toLinearIsometry.toContinuousLinearMap.completion + +@[simp] lemma completionCLM_coe (f : E ≃ₗᵢ[𝕜] F) (a : E) : + f.completionCLM (a : Completion E) = (f a : Completion F) := by + rw [completionCLM, ContinuousLinearMap.completion_apply_coe, + LinearIsometry.coe_toContinuousLinearMap, LinearIsometryEquiv.coe_toLinearIsometry] + +lemma completionCLM_left (f : E ≃ₗᵢ[𝕜] F) (x : Completion E) : + f.symm.completionCLM (f.completionCLM x) = x := by + induction x using Completion.induction_on with + | hp => exact isClosed_eq ((map_continuous _).comp (map_continuous _)) continuous_id + | ih a => rw [completionCLM_coe, completionCLM_coe, LinearIsometryEquiv.symm_apply_apply] + +/-- The completion of a linear isometric equivalence `f : E ≃ₗᵢ[𝕜] F`, a linear isometric +equivalence `Completion E ≃ₗᵢ[𝕜] Completion F`. -/ +noncomputable def completion (f : E ≃ₗᵢ[𝕜] F) : Completion E ≃ₗᵢ[𝕜] Completion F where + toFun := f.completionCLM + invFun := f.symm.completionCLM + map_add' x y := _root_.map_add f.completionCLM x y + map_smul' m x := _root_.map_smul f.completionCLM m x + left_inv := f.completionCLM_left + right_inv x := by + have h := f.symm.completionCLM_left x + rwa [LinearIsometryEquiv.symm_symm] at h + norm_map' x := by + induction x using Completion.induction_on with + | hp => exact isClosed_eq (continuous_norm.comp f.completionCLM.continuous) continuous_norm + | ih a => + change ‖f.completionCLM (a : Completion E)‖ = ‖(a : Completion E)‖ + rw [completionCLM_coe, Completion.norm_coe, Completion.norm_coe, f.norm_map] + +@[simp] lemma completion_coe (f : E ≃ₗᵢ[𝕜] F) (a : E) : + f.completion (a : Completion E) = (f a : Completion F) := by + change f.completionCLM (a : Completion E) = _ + exact f.completionCLM_coe a + +end LinearIsometryEquiv + +/-- The completion of a finite-dimensional normed space (over a complete field) is +finite-dimensional. The coercion `toComplL : E → Completion E` is a linear map with dense range +whose image is a finite-dimensional (hence closed) subspace, so it is surjective, and +finite-dimensionality transfers along a surjection. -/ +lemma FiniteDimensional.completion {𝕜 E : Type*} [NontriviallyNormedField 𝕜] [CompleteSpace 𝕜] + [NormedAddCommGroup E] [NormedSpace 𝕜 E] [FiniteDimensional 𝕜 E] : + FiniteDimensional 𝕜 (Completion E) := by + set f : E →L[𝕜] Completion E := Completion.toComplL with hf + have hdense : DenseRange f := by + simpa [hf, Completion.coe_toComplL] using Completion.denseRange_coe (α := E) + have hclosed : IsClosed (Set.range f) := by + have h := (LinearMap.range (f : E →ₗ[𝕜] Completion E)).closed_of_finiteDimensional + rwa [LinearMap.coe_range] at h + have hsurj : Function.Surjective f := by + have hu : Set.range f = Set.univ := by rw [← hclosed.closure_eq, hdense.closure_eq] + exact Set.range_eq_univ.mp hu + exact Module.Finite.of_surjective (f : E →ₗ[𝕜] Completion E) hsurj + +end Completion + +variable {H₁ H₂ : Type*} + [NormedAddCommGroup H₁] [InnerProductSpace ℂ H₁] + [NormedAddCommGroup H₂] [InnerProductSpace ℂ H₂] + +/-- The **completed Hilbert-space tensor product** of two complex inner product spaces: the +completion of the algebraic tensor product `H₁ ⊗[ℂ] H₂` with respect to the inner-product norm +`‖x ⊗ₜ y‖ = ‖x‖ * ‖y‖`. As the completion of an inner product space it is again an +`InnerProductSpace ℂ`, and being a completion it is a `CompleteSpace`, so it is a complex +Hilbert space. -/ +abbrev HilbertTensor (H₁ H₂ : Type*) + [NormedAddCommGroup H₁] [InnerProductSpace ℂ H₁] + [NormedAddCommGroup H₂] [InnerProductSpace ℂ H₂] : Type _ := + UniformSpace.Completion (H₁ ⊗[ℂ] H₂) + +/-- The completed tensor product of two finite-dimensional Hilbert spaces is finite-dimensional: +the algebraic tensor product is already finite-dimensional (hence complete), so completing it +changes nothing. -/ +instance instFiniteDimensionalHilbertTensor [FiniteDimensional ℂ H₁] [FiniteDimensional ℂ H₂] : + FiniteDimensional ℂ (HilbertTensor H₁ H₂) := by + haveI : FiniteDimensional ℂ (H₁ ⊗[ℂ] H₂) := Module.Finite.tensorProduct ℂ H₁ H₂ + exact FiniteDimensional.completion + +namespace HilbertTensor + +/-- A pure tensor `x ⊗ y` regarded as an element of the completed tensor product. -/ +noncomputable def tmul (x : H₁) (y : H₂) : HilbertTensor H₁ H₂ := + ((x ⊗ₜ[ℂ] y : H₁ ⊗[ℂ] H₂) : HilbertTensor H₁ H₂) + +/-- `H₁ ⊗̂ H₂` denotes the completed Hilbert-space tensor product `HilbertTensor H₁ H₂`. The hat +distinguishes it from Mathlib's algebraic tensor product `H₁ ⊗[ℂ] H₂`. -/ +scoped infixr:35 " ⊗̂ " => HilbertTensor + +/-- `x ⊗ₕ y` denotes the pure tensor `HilbertTensor.tmul x y` in the completed tensor product. The +subscript `h` (Hilbert) distinguishes it from Mathlib's algebraic pure tensor `x ⊗ₜ y`. -/ +scoped infixr:100 " ⊗ₕ " => HilbertTensor.tmul + +/-- The inner product of two pure tensors in the completed tensor product factorises as the +product of the inner products of the factors. -/ +@[simp] theorem inner_tmul (x x' : H₁) (y y' : H₂) : + inner ℂ (x ⊗ₕ y) (x' ⊗ₕ y') = inner ℂ x x' * inner ℂ y y' := by + rw [tmul, tmul, UniformSpace.Completion.inner_coe, TensorProduct.inner_tmul] + +/-- The norm of a pure tensor factorises. -/ +@[simp] lemma norm_tmul (x : H₁) (y : H₂) : ‖x ⊗ₕ y‖ = ‖x‖ * ‖y‖ := by + rw [tmul, UniformSpace.Completion.norm_coe, TensorProduct.norm_tmul] + +/-- The pure tensor is additive in its right argument. -/ +theorem tmul_add (x : H₁) (y y' : H₂) : x ⊗ₕ (y + y') = x ⊗ₕ y + x ⊗ₕ y' := by + rw [tmul, tmul, tmul, ← UniformSpace.Completion.coe_add, TensorProduct.tmul_add] + +/-- The pure tensor is additive in its left argument. -/ +theorem add_tmul (x x' : H₁) (y : H₂) : (x + x') ⊗ₕ y = x ⊗ₕ y + x' ⊗ₕ y := by + rw [tmul, tmul, tmul, ← UniformSpace.Completion.coe_add, TensorProduct.add_tmul] + +/-- A scalar in the left argument of a pure tensor factors out. -/ +theorem tmul_smul_left (c : ℂ) (x : H₁) (y : H₂) : (c • x) ⊗ₕ y = c • x ⊗ₕ y := by + rw [tmul, tmul, ← UniformSpace.Completion.coe_smul, TensorProduct.smul_tmul'] + +/-- A scalar in the right argument of a pure tensor factors out. -/ +theorem smul_tmul_right (c : ℂ) (x : H₁) (y : H₂) : x ⊗ₕ (c • y) = c • x ⊗ₕ y := by + rw [tmul, tmul, ← UniformSpace.Completion.coe_smul, TensorProduct.tmul_smul] + +/-- A pure tensor with a zero left argument vanishes. -/ +@[simp] lemma zero_tmul (y : H₂) : (0 : H₁) ⊗ₕ y = 0 := by + rw [tmul, TensorProduct.zero_tmul, UniformSpace.Completion.coe_zero] + +/-- A pure tensor with a zero right argument vanishes. -/ +@[simp] lemma tmul_zero (x : H₁) : x ⊗ₕ (0 : H₂) = 0 := by + rw [tmul, TensorProduct.tmul_zero, UniformSpace.Completion.coe_zero] + +/-- The bounded inclusion `H₁ → H₁ ⊗̂ H₂`, `x ↦ x ⊗ z`, for a fixed `z ∈ H₂`. Its operator norm is +`‖z‖` (`‖tmulLeftL z x‖ = ‖x‖ * ‖z‖`), so it is an isometry exactly when `‖z‖ = 1`. This is the +mirror image of `tmulRightL`, fixing the second factor instead of the first. -/ +noncomputable def tmulLeftL (z : H₂) : H₁ →L[ℂ] HilbertTensor H₁ H₂ := + UniformSpace.Completion.toComplL.comp + (LinearMap.mkContinuous ((TensorProduct.mk ℂ H₁ H₂).flip z) ‖z‖ fun x => by + rw [LinearMap.flip_apply, TensorProduct.mk_apply, TensorProduct.norm_tmul, mul_comm]) + +@[simp] lemma tmulLeftL_apply (z : H₂) (x : H₁) : tmulLeftL z x = tmul x z := by + rw [tmulLeftL, ContinuousLinearMap.comp_apply, LinearMap.mkContinuous_apply, + LinearMap.flip_apply, TensorProduct.mk_apply] + rfl + +end HilbertTensor + +namespace TensorProduct + +/-- Every element of the algebraic tensor product can be written as a finite sum +`∑ ξ i ⊗ₜ e i` where the second factors `e i` form an orthonormal family of `H₂`. This is the +analytic normal form behind the cross-norm bound: choose a finite-dimensional submodule of `H₂` +carrying the element and take an orthonormal basis of it. -/ +lemma exists_orthonormal_rep (z : H₁ ⊗[ℂ] H₂) : + ∃ (n : ℕ) (e : Fin n → H₂) (ξ : Fin n → H₁), + Orthonormal ℂ e ∧ z = ∑ i, ξ i ⊗ₜ[ℂ] e i := by + obtain ⟨N', hN'fin, hz⟩ := + TensorProduct.exists_finite_submodule_right_of_setFinite {z} (Set.finite_singleton z) + obtain ⟨z₀, hz₀⟩ := hz (Set.mem_singleton z) + haveI : FiniteDimensional ℂ N' := hN'fin + let b := stdOrthonormalBasis ℂ N' + set ξ : Fin (Module.finrank ℂ N') → H₁ := + fun i => TensorProduct.equivFinsuppOfBasisRight b.toBasis z₀ i with hξ + have hrep : z₀ = ∑ i, ξ i ⊗ₜ[ℂ] b.toBasis i := by + conv_lhs => rw [← (TensorProduct.equivFinsuppOfBasisRight b.toBasis).symm_apply_apply z₀] + rw [TensorProduct.equivFinsuppOfBasisRight_symm_apply, + Finsupp.sum_fintype _ _ (fun i => by simp)] + refine ⟨Module.finrank ℂ N', fun i => (b i : H₂), ξ, + b.orthonormal.comp_linearIsometry N'.subtypeₗᵢ, ?_⟩ + calc z = (LinearMap.lTensor H₁ N'.subtype) z₀ := hz₀.symm + _ = (LinearMap.lTensor H₁ N'.subtype) (∑ i, ξ i ⊗ₜ[ℂ] b.toBasis i) := by rw [← hrep] + _ = ∑ i, ξ i ⊗ₜ[ℂ] (b i : H₂) := by + rw [map_sum]; simp_rw [LinearMap.lTensor_tmul]; rfl + +/-- **Cross-norm bound (left factor).** On the algebraic Hilbert tensor product, amplifying a +bounded operator `A` on the first factor by the identity does not increase the norm beyond a +factor of `‖A‖`: `‖(A ⊗ 1) z‖ ≤ ‖A‖ * ‖z‖`. -/ +theorem norm_map_left_le (A : H₁ →L[ℂ] H₁) (z : H₁ ⊗[ℂ] H₂) : + ‖TensorProduct.map A.toLinearMap LinearMap.id z‖ ≤ ‖A‖ * ‖z‖ := by + obtain ⟨n, e, ξ, he, rfl⟩ := exists_orthonormal_rep z + classical + have key : ∀ (η : Fin n → H₁), ‖∑ i, η i ⊗ₜ[ℂ] e i‖ ^ 2 = ∑ i, ‖η i‖ ^ 2 := by + intro η + rw [← inner_self_eq_norm_sq (𝕜 := ℂ)] + have h : inner ℂ (∑ i, η i ⊗ₜ[ℂ] e i) (∑ i, η i ⊗ₜ[ℂ] e i) + = ∑ i, inner ℂ (η i) (η i) := by + rw [sum_inner] + simp_rw [inner_sum, TensorProduct.inner_tmul, orthonormal_iff_ite.mp he] + simp [Finset.sum_ite_eq] + rw [h, map_sum] + simp_rw [inner_self_eq_norm_sq (𝕜 := ℂ)] + rw [map_sum] + simp_rw [TensorProduct.map_tmul, LinearMap.id_coe, id_eq, ContinuousLinearMap.coe_coe] + rw [← Real.sqrt_sq (norm_nonneg _), ← Real.sqrt_sq (mul_nonneg (norm_nonneg A) (norm_nonneg _))] + apply Real.sqrt_le_sqrt + rw [mul_pow, key, key, Finset.mul_sum] + apply Finset.sum_le_sum + intro i _ + rw [← mul_pow] + gcongr + exact A.le_opNorm (ξ i) + +/-- The commutation isometry intertwines the two one-sided amplifications on the algebraic +tensor product: swapping the factors turns `1 ⊗ B` into `B ⊗ 1`. -/ +lemma commIsometry_map_id (B : H₂ →L[ℂ] H₂) (z : H₁ ⊗[ℂ] H₂) : + commIsometry ℂ H₁ H₂ (TensorProduct.map LinearMap.id B.toLinearMap z) + = TensorProduct.map B.toLinearMap LinearMap.id (commIsometry ℂ H₁ H₂ z) := by + induction z using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp + | add a b ha hb => simp [map_add, ha, hb] + +/-- **Cross-norm bound (right factor).** `‖(1 ⊗ B) z‖ ≤ ‖B‖ * ‖z‖`, obtained from the left bound +by conjugating with the commutation isometry. -/ +theorem norm_map_right_le (B : H₂ →L[ℂ] H₂) (z : H₁ ⊗[ℂ] H₂) : + ‖TensorProduct.map LinearMap.id B.toLinearMap z‖ ≤ ‖B‖ * ‖z‖ := by + rw [← (commIsometry ℂ H₁ H₂).norm_map (TensorProduct.map LinearMap.id B.toLinearMap z), + commIsometry_map_id, ← (commIsometry ℂ H₁ H₂).norm_map z] + exact norm_map_left_le B _ + +end TensorProduct + +namespace HilbertTensor + +open UniformSpace + +/-- The amplification `A ⊗ 1` as a bounded operator on the *algebraic* tensor product, packaged +from the cross-norm bound `TensorProduct.norm_map_left_le`. -/ +noncomputable def algAmplifyLeft (A : H₁ →L[ℂ] H₁) : (H₁ ⊗[ℂ] H₂) →L[ℂ] (H₁ ⊗[ℂ] H₂) := + LinearMap.mkContinuous (TensorProduct.map A.toLinearMap LinearMap.id) ‖A‖ + (TensorProduct.norm_map_left_le A) + +/-- The amplification `1 ⊗ B` as a bounded operator on the *algebraic* tensor product. -/ +noncomputable def algAmplifyRight (B : H₂ →L[ℂ] H₂) : (H₁ ⊗[ℂ] H₂) →L[ℂ] (H₁ ⊗[ℂ] H₂) := + LinearMap.mkContinuous (TensorProduct.map LinearMap.id B.toLinearMap) ‖B‖ + (TensorProduct.norm_map_right_le B) + +@[simp] lemma algAmplifyLeft_tmul (A : H₁ →L[ℂ] H₁) (x : H₁) (y : H₂) : + algAmplifyLeft (H₂ := H₂) A (x ⊗ₜ[ℂ] y) = (A x) ⊗ₜ[ℂ] y := by + simp [algAmplifyLeft] + +@[simp] lemma algAmplifyRight_tmul (B : H₂ →L[ℂ] H₂) (x : H₁) (y : H₂) : + algAmplifyRight (H₁ := H₁) B (x ⊗ₜ[ℂ] y) = x ⊗ₜ[ℂ] (B y) := by + simp [algAmplifyRight] + +/-- The **left amplification** `A ↦ A ⊗̂ 1` of a bounded operator on the first factor to a bounded +operator on the completed Hilbert tensor product, obtained by extending `algAmplifyLeft A` from +the dense algebraic tensor product to the completion. -/ +noncomputable def amplifyLeft (A : H₁ →L[ℂ] H₁) : + HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂ where + toFun := Completion.map (algAmplifyLeft A) + map_add' x y := by + refine Completion.induction_on₂ x y + (isClosed_eq ((Completion.continuous_map (f := algAmplifyLeft A)).comp continuous_add) + (((Completion.continuous_map (f := algAmplifyLeft A)).comp continuous_fst).add + ((Completion.continuous_map (f := algAmplifyLeft A)).comp continuous_snd))) + (fun a b => ?_) + rw [← Completion.coe_add, Completion.map_coe (algAmplifyLeft A).uniformContinuous, + Completion.map_coe (algAmplifyLeft A).uniformContinuous, + Completion.map_coe (algAmplifyLeft A).uniformContinuous, ← Completion.coe_add, map_add] + map_smul' c x := by + refine Completion.induction_on x + (isClosed_eq + ((Completion.continuous_map (f := algAmplifyLeft A)).comp (continuous_const_smul c)) + ((continuous_const_smul c).comp (Completion.continuous_map (f := algAmplifyLeft A)))) + (fun a => ?_) + rw [← Completion.coe_smul, Completion.map_coe (algAmplifyLeft A).uniformContinuous, + Completion.map_coe (algAmplifyLeft A).uniformContinuous, ← Completion.coe_smul, map_smul] + rfl + cont := Completion.continuous_map + +/-- The **right amplification** `B ↦ 1 ⊗̂ B`. -/ +noncomputable def amplifyRight (B : H₂ →L[ℂ] H₂) : + HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂ where + toFun := Completion.map (algAmplifyRight B) + map_add' x y := by + refine Completion.induction_on₂ x y + (isClosed_eq ((Completion.continuous_map (f := algAmplifyRight B)).comp continuous_add) + (((Completion.continuous_map (f := algAmplifyRight B)).comp continuous_fst).add + ((Completion.continuous_map (f := algAmplifyRight B)).comp continuous_snd))) + (fun a b => ?_) + rw [← Completion.coe_add, Completion.map_coe (algAmplifyRight B).uniformContinuous, + Completion.map_coe (algAmplifyRight B).uniformContinuous, + Completion.map_coe (algAmplifyRight B).uniformContinuous, ← Completion.coe_add, map_add] + map_smul' c x := by + refine Completion.induction_on x + (isClosed_eq + ((Completion.continuous_map (f := algAmplifyRight B)).comp (continuous_const_smul c)) + ((continuous_const_smul c).comp (Completion.continuous_map (f := algAmplifyRight B)))) + (fun a => ?_) + rw [← Completion.coe_smul, Completion.map_coe (algAmplifyRight B).uniformContinuous, + Completion.map_coe (algAmplifyRight B).uniformContinuous, ← Completion.coe_smul, map_smul] + rfl + cont := Completion.continuous_map + +@[simp] theorem amplifyLeft_tmul (A : H₁ →L[ℂ] H₁) (x : H₁) (y : H₂) : + amplifyLeft A (x ⊗ₕ y) = (A x) ⊗ₕ y := by + rw [amplifyLeft, tmul] + change Completion.map (algAmplifyLeft A) _ = _ + rw [Completion.map_coe (algAmplifyLeft A).uniformContinuous, algAmplifyLeft_tmul, tmul] + +@[simp] theorem amplifyRight_tmul (B : H₂ →L[ℂ] H₂) (x : H₁) (y : H₂) : + amplifyRight B (x ⊗ₕ y) = x ⊗ₕ (B y) := by + rw [amplifyRight, tmul] + change Completion.map (algAmplifyRight B) _ = _ + rw [Completion.map_coe (algAmplifyRight B).uniformContinuous, algAmplifyRight_tmul, tmul] + +/-- The defining action of the left amplification on the image of the algebraic tensor product. -/ +@[simp] lemma amplifyLeft_coe (A : H₁ →L[ℂ] H₁) (a : H₁ ⊗[ℂ] H₂) : + amplifyLeft A (a : HilbertTensor H₁ H₂) + = ((algAmplifyLeft A a : H₁ ⊗[ℂ] H₂) : HilbertTensor H₁ H₂) := + Completion.map_coe (algAmplifyLeft A).uniformContinuous a + +/-- The defining action of the right amplification on the image of the algebraic tensor product. -/ +@[simp] lemma amplifyRight_coe (B : H₂ →L[ℂ] H₂) (a : H₁ ⊗[ℂ] H₂) : + amplifyRight B (a : HilbertTensor H₁ H₂) + = ((algAmplifyRight B a : H₁ ⊗[ℂ] H₂) : HilbertTensor H₁ H₂) := + Completion.map_coe (algAmplifyRight B).uniformContinuous a + +/-! ### Multiplicativity and unitality + +The amplifications are unital algebra homomorphisms onto their images: they send the identity to +the identity and turn composition in `B(H₁)` (resp. `B(H₂)`) into composition in +`B(HilbertTensor H₁ H₂)`. -/ + +lemma algAmplifyLeft_one_apply (a : H₁ ⊗[ℂ] H₂) : + algAmplifyLeft (H₂ := H₂) (1 : H₁ →L[ℂ] H₁) a = a := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp + | add p q hp hq => simp [map_add, hp, hq] + +lemma algAmplifyLeft_mul_apply (A B : H₁ →L[ℂ] H₁) (a : H₁ ⊗[ℂ] H₂) : + algAmplifyLeft (A * B) a = algAmplifyLeft A (algAmplifyLeft B a) := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.mul_apply] + | add p q hp hq => simp [map_add, hp, hq] + +lemma algAmplifyRight_one_apply (a : H₁ ⊗[ℂ] H₂) : + algAmplifyRight (H₁ := H₁) (1 : H₂ →L[ℂ] H₂) a = a := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp + | add p q hp hq => simp [map_add, hp, hq] + +lemma algAmplifyRight_mul_apply (A B : H₂ →L[ℂ] H₂) (a : H₁ ⊗[ℂ] H₂) : + algAmplifyRight (A * B) a = algAmplifyRight A (algAmplifyRight B a) := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.mul_apply] + | add p q hp hq => simp [map_add, hp, hq] + +@[simp] theorem amplifyLeft_one : + amplifyLeft (1 : H₁ →L[ℂ] H₁) = (1 : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyLeft _).continuous (ContinuousLinearMap.continuous 1)) (fun a => ?_) + simp [algAmplifyLeft_one_apply] + +theorem amplifyLeft_mul (A B : H₁ →L[ℂ] H₁) : + amplifyLeft (H₂ := H₂) (A * B) = amplifyLeft A * amplifyLeft B := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyLeft _).continuous + ((amplifyLeft A).continuous.comp (amplifyLeft B).continuous)) (fun a => ?_) + simp [ContinuousLinearMap.mul_apply, algAmplifyLeft_mul_apply] + +@[simp] theorem amplifyRight_one : + amplifyRight (1 : H₂ →L[ℂ] H₂) = (1 : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyRight _).continuous (ContinuousLinearMap.continuous 1)) (fun a => ?_) + simp [algAmplifyRight_one_apply] + +theorem amplifyRight_mul (A B : H₂ →L[ℂ] H₂) : + amplifyRight (H₁ := H₁) (A * B) = amplifyRight A * amplifyRight B := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyRight _).continuous + ((amplifyRight A).continuous.comp (amplifyRight B).continuous)) (fun a => ?_) + simp [ContinuousLinearMap.mul_apply, algAmplifyRight_mul_apply] + +/-! ### Additivity and homogeneity in the amplified operator + +The amplifications `A ↦ A ⊗̂ 1` and `B ↦ 1 ⊗̂ B` are themselves `ℂ`-linear in the operator +being amplified: they preserve `0`, addition, and scalar multiplication. -/ + +lemma algAmplifyLeft_zero_apply (a : H₁ ⊗[ℂ] H₂) : + algAmplifyLeft (H₂ := H₂) (0 : H₁ →L[ℂ] H₁) a = 0 := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp + | add p q hp hq => simp [map_add, hp, hq] + +lemma algAmplifyLeft_add_apply (A B : H₁ →L[ℂ] H₁) (a : H₁ ⊗[ℂ] H₂) : + algAmplifyLeft (A + B) a = algAmplifyLeft A a + algAmplifyLeft B a := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.add_apply, TensorProduct.add_tmul] + | add p q hp hq => simp only [map_add, hp, hq]; abel + +lemma algAmplifyLeft_smul_apply (c : ℂ) (A : H₁ →L[ℂ] H₁) (a : H₁ ⊗[ℂ] H₂) : + algAmplifyLeft (c • A) a = c • algAmplifyLeft A a := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.smul_apply, TensorProduct.smul_tmul'] + | add p q hp hq => simp only [map_add, hp, hq, smul_add] + +lemma algAmplifyRight_zero_apply (a : H₁ ⊗[ℂ] H₂) : + algAmplifyRight (H₁ := H₁) (0 : H₂ →L[ℂ] H₂) a = 0 := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp + | add p q hp hq => simp [map_add, hp, hq] + +lemma algAmplifyRight_add_apply (A B : H₂ →L[ℂ] H₂) (a : H₁ ⊗[ℂ] H₂) : + algAmplifyRight (A + B) a = algAmplifyRight A a + algAmplifyRight B a := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.add_apply, TensorProduct.tmul_add] + | add p q hp hq => simp only [map_add, hp, hq]; abel + +lemma algAmplifyRight_smul_apply (c : ℂ) (B : H₂ →L[ℂ] H₂) (a : H₁ ⊗[ℂ] H₂) : + algAmplifyRight (c • B) a = c • algAmplifyRight B a := by + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => simp [ContinuousLinearMap.smul_apply, TensorProduct.tmul_smul] + | add p q hp hq => simp only [map_add, hp, hq, smul_add] + +@[simp] lemma amplifyLeft_zero : + amplifyLeft (0 : H₁ →L[ℂ] H₁) = (0 : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyLeft _).continuous (ContinuousLinearMap.continuous 0)) (fun a => ?_) + rw [amplifyLeft_coe, algAmplifyLeft_zero_apply, UniformSpace.Completion.coe_zero, + ContinuousLinearMap.zero_apply] + +theorem amplifyLeft_add (A B : H₁ →L[ℂ] H₁) : + amplifyLeft (H₂ := H₂) (A + B) = amplifyLeft A + amplifyLeft B := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyLeft _).continuous + ((amplifyLeft A).continuous.add (amplifyLeft B).continuous)) (fun a => ?_) + rw [amplifyLeft_coe, algAmplifyLeft_add_apply, UniformSpace.Completion.coe_add, + ContinuousLinearMap.add_apply, amplifyLeft_coe, amplifyLeft_coe] + +theorem amplifyLeft_smul (c : ℂ) (A : H₁ →L[ℂ] H₁) : + amplifyLeft (H₂ := H₂) (c • A) = c • amplifyLeft A := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyLeft _).continuous + ((continuous_const_smul c).comp (amplifyLeft A).continuous)) (fun a => ?_) + rw [amplifyLeft_coe, algAmplifyLeft_smul_apply, UniformSpace.Completion.coe_smul, + ContinuousLinearMap.smul_apply, amplifyLeft_coe] + +@[simp] lemma amplifyRight_zero : + amplifyRight (0 : H₂ →L[ℂ] H₂) = (0 : HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyRight _).continuous (ContinuousLinearMap.continuous 0)) (fun a => ?_) + rw [amplifyRight_coe, algAmplifyRight_zero_apply, UniformSpace.Completion.coe_zero, + ContinuousLinearMap.zero_apply] + +theorem amplifyRight_add (A B : H₂ →L[ℂ] H₂) : + amplifyRight (H₁ := H₁) (A + B) = amplifyRight A + amplifyRight B := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyRight _).continuous + ((amplifyRight A).continuous.add (amplifyRight B).continuous)) (fun a => ?_) + rw [amplifyRight_coe, algAmplifyRight_add_apply, UniformSpace.Completion.coe_add, + ContinuousLinearMap.add_apply, amplifyRight_coe, amplifyRight_coe] + +theorem amplifyRight_smul (c : ℂ) (B : H₂ →L[ℂ] H₂) : + amplifyRight (H₁ := H₁) (c • B) = c • amplifyRight B := by + ext z + refine Completion.induction_on z + (isClosed_eq (amplifyRight _).continuous + ((continuous_const_smul c).comp (amplifyRight B).continuous)) (fun a => ?_) + rw [amplifyRight_coe, algAmplifyRight_smul_apply, UniformSpace.Completion.coe_smul, + ContinuousLinearMap.smul_apply, amplifyRight_coe] + +/-! ### The commutation (swap) equivalence + +Swapping the two tensor factors is a linear isometric equivalence +`HilbertTensor H₁ H₂ ≃ₗᵢ HilbertTensor H₂ H₁`, the completion of Mathlib's algebraic +`TensorProduct.commIsometry`. Conjugating by it turns a right amplification `1 ⊗̂ B` into the left +amplification `B ⊗̂ 1` on the swapped space, which is what lets the right-hand slice lemma be +reused verbatim on the left. -/ + +/-- The **commutation (swap) equivalence** `x ⊗̂ y ↦ y ⊗̂ x`, a linear isometric equivalence +`HilbertTensor H₁ H₂ ≃ₗᵢ HilbertTensor H₂ H₁`, obtained by completing `TensorProduct.commIsometry`. -/ +noncomputable def commEquiv : HilbertTensor H₁ H₂ ≃ₗᵢ[ℂ] HilbertTensor H₂ H₁ := + (TensorProduct.commIsometry ℂ H₁ H₂).completion + +@[simp] lemma commEquiv_tmul (x : H₁) (y : H₂) : commEquiv (x ⊗ₕ y) = y ⊗ₕ x := by + rw [commEquiv, tmul, LinearIsometryEquiv.completion_coe, TensorProduct.commIsometry_apply, + TensorProduct.comm_tmul, tmul] + +@[simp] lemma commEquiv_symm_tmul (y : H₂) (x : H₁) : + commEquiv.symm (y ⊗ₕ x) = x ⊗ₕ y := by + rw [← commEquiv_tmul x y, LinearIsometryEquiv.symm_apply_apply] + +/-- Conjugating a right amplification `1 ⊗̂ B` by the swap equivalence yields the left +amplification `B ⊗̂ 1` on the swapped space. -/ +lemma conjStarAlgEquiv_commEquiv_amplifyRight (B : H₂ →L[ℂ] H₂) : + commEquiv.conjStarAlgEquiv (amplifyRight (H₁ := H₁) B) = amplifyLeft (H₂ := H₁) B := by + refine ContinuousLinearMap.ext fun w => ?_ + rw [LinearIsometryEquiv.conjStarAlgEquiv_apply_apply] + refine UniformSpace.Completion.induction_on w + (isClosed_eq (by fun_prop) (by fun_prop)) (fun a => ?_) + induction a using TensorProduct.induction_on with + | zero => simp only [UniformSpace.Completion.coe_zero, map_zero] + | tmul y x => + change commEquiv (amplifyRight B (commEquiv.symm (y ⊗ₕ x))) = amplifyLeft B (y ⊗ₕ x) + rw [commEquiv_symm_tmul, amplifyRight_tmul, commEquiv_tmul, amplifyLeft_tmul] + | add p q hp hq => + simp only [UniformSpace.Completion.coe_add, map_add, hp, hq] + +/-- Conjugating a right amplification `1 ⊗̂ S` by the *inverse* swap equivalence yields the left +amplification `S ⊗̂ 1`. This is the back-transport companion of +`conjStarAlgEquiv_commEquiv_amplifyRight`, used to carry the right-hand slice lemma back to the +original space. -/ +lemma conjStarAlgEquiv_symm_commEquiv_amplifyRight (S : H₁ →L[ℂ] H₁) : + commEquiv.conjStarAlgEquiv.symm (amplifyRight (H₁ := H₂) S) = amplifyLeft (H₂ := H₂) S := by + refine ContinuousLinearMap.ext fun w => ?_ + rw [LinearIsometryEquiv.symm_conjStarAlgEquiv_apply_apply] + refine UniformSpace.Completion.induction_on w + (isClosed_eq (by fun_prop) (by fun_prop)) (fun a => ?_) + induction a using TensorProduct.induction_on with + | zero => simp only [UniformSpace.Completion.coe_zero, map_zero] + | tmul x y => + change commEquiv.symm (amplifyRight S (commEquiv (x ⊗ₕ y))) = amplifyLeft S (x ⊗ₕ y) + rw [commEquiv_tmul, amplifyRight_tmul, commEquiv_symm_tmul, amplifyLeft_tmul] + | add p q hp hq => + simp only [UniformSpace.Completion.coe_add, map_add, hp, hq] + +/-! ### Adjoints + +On genuine Hilbert spaces (`CompleteSpace H₁`, `CompleteSpace H₂`) the amplifications are +`*`-homomorphisms: they intertwine the adjoint on `B(H₁)` (resp. `B(H₂)`) with the adjoint on +`B(HilbertTensor H₁ H₂)`, hence preserve the `star` operation. Together with +`amplifyLeft_one`/`amplifyLeft_mul`/`amplifyLeft_add`/`amplifyLeft_smul` this exhibits +`amplifyLeft` and `amplifyRight` as unital `*`-algebra homomorphisms, bundled as +`amplifyLeftₐ` and `amplifyRightₐ`. -/ + +section Adjoint + +variable [CompleteSpace H₁] [CompleteSpace H₂] + +omit [CompleteSpace H₂] in +lemma algAmplifyLeft_inner_adjoint (A : H₁ →L[ℂ] H₁) (a b : H₁ ⊗[ℂ] H₂) : + inner ℂ (algAmplifyLeft (ContinuousLinearMap.adjoint A) a) b + = inner ℂ a (algAmplifyLeft A b) := by + simp only [algAmplifyLeft, LinearMap.mkContinuous_apply] + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => + induction b using TensorProduct.induction_on with + | zero => simp + | tmul x' y' => + simp only [TensorProduct.map_tmul, LinearMap.id_coe, id_eq, ContinuousLinearMap.coe_coe, + TensorProduct.inner_tmul] + rw [ContinuousLinearMap.adjoint_inner_left] + | add p q hp hq => simp only [inner_add_right, map_add, hp, hq] + | add p q hp hq => simp only [inner_add_left, map_add, hp, hq] + +omit [CompleteSpace H₁] in +lemma algAmplifyRight_inner_adjoint (B : H₂ →L[ℂ] H₂) (a b : H₁ ⊗[ℂ] H₂) : + inner ℂ (algAmplifyRight (ContinuousLinearMap.adjoint B) a) b + = inner ℂ a (algAmplifyRight B b) := by + simp only [algAmplifyRight, LinearMap.mkContinuous_apply] + induction a using TensorProduct.induction_on with + | zero => simp + | tmul x y => + induction b using TensorProduct.induction_on with + | zero => simp + | tmul x' y' => + simp only [TensorProduct.map_tmul, LinearMap.id_coe, id_eq, ContinuousLinearMap.coe_coe, + TensorProduct.inner_tmul] + rw [ContinuousLinearMap.adjoint_inner_left] + | add p q hp hq => simp only [inner_add_right, map_add, hp, hq] + | add p q hp hq => simp only [inner_add_left, map_add, hp, hq] + +omit [CompleteSpace H₂] in +/-- The adjoint of the left amplification of `A` is the left amplification of the adjoint of `A`. -/ +lemma amplifyLeft_adjoint (A : H₁ →L[ℂ] H₁) : + ContinuousLinearMap.adjoint (amplifyLeft (H₂ := H₂) A) + = amplifyLeft (ContinuousLinearMap.adjoint A) := by + symm + rw [ContinuousLinearMap.eq_adjoint_iff] + intro u v + refine Completion.induction_on₂ u v (isClosed_eq (by fun_prop) (by fun_prop)) (fun a b => ?_) + rw [amplifyLeft_coe, amplifyLeft_coe, UniformSpace.Completion.inner_coe, + UniformSpace.Completion.inner_coe, algAmplifyLeft_inner_adjoint] + +omit [CompleteSpace H₁] in +/-- The adjoint of the right amplification of `B` is the right amplification of the adjoint of +`B`. -/ +lemma amplifyRight_adjoint (B : H₂ →L[ℂ] H₂) : + ContinuousLinearMap.adjoint (amplifyRight (H₁ := H₁) B) + = amplifyRight (ContinuousLinearMap.adjoint B) := by + symm + rw [ContinuousLinearMap.eq_adjoint_iff] + intro u v + refine Completion.induction_on₂ u v (isClosed_eq (by fun_prop) (by fun_prop)) (fun a b => ?_) + rw [amplifyRight_coe, amplifyRight_coe, UniformSpace.Completion.inner_coe, + UniformSpace.Completion.inner_coe, algAmplifyRight_inner_adjoint] + +omit [CompleteSpace H₂] in +/-- The left amplification preserves the `star` (adjoint) operation. -/ +@[simp] lemma amplifyLeft_star (A : H₁ →L[ℂ] H₁) : + star (amplifyLeft (H₂ := H₂) A) = amplifyLeft (star A) := + amplifyLeft_adjoint A + +omit [CompleteSpace H₁] in +/-- The right amplification preserves the `star` (adjoint) operation. -/ +@[simp] lemma amplifyRight_star (B : H₂ →L[ℂ] H₂) : + star (amplifyRight (H₁ := H₁) B) = amplifyRight (star B) := + amplifyRight_adjoint B + +omit [CompleteSpace H₂] in +/-- The **left amplification** `A ↦ A ⊗̂ 1`, bundled as a unital `⋆`-algebra homomorphism +`B(H₁) →⋆ₐ[ℂ] B(HilbertTensor H₁ H₂)`. This packages `amplifyLeft_one`, `amplifyLeft_mul`, +`amplifyLeft_add`, `amplifyLeft_smul`, and `amplifyLeft_star` into a single morphism. -/ +noncomputable def amplifyLeftₐ : + (H₁ →L[ℂ] H₁) →⋆ₐ[ℂ] (HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) where + toFun := amplifyLeft + map_one' := amplifyLeft_one + map_mul' := amplifyLeft_mul + map_zero' := amplifyLeft_zero + map_add' := amplifyLeft_add + commutes' r := by + rw [Algebra.algebraMap_eq_smul_one, Algebra.algebraMap_eq_smul_one, amplifyLeft_smul, + amplifyLeft_one] + map_star' A := (amplifyLeft_star A).symm + +omit [CompleteSpace H₂] in +@[simp] lemma amplifyLeftₐ_apply (A : H₁ →L[ℂ] H₁) : + amplifyLeftₐ (H₂ := H₂) A = amplifyLeft A := rfl + +omit [CompleteSpace H₁] in +/-- The **right amplification** `B ↦ 1 ⊗̂ B`, bundled as a unital `⋆`-algebra homomorphism +`B(H₂) →⋆ₐ[ℂ] B(HilbertTensor H₁ H₂)`. -/ +noncomputable def amplifyRightₐ : + (H₂ →L[ℂ] H₂) →⋆ₐ[ℂ] (HilbertTensor H₁ H₂ →L[ℂ] HilbertTensor H₁ H₂) where + toFun := amplifyRight + map_one' := amplifyRight_one + map_mul' := amplifyRight_mul + map_zero' := amplifyRight_zero + map_add' := amplifyRight_add + commutes' r := by + rw [Algebra.algebraMap_eq_smul_one, Algebra.algebraMap_eq_smul_one, amplifyRight_smul, + amplifyRight_one] + map_star' B := (amplifyRight_star B).symm + +omit [CompleteSpace H₁] in +@[simp] lemma amplifyRightₐ_apply (B : H₂ →L[ℂ] H₂) : + amplifyRightₐ (H₁ := H₁) B = amplifyRight B := rfl + +end Adjoint + +/-! ### The tensor bridge `ℓ²(ι) ⊗̂ K ≃ lp (fun _ : ι => K) 2` + +The completed Hilbert tensor product of `ℓ²(ι) = lp (fun _ : ι => ℂ) 2` with a Hilbert space `K` +is, canonically, the `ℓ²` sum of `ι`-many copies of `K`. The isomorphism sends the pure tensor +`δᵢ ⊗ k` (with `δᵢ = lp.single 2 i 1` the `i`-th standard basis vector of `ℓ²(ι)`) to the single +`lp.single 2 i k`. It is built by exhibiting the inclusions `k ↦ δᵢ ⊗ k` as an orthogonal family +whose total span is dense, i.e. as a Hilbert sum (`IsHilbertSum`). -/ + +section LpTensorBridge + +open scoped ENNReal + +variable {ι : Type*} [DecidableEq ι] + {K : Type*} [NormedAddCommGroup K] [InnerProductSpace ℂ K] + +/-- The isometric inclusion `K → ℓ²(ι) ⊗̂ K`, `k ↦ δᵢ ⊗ k`, where `δᵢ = lp.single 2 i 1` is the +`i`-th standard basis vector of `ℓ²(ι) = lp (fun _ : ι => ℂ) 2`. As `δᵢ` is a unit vector this is a +genuine linear isometry; the family `i ↦ tmulSingleₗᵢ i` exhibits `ℓ²(ι) ⊗̂ K` as the Hilbert sum +of copies of `K`. -/ +noncomputable def tmulSingleₗᵢ (i : ι) : + K →ₗᵢ[ℂ] (lp (fun _ : ι => ℂ) 2) ⊗̂ K where + toFun k := tmul (lp.single 2 i (1 : ℂ)) k + map_add' k k' := tmul_add _ k k' + map_smul' c k := by + rw [RingHom.id_apply, tmul, tmul, TensorProduct.tmul_smul, UniformSpace.Completion.coe_smul] + norm_map' k := by + simp only [LinearMap.coe_mk, AddHom.coe_mk, norm_tmul] + rw [lp.norm_single (p := 2) (by norm_num), norm_one, one_mul] + +@[simp] lemma tmulSingleₗᵢ_apply (i : ι) (k : K) : + tmulSingleₗᵢ i k = tmul (lp.single 2 i (1 : ℂ)) k := by rfl + +/-- Distinct standard basis vectors of `ℓ²(ι)` are orthogonal, so the inclusions `tmulSingleₗᵢ` +form an orthogonal family. -/ +lemma orthogonalFamily_tmulSingleₗᵢ : + OrthogonalFamily ℂ (fun _ : ι => K) (fun i => tmulSingleₗᵢ (K := K) i) := by + intro i j hij k k' + have hδ : inner ℂ (lp.single 2 i (1 : ℂ) : lp (fun _ : ι => ℂ) 2) (lp.single 2 j (1 : ℂ)) = 0 := by + rw [lp.inner_single_left, lp.coeFn_single, Pi.single_eq_of_ne hij, inner_zero_right] + rw [tmulSingleₗᵢ_apply, tmulSingleₗᵢ_apply, inner_tmul, hδ, zero_mul] + +/-- The orthogonal family `tmulSingleₗᵢ` has dense total span, exhibiting `ℓ²(ι) ⊗̂ K` as the +Hilbert sum of `ι`-many copies of `K`. Density is proved by approximating a pure tensor +`a ⊗ k` (`a ∈ ℓ²(ι)`) by the convergent series `∑ᵢ (a i) • (δᵢ ⊗ k)`, each term of which lies in +the range of `tmulSingleₗᵢ i`. -/ +lemma isHilbertSum_tmulSingleₗᵢ [CompleteSpace K] : + IsHilbertSum ℂ (fun _ : ι => K) (fun i => tmulSingleₗᵢ (K := K) i) := by + haveI : ∀ _ : ι, CompleteSpace K := fun _ => inferInstance + refine IsHilbertSum.mk orthogonalFamily_tmulSingleₗᵢ ?_ + set M : Submodule ℂ (HilbertTensor (lp (fun _ : ι => ℂ) 2) K) := + ⨆ i, LinearMap.range (tmulSingleₗᵢ (K := K) i).toLinearMap with hM + have key : ∀ z : (lp (fun _ : ι => ℂ) 2) ⊗[ℂ] K, + ((z : HilbertTensor (lp (fun _ : ι => ℂ) 2) K)) ∈ M.topologicalClosure := by + intro z + induction z using TensorProduct.induction_on with + | zero => + rw [UniformSpace.Completion.coe_zero] + exact (M.topologicalClosure).zero_mem + | add p q hp hq => + rw [UniformSpace.Completion.coe_add] + exact (M.topologicalClosure).add_mem hp hq + | tmul a k => + change tmul a k ∈ M.topologicalClosure + have hsum : HasSum (fun i => tmul (lp.single 2 i (a i)) k) (tmul a k) := by + have h := (lp.hasSum_single (E := fun _ : ι => ℂ) (p := 2) (by norm_num) a).mapL + (tmulLeftL (H₁ := lp (fun _ : ι => ℂ) 2) (H₂ := K) k) + simpa only [tmulLeftL_apply] using h + have hmem : ∀ i, tmul (lp.single 2 i (a i)) k ∈ M := by + intro i + have heq : tmul (lp.single 2 i (a i)) k = (a i) • tmulSingleₗᵢ (K := K) i k := by + rw [tmulSingleₗᵢ_apply, + show (lp.single 2 i (a i) : lp (fun _ : ι => ℂ) 2) + = (a i) • lp.single 2 i (1 : ℂ) by + rw [← lp.single_smul, smul_eq_mul, mul_one], + tmul, tmul, ← TensorProduct.smul_tmul', UniformSpace.Completion.coe_smul] + rw [heq] + exact Submodule.smul_mem _ _ + (Submodule.mem_iSup_of_mem i (LinearMap.mem_range_self _ k)) + have hcl : tmul a k ∈ closure (M : Set (HilbertTensor (lp (fun _ : ι => ℂ) 2) K)) := + mem_closure_of_tendsto hsum + (Filter.Eventually.of_forall fun s => + SetLike.mem_coe.mpr (Submodule.sum_mem _ fun i _ => hmem i)) + rwa [← Submodule.topologicalClosure_coe, SetLike.mem_coe] at hcl + have hsub : Set.range ((↑) : (lp (fun _ : ι => ℂ) 2 ⊗[ℂ] K) → + HilbertTensor (lp (fun _ : ι => ℂ) 2) K) ⊆ (M.topologicalClosure : Set _) := by + rintro _ ⟨z, rfl⟩ + exact key z + rw [top_le_iff] + apply SetLike.coe_injective + rw [Submodule.top_coe] + apply Set.eq_univ_of_univ_subset + rw [← UniformSpace.Completion.denseRange_coe.closure_range] + exact closure_minimal hsub M.isClosed_topologicalClosure + +/-- **Tensor bridge.** The `ℓ²` sum of `ι`-many copies of a Hilbert space `K` is isometrically the +completed Hilbert tensor product `ℓ²(ι) ⊗̂ K`, where `ℓ²(ι) = lp (fun _ : ι => ℂ) 2`. Under the +bijection the single `lp.single 2 i k` corresponds to the pure tensor `δᵢ ⊗ k` +(`lpTensorEquiv_single`). -/ +noncomputable def lpTensorEquiv [CompleteSpace K] : + lp (fun _ : ι => K) 2 ≃ₗᵢ[ℂ] (lp (fun _ : ι => ℂ) 2) ⊗̂ K := + isHilbertSum_tmulSingleₗᵢ.linearIsometryEquiv.symm + +@[simp] lemma lpTensorEquiv_single [CompleteSpace K] (i : ι) (k : K) : + lpTensorEquiv (lp.single 2 i k) = (lp.single 2 i (1 : ℂ)) ⊗ₕ k := by + rw [lpTensorEquiv, + IsHilbertSum.linearIsometryEquiv_symm_apply_single isHilbertSum_tmulSingleₗᵢ, + tmulSingleₗᵢ_apply] + +end LpTensorBridge + +end HilbertTensor diff --git a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/l2Space.lean b/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/l2Space.lean deleted file mode 100644 index ec4feaa..0000000 --- a/QuantumSystem/ForMathlib/Analysis/InnerProductSpace/l2Space.lean +++ /dev/null @@ -1,67 +0,0 @@ -module - -public import Mathlib.Analysis.InnerProductSpace.l2Space - -/-! -# Helper lemmas for Hilbert bases and Parseval identity - -This file provides additional lemmas for Hilbert bases that extend Mathlib's `l2Space`. - -## Main results - -* `inner_mul_inner_eq_norm_sq`: For a Hilbert basis, `⟪x, bᵢ⟫ * ⟪bᵢ, x⟫ = ‖⟪bᵢ, x⟫‖²` -* `HilbertBasis.norm_sq_eq_tsum_norm_sq_inner'`: Parseval identity `‖x‖² = ∑ᵢ ‖⟪bᵢ, x⟫‖²` -* `HilbertBasis.summable_norm_sq_inner'`: The sequence `‖⟪bᵢ, x⟫‖²` is summable --/ - -@[expose] public section - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] -variable {ι : Type*} - -/-- For a Hilbert basis, `⟪x, bᵢ⟫ * ⟪bᵢ, x⟫ = ‖⟪bᵢ, x⟫‖²` as complex numbers. -/ -theorem inner_mul_inner_eq_norm_sq (b : HilbertBasis ι ℂ H) (x : H) (i : ι) : - inner (𝕜 := ℂ) x (b i) * inner (𝕜 := ℂ) (b i) x = (‖inner (𝕜 := ℂ) (b i) x‖^2 : ℂ) := by - rw [← inner_conj_symm (𝕜 := ℂ)] - have h : ↑(Complex.normSq (inner ℂ (b i) x)) = (starRingEnd ℂ) (inner ℂ (b i) x) * inner ℂ (b i) x := - Complex.normSq_eq_conj_mul_self - rw [← h] - norm_cast - exact Complex.normSq_eq_norm_sq _ - -/-- Helper: real part of a real power. -/ -private lemma ofReal_pow_re (r : ℝ) (n : ℕ) : ((r : ℂ)^n).re = r^n := by - induction n with - | zero => simp - | succ n ih => - rw [pow_succ, pow_succ] - rw [Complex.mul_re, Complex.ofReal_re, Complex.ofReal_im, ih] - ring - -/-- Parseval identity for Hilbert bases: `‖x‖² = ∑ᵢ ‖⟪bᵢ, x⟫‖²`. -/ -theorem HilbertBasis.norm_sq_eq_tsum_norm_sq_inner' (b : HilbertBasis ι ℂ H) (x : H) : - ‖x‖^2 = ∑' i, ‖inner (𝕜 := ℂ) (b i) x‖^2 := by - have h := b.tsum_inner_mul_inner x x - simp_rw [inner_mul_inner_eq_norm_sq b x] at h - have h2 : (inner (𝕜 := ℂ) x x).re = ‖x‖^2 := by - rw [inner_self_eq_norm_sq_to_K] - exact ofReal_pow_re _ 2 - have h3 : (∑' i, (‖inner (𝕜 := ℂ) (b i) x‖^2 : ℂ)).re = ∑' i, ‖inner (𝕜 := ℂ) (b i) x‖^2 := by - rw [Complex.re_tsum] - · congr 1 - ext i - exact ofReal_pow_re _ 2 - · have := b.summable_inner_mul_inner x x - simp_rw [inner_mul_inner_eq_norm_sq b x] at this - exact this - rw [← h2, ← h, h3] - -/-- The sequence `‖⟪bᵢ, x⟫‖²` is summable for any Hilbert basis. -/ -theorem HilbertBasis.summable_norm_sq_inner' (b : HilbertBasis ι ℂ H) (x : H) : - Summable (fun i => ‖inner (𝕜 := ℂ) (b i) x‖^2) := by - have hsummable := b.summable_inner_mul_inner x x - simp_rw [inner_mul_inner_eq_norm_sq b x] at hsummable - obtain ⟨s, hs⟩ := hsummable - have hs_re := Complex.hasSum_re hs - simp_rw [ofReal_pow_re] at hs_re - exact hs_re.summable diff --git a/QuantumSystem/ForMathlib/Analysis/LocallyConvex/StrongOperatorTopology.lean b/QuantumSystem/ForMathlib/Analysis/LocallyConvex/StrongOperatorTopology.lean index 76a94da..9e2e4ee 100644 --- a/QuantumSystem/ForMathlib/Analysis/LocallyConvex/StrongOperatorTopology.lean +++ b/QuantumSystem/ForMathlib/Analysis/LocallyConvex/StrongOperatorTopology.lean @@ -2,39 +2,42 @@ module public import Mathlib.Analysis.Complex.Basic public import Mathlib.Analysis.InnerProductSpace.Defs -public import Mathlib.Analysis.LocallyConvex.WeakOperatorTopology +public import Mathlib.Topology.Algebra.Module.Spaces.PointwiseConvergenceCLM @[expose] public section /-! # Strong operator topology closedness of commutants -This file provides SOT-continuity of left/right multiplication by a fixed bounded operator, -consequently showing that commutants (`Set.centralizer`) and double commutants are SOT-closed. +This file shows that commutants (`Set.centralizer`) and double commutants are closed in the +strong operator topology (SOT). -The strong operator topology (SOT) on `B(H) = H →L[ℂ] H` is the topology of pointwise convergence -in the norm topology: a net `T_α → T` in SOT iff `∀ x, T_α x → T x` in norm. +The strong operator topology on `B(H) = H →L[ℂ] H` is the topology of pointwise convergence +in the norm topology: a net `T_α → T` in SOT iff `∀ x, T_α x → T x` in norm. Mathlib already +provides this topology as `PointwiseConvergenceCLM` (notation `H →SLₚₜ[RingHom.id ℂ] H`), a type +copy of `H →L[ℂ] H` carrying the topology of uniform convergence on finite sets; this file uses +that type copy rather than introducing another one. ## Main definitions -* `ContinuousLinearMapSOT`: type copy of `H →L[ℂ] H` equipped with the SOT. -* `ContinuousLinearMapSOT.inducingFn`: the inducing function for SOT (evaluation at all points). -* `leftMulSOT`, `rightMulSOT`: left/right multiplication on the SOT type-copy. -* `IsSOTClosed`: a predicate for subsets closed in the SOT. +* `toSOTEquiv`: the linear equivalence from `B(H)` to the SOT type-copy. * `Set.toSOT`: view a subset of operators inside the SOT type-copy. +* `IsSOTClosed`: a predicate for subsets closed in the SOT. ## Main results -* `continuous_leftMulSOT`, `continuous_rightMulSOT`: multiplication by a fixed operator is - SOT-continuous. +* `Set.toSOT_eq_image`: `Set.toSOT` is the image of the subset under `toSOTEquiv`. * `isSOTClosed_centralizer`: the commutant of any set is SOT-closed. * `isSOTClosed_centralizer_centralizer`: double commutants are SOT-closed. -## Comparison with WOT +Left and right multiplication by a fixed operator are SOT-continuous; they are Mathlib's +`PointwiseConvergenceCLM.postcomp` and `PointwiseConvergenceCLM.precomp`, which are already +bundled as continuous linear maps, so this file uses them directly. -SOT is finer than WOT: SOT convergence implies WOT convergence. This file does not develop the -SOT→WOT comparison map; it is imported/used elsewhere when relating SOT-closedness to -WOT-closedness for *-subalgebras. +The comparison with the weak operator topology — SOT is finer than WOT, so every WOT-closed set +is SOT-closed (`continuous_sotToWOT`, `isSOTClosed_of_isWOTClosed`) — lives in +`QuantumSystem.Algebra.Star.DoubleCommutant.SOTClosedSubAlgebra`, the first file that may import +both type copies: this file, like every `ForMathlib` file, imports Mathlib only. -/ namespace StrongOperatorTopology @@ -44,161 +47,67 @@ open scoped Topology variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] local notation "B" => (H →L[ℂ] H) +local notation "BSOT" => (H →SLₚₜ[RingHom.id ℂ] H) -/-- Type copy of `H →L[ℂ] H` equipped with the strong operator topology (pointwise norm convergence). -/ -@[ext] -structure ContinuousLinearMapSOT (H : Type*) [NormedAddCommGroup H] [InnerProductSpace ℂ H] where - /-- The underlying continuous linear map. -/ - toCLM : H →L[ℂ] H - -local notation "BSOT" => ContinuousLinearMapSOT H - -namespace ContinuousLinearMapSOT - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] - -/-- Coercion from `BSOT` back to `B`. -/ -instance : Coe (ContinuousLinearMapSOT H) (H →L[ℂ] H) := ⟨toCLM⟩ - -/-- Coercion from `BSOT` to a function `H → H`. -/ -instance : CoeFun (ContinuousLinearMapSOT H) (fun _ => H → H) := ⟨fun T x => T.toCLM x⟩ - -/-- Addition on `BSOT`. -/ -instance : Add (ContinuousLinearMapSOT H) := ⟨fun T S => ⟨T.toCLM + S.toCLM⟩⟩ - -/-- Scalar multiplication on `BSOT`. -/ -instance : SMul ℂ (ContinuousLinearMapSOT H) := ⟨fun c T => ⟨c • T.toCLM⟩⟩ - -/-- Zero element on `BSOT`. -/ -instance : Zero (ContinuousLinearMapSOT H) := ⟨⟨0⟩⟩ - -@[simp] lemma add_toCLM (T S : ContinuousLinearMapSOT H) : (T + S).toCLM = T.toCLM + S.toCLM := rfl -@[simp] lemma smul_toCLM (c : ℂ) (T : ContinuousLinearMapSOT H) : (c • T).toCLM = c • T.toCLM := rfl -@[simp] lemma zero_toCLM : (0 : ContinuousLinearMapSOT H).toCLM = 0 := rfl -@[simp] lemma mk_toCLM (T : H →L[ℂ] H) : (ContinuousLinearMapSOT.mk T).toCLM = T := rfl - -/-- The inducing function for SOT: evaluation at all points. -/ -def inducingFn : ContinuousLinearMapSOT H → (H → H) := fun T x => T x - -/-- The SOT on `BSOT` is the initial topology induced by pointwise evaluation. -/ -instance : TopologicalSpace (ContinuousLinearMapSOT H) := - TopologicalSpace.induced inducingFn Pi.topologicalSpace - -/-- The SOT is induced by `inducingFn`. -/ -lemma isInducing_inducingFn : - Topology.IsInducing (inducingFn (H := H)) := - ⟨rfl⟩ +/-- The linear equivalence from `B(H)` to the SOT type-copy. It is the identity on the underlying +operators; only the topology differs. -/ +noncomputable def toSOTEquiv : B ≃ₗ[ℂ] BSOT := LinearEquiv.refl ℂ (H →L[ℂ] H) -/-- `inducingFn` is injective. -/ -lemma inducingFn_injective : Function.Injective (inducingFn (H := H)) := by - intro T S h - ext x - exact congrFun h x +@[simp] lemma toSOTEquiv_apply (T : B) (x : H) : (toSOTEquiv (H := H) T) x = T x := rfl -/-- The SOT on `B(H)` is T2 (Hausdorff) since it embeds into a product of T2 spaces. -/ -instance : T2Space (ContinuousLinearMapSOT H) := by - have hemb : Topology.IsEmbedding (inducingFn (H := H)) := - ⟨isInducing_inducingFn (H := H), inducingFn_injective (H := H)⟩ - exact hemb.t2Space - -/-- Evaluation at a point `x` is SOT-continuous. -/ -lemma continuous_eval (x : H) : Continuous (fun T : ContinuousLinearMapSOT H => T x) := by - have : (fun T : ContinuousLinearMapSOT H => T x) = (fun f : H → H => f x) ∘ inducingFn := rfl - rw [this] - exact (continuous_apply x).comp (isInducing_inducingFn (H := H)).continuous - -/-- Characterization of SOT-continuity: a map is SOT-continuous iff all pointwise evaluations are. -/ -lemma continuous_of_forall_eval_continuous {g : ContinuousLinearMapSOT H → ContinuousLinearMapSOT H} - (h : ∀ x : H, Continuous (fun T => g T x)) : Continuous g := by - rw [continuous_induced_rng] - exact continuous_pi h - -end ContinuousLinearMapSOT - -open ContinuousLinearMapSOT in -/-- A subset of `B(H)` is SOT-closed if its image in `BSOT` is closed. -/ -def IsSOTClosed (S : Set B) : Prop := - IsClosed {T : BSOT | T.toCLM ∈ S} +@[simp] lemma toSOTEquiv_symm_apply (T : BSOT) (x : H) : + ((toSOTEquiv (H := H)).symm T) x = T x := rfl /-- View a subset of operators inside the SOT type-copy. -/ def Set.toSOT (S : Set B) : Set BSOT := - {T | T.toCLM ∈ S} + {T | (toSOTEquiv (H := H)).symm T ∈ S} lemma Set.mem_toSOT_iff {S : Set B} {T : BSOT} : - T ∈ Set.toSOT (H := H) S ↔ T.toCLM ∈ S := Iff.rfl - -/-- Left multiplication on `BSOT`. -/ -def leftMulSOT (a : B) : BSOT → BSOT := - fun T => ⟨a * T.toCLM⟩ - -/-- Right multiplication on `BSOT`. -/ -def rightMulSOT (a : B) : BSOT → BSOT := - fun T => ⟨T.toCLM * a⟩ - -@[simp] lemma leftMulSOT_apply (a : B) (T : BSOT) (x : H) : - (leftMulSOT (H := H) a T) x = a (T x) := by - simp [leftMulSOT, ContinuousLinearMap.mul_apply] - -@[simp] lemma rightMulSOT_apply (a : B) (T : BSOT) (x : H) : - (rightMulSOT (H := H) a T) x = T (a x) := by - simp [rightMulSOT, ContinuousLinearMap.mul_apply] - -/-- Left multiplication by a fixed operator is SOT-continuous. -/ -lemma continuous_leftMulSOT (a : B) : Continuous (leftMulSOT (H := H) a) := by - refine ContinuousLinearMapSOT.continuous_of_forall_eval_continuous (H := H) ?_ - intro x - have : (fun T : BSOT => (leftMulSOT (H := H) a T) x) = (fun y => a y) ∘ (fun T => T x) := by - ext T - simp - rw [this] - exact a.continuous.comp (ContinuousLinearMapSOT.continuous_eval (H := H) x) - -/-- Right multiplication by a fixed operator is SOT-continuous. -/ -lemma continuous_rightMulSOT (a : B) : Continuous (rightMulSOT (H := H) a) := by - refine ContinuousLinearMapSOT.continuous_of_forall_eval_continuous (H := H) ?_ - intro x - have : (fun T : BSOT => (rightMulSOT (H := H) a T) x) = (fun T => T (a x)) := by - ext T - simp - rw [this] - exact ContinuousLinearMapSOT.continuous_eval (H := H) (a x) - -/-- The set of operators (in BSOT) commuting with a fixed operator `a` is closed. -/ -lemma isClosed_commutesWithSOT (a : B) : - IsClosed {T : BSOT | leftMulSOT (H := H) a T = rightMulSOT (H := H) a T} := - isClosed_eq (continuous_leftMulSOT (H := H) a) (continuous_rightMulSOT (H := H) a) + T ∈ Set.toSOT (H := H) S ↔ (toSOTEquiv (H := H)).symm T ∈ S := + Iff.rfl + +/-- `Set.toSOT` is the image of the subset under `toSOTEquiv`. -/ +lemma Set.toSOT_eq_image (S : Set B) : + Set.toSOT (H := H) S = (toSOTEquiv (H := H)) '' S := by + ext T + constructor + · intro h + exact ⟨_, h, (toSOTEquiv (H := H)).apply_symm_apply T⟩ + · rintro ⟨x, hx, rfl⟩ + simpa [Set.mem_toSOT_iff] using hx + +/-- A subset of `B(H)` is SOT-closed if its image in the SOT type-copy is closed. -/ +def IsSOTClosed (S : Set B) : Prop := + IsClosed (Set.toSOT (H := H) S) +/-- SOT-closedness stated as closedness of the image, the form in which the strong operator +topology is usually phrased. -/ +lemma isSOTClosed_iff_isClosed_image (S : Set B) : + IsSOTClosed (H := H) S ↔ + IsClosed (ContinuousLinearMap.toPointwiseConvergenceCLM ℂ (RingHom.id ℂ) H H '' S) := by + rw [IsSOTClosed, Set.toSOT_eq_image] + rfl /-- The commutant `Set.centralizer S` is SOT-closed. -/ lemma isSOTClosed_centralizer (S : Set B) : IsSOTClosed (H := H) (Set.centralizer S) := by - unfold IsSOTClosed - -- Rewrite the SOT-image of the centralizer as an intersection. - have : {T : BSOT | T.toCLM ∈ Set.centralizer S} = - ⋂ a : B, ⋂ _ : a ∈ S, {T : BSOT | leftMulSOT (H := H) a T = rightMulSOT (H := H) a T} := by + -- Express the commutant as an intersection of commuting constraints, each closed because + -- left and right multiplication are SOT-continuous. + have key : Set.toSOT (H := H) (Set.centralizer S) = + ⋂ a ∈ S, {T : BSOT | PointwiseConvergenceCLM.postcomp H a T + = PointwiseConvergenceCLM.precomp H a T} := by ext T + simp only [Set.mem_toSOT_iff, Set.mem_centralizer_iff, Set.mem_iInter, Set.mem_setOf_eq] constructor - · intro hT - refine Set.mem_iInter.2 ?_ - intro a - refine Set.mem_iInter.2 ?_ - intro ha - have hcomm : a * T.toCLM = T.toCLM * a := Set.mem_centralizer_iff.mp hT a ha - simp only [Set.mem_setOf_eq, leftMulSOT, rightMulSOT] + · intro hT a ha + ext x + simpa [ContinuousLinearMap.mul_apply] using congrArg (fun R => R x) (hT a ha) + · intro hT a ha ext x - simp [ContinuousLinearMap.mul_apply, hcomm] - · intro hT - refine Set.mem_centralizer_iff.mpr ?_ - intro a ha - have h := Set.mem_iInter.1 (Set.mem_iInter.1 hT a) ha - simp only [Set.mem_setOf_eq, leftMulSOT, rightMulSOT] at h - have := congrArg ContinuousLinearMapSOT.toCLM h - simpa using this - rw [this] - refine isClosed_iInter ?_ - intro a - refine isClosed_iInter ?_ - intro _ - exact isClosed_commutesWithSOT (H := H) a + simpa [ContinuousLinearMap.mul_apply] using congrArg (fun R => R x) (hT a ha) + rw [IsSOTClosed, key] + exact isClosed_biInter fun a _ => + isClosed_eq (PointwiseConvergenceCLM.postcomp H a).continuous + (PointwiseConvergenceCLM.precomp H a).continuous /-- Any double commutant is SOT-closed. -/ theorem isSOTClosed_centralizer_centralizer (S : Set B) : diff --git a/QuantumSystem/ForMathlib/Analysis/LocallyConvex/WeakOperatorTopology.lean b/QuantumSystem/ForMathlib/Analysis/LocallyConvex/WeakOperatorTopology.lean index d192bf8..86e8b69 100644 --- a/QuantumSystem/ForMathlib/Analysis/LocallyConvex/WeakOperatorTopology.lean +++ b/QuantumSystem/ForMathlib/Analysis/LocallyConvex/WeakOperatorTopology.lean @@ -28,6 +28,9 @@ consequently showing that commutants (`Set.centralizer`) and double commutants a * `continuous_leftMulWOT`, `continuous_rightMulWOT`: multiplication by a fixed operator is WOT-continuous. * `isClosed_commutesWithWOT`: the set of operators commuting with a fixed operator is WOT-closed. +* `Set.toWOT_eq_image`: `Set.toWOT` is the image of the subset under `toWOTEquiv`. +* `isWOTClosed_iff_isClosed_image`: WOT-closedness as closedness of the image under the canonical + inclusion `ContinuousLinearMap.toWOTCLM`. * `isWOTClosed_centralizer`: the commutant of any set is WOT-closed. * `isWOTClosed_centralizer_centralizer`: double commutants are WOT-closed. * `exists_wotCLM_sep_of_isClosed_submodule`: a finite-coordinate separation lemma for WOT-closed @@ -104,10 +107,29 @@ lemma Set.mem_toWOT_iff {S : Set B} {T : BWOT} : T ∈ Set.toWOT (H := H) S ↔ (toWOTEquiv (H := H)).symm T ∈ S := by rfl +/-- `Set.toWOT` is the image of the subset under `toWOTEquiv`. -/ +lemma Set.toWOT_eq_image (S : Set B) : + Set.toWOT (H := H) S = (toWOTEquiv (H := H)) '' S := by + ext T + constructor + · intro h + exact ⟨_, h, (toWOTEquiv (H := H)).apply_symm_apply T⟩ + · rintro ⟨x, hx, rfl⟩ + simpa [Set.mem_toWOT_iff] using hx + /-- A subset of operators is WOT-closed if its image in the WOT type-copy is closed. -/ def IsWOTClosed (S : Set B) : Prop := IsClosed (Set.toWOT (H := H) S) +/-- WOT-closedness stated as closedness of the image under the canonical inclusion, the form in +which the weak operator topology is usually phrased. -/ +lemma isWOTClosed_iff_isClosed_image (S : Set B) : + IsWOTClosed (H := H) S ↔ + IsClosed (ContinuousLinearMapWOT.ContinuousLinearMap.toWOTCLM + (σ := RingHom.id ℂ) (E := H) (F := H) '' S) := by + rw [IsWOTClosed, Set.toWOT_eq_image] + rfl + /-- The commutant `Set.centralizer S` is WOT-closed. -/ lemma isWOTClosed_centralizer (S : Set B) : IsWOTClosed (H := H) (Set.centralizer S) := by -- Express the commutant as an intersection of commuting constraints. diff --git a/QuantumSystem/ForMathlib/Analysis/Normed/Lp/Separable.lean b/QuantumSystem/ForMathlib/Analysis/Normed/Lp/Separable.lean new file mode 100644 index 0000000..647e1ed --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/Normed/Lp/Separable.lean @@ -0,0 +1,86 @@ +module + +public import Mathlib.Analysis.Normed.Lp.lpSpace +public import Mathlib.Topology.Algebra.Module.Basic + +/-! +# Separability of `lp` over a countable index + +An `lp` space over a **countable** index type, whose summands are separable, is separable — +provided `p ≠ ⊤`. The exponent restriction is essential: `lp G ⊤` is the space of bounded +families, which is not separable over an infinite index even when every summand is `ℂ`. + +The proof is the standard one: the singles span a dense subspace, because +`lp.hasSum_single` expands every element as the sum of its coordinates, and a countable +union of continuous images of separable spaces spans a separable submodule. + +Mathlib has `PiLp.secondCountableTopology` for the finite-product form and +`WithLp.secondCountableTopology` for the binary one, but nothing for the `lp` subtype; this +file supplies it. + +## Main results + +* `lp.separableSpace_of_ne_top` — the statement above, for an arbitrary exponent `p ≠ ⊤`. + The scalar field is an explicit argument, since it appears only in the proof. +* `lp.instSeparableSpaceOfCountable` — the same at `p = 2` over `ℂ`, as an instance; this + is the case the ℓ²-direct sum of complex Hilbert spaces uses. +-/ + +@[expose] public section + +open TopologicalSpace + +open scoped ENNReal + +namespace lp + +variable {ι : Type*} {G : ι → Type*} [∀ i, NormedAddCommGroup (G i)] + +/-- The single-coordinate embedding `G i → lp G p` is continuous. + +It is in fact isometric — `lp.norm_single` — but continuity is all that is needed +downstream. The scalar field is explicit because it occurs only in the proof, which routes +through the linear map `lp.lsingle` to get additivity. -/ +lemma continuous_single (𝕜 : Type*) [NontriviallyNormedField 𝕜] [∀ i, NormedSpace 𝕜 (G i)] + [DecidableEq ι] {p : ℝ≥0∞} [Fact (1 ≤ p)] (i : ι) : + Continuous (fun x : G i => lp.single p i x) := by + have hp0 : (0 : ℝ≥0∞) < p := lt_of_lt_of_le zero_lt_one Fact.out + have h : ∀ x : G i, ‖lp.lsingle (𝕜 := 𝕜) p i x‖ = ‖x‖ := fun x => by + simpa using lp.norm_single hp0 i x + simpa using (AddMonoidHomClass.isometry_of_norm (lp.lsingle (𝕜 := 𝕜) p i) h).continuous + +/-- An `lp` space over a countable index with separable summands is separable, for any +exponent `p ≠ ⊤`. + +The hypothesis `p ≠ ⊤` cannot be dropped: `lp (fun _ : ℕ => ℂ) ⊤` is `ℓ^∞`, which is not +separable. The scalar field `𝕜` is explicit because it does not occur in the conclusion. -/ +theorem separableSpace_of_ne_top (𝕜 : Type*) [NontriviallyNormedField 𝕜] [SeparableSpace 𝕜] + [∀ i, NormedSpace 𝕜 (G i)] [Countable ι] [∀ i, SeparableSpace (G i)] + {p : ℝ≥0∞} [Fact (1 ≤ p)] (hp : p ≠ ⊤) : + SeparableSpace (lp G p) := by + classical + set S : Set (lp G p) := ⋃ i, Set.range (fun x : G i => lp.single p i x) with hSdef + have hS : IsSeparable S := + IsSeparable.iUnion fun i => isSeparable_range (continuous_single 𝕜 i) + have hspan : IsSeparable ((Submodule.span 𝕜 S : Submodule 𝕜 (lp G p)) : Set (lp G p)) := + hS.span + -- The singles span a dense subspace: every element is the sum of its coordinates. + have hdense : Dense ((Submodule.span 𝕜 S : Submodule 𝕜 (lp G p)) : Set (lp G p)) := by + intro f + refine mem_closure_of_tendsto (lp.hasSum_single hp f) ?_ + filter_upwards with s + refine Submodule.sum_mem _ fun i _ => Submodule.subset_span ?_ + exact Set.mem_iUnion.mpr ⟨i, Set.mem_range_self _⟩ + rw [← isSeparable_univ_iff, ← hdense.closure_eq] + exact hspan.closure + +/-- The `p = 2`, `𝕜 = ℂ` case of `lp.separableSpace_of_ne_top`, as an instance. + +Both the exponent and the scalar field are fixed here rather than left as variables: an +instance can carry neither the hypothesis `p ≠ ⊤` nor a scalar field absent from its +conclusion. -/ +instance instSeparableSpaceOfCountable [∀ i, NormedSpace ℂ (G i)] [Countable ι] + [∀ i, SeparableSpace (G i)] : SeparableSpace (lp G 2) := + separableSpace_of_ne_top ℂ (by simp) + +end lp diff --git a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Abs.lean b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Abs.lean deleted file mode 100644 index 95f98ae..0000000 --- a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Abs.lean +++ /dev/null @@ -1,212 +0,0 @@ -module - -public import Mathlib.Analysis.InnerProductSpace.StarOrder -public import Mathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.Abs - -open scoped InnerProductSpace -open ContinuousLinearMap - -@[expose] public section - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] - -/-! ### Absolute value of an operator -/ - -section AbsoluteValue - -/-- `T†T` is always a non-negative operator (in the Loewner order). -/ -lemma adjoint_mul_self_nonneg (T : H →L[ℂ] H) : 0 ≤ T.adjoint * T := by - rw [nonneg_iff_isPositive] - exact isPositive_adjoint_comp_self T - -/-- The absolute value of a bounded linear operator `T`, defined as `√(T†T)`. -This uses the continuous functional calculus for positive operators. -This equals `CFC.abs T` since `star T = T.adjoint` for ContinuousLinearMap. -/ -noncomputable def absoluteValue (T : H →L[ℂ] H) : H →L[ℂ] H := - CFC.sqrt (T.adjoint * T) - -lemma absoluteValue_eq_cfc_abs (T : H →L[ℂ] H) : absoluteValue T = CFC.abs T := by - simp only [absoluteValue, CFC.abs, ContinuousLinearMap.star_eq_adjoint] - -/-- The absolute value of an operator is non-negative. -/ -lemma absoluteValue_nonneg (T : H →L[ℂ] H) : 0 ≤ absoluteValue T := by - rw [absoluteValue_eq_cfc_abs] - exact CFC.abs_nonneg T - -/-- The absolute value of an operator is self-adjoint. -/ -lemma absoluteValue_isSelfAdjoint (T : H →L[ℂ] H) : IsSelfAdjoint (absoluteValue T) := by - have h := absoluteValue_nonneg T - rw [nonneg_iff_isPositive] at h - exact h.isSelfAdjoint - -/-- The absolute value of an operator is positive (in the sense of IsPositive). -/ -lemma absoluteValue_isPositive (T : H →L[ℂ] H) : (absoluteValue T).IsPositive := by - rw [← nonneg_iff_isPositive] - exact absoluteValue_nonneg T - -/-- `|T|² = T†T` -/ -lemma absoluteValue_sq (T : H →L[ℂ] H) : absoluteValue T * absoluteValue T = T.adjoint * T := by - unfold absoluteValue - exact CFC.sqrt_mul_sqrt_self _ (adjoint_mul_self_nonneg T) - -/-- The absolute value of the zero operator is zero. -/ -lemma absoluteValue_zero : absoluteValue (0 : H →L[ℂ] H) = 0 := by - unfold absoluteValue - have h : (0 : H →L[ℂ] H).adjoint = 0 := by ext; simp - simp only [h, zero_mul] - exact CFC.sqrt_zero - -/-- For a positive operator T, the absolute value equals T itself. -/ -lemma absoluteValue_of_nonneg {T : H →L[ℂ] H} (hT : 0 ≤ T) : absoluteValue T = T := by - unfold absoluteValue - have hpos : T.IsPositive := by rwa [← nonneg_iff_isPositive] - have hsa : T.adjoint = T := hpos.isSelfAdjoint.adjoint_eq - rw [hsa, ← sq] - exact CFC.sqrt_sq T hT - -lemma absoluteValue_smul (c : ℂ) (T : H →L[ℂ] H) : - absoluteValue (c • T) = ‖c‖ • absoluteValue T := by - letI : IsScalarTower ℝ ℂ H := ⟨fun r z x => by - rw [show (r • z : ℂ) = (r : ℂ) * z from rfl, mul_smul] - rfl⟩ - letI : IsScalarTower ℝ ℂ (H →L[ℂ] H) := ⟨fun r z S => by - apply ContinuousLinearMap.ext - intro x - change (r • z) • S x = r • z • S x - exact smul_assoc r z (S x)⟩ - rw [absoluteValue_eq_cfc_abs, absoluteValue_eq_cfc_abs] - exact CFC.abs_smul c T - -lemma norm_absoluteValue_eq_norm (T : H →L[ℂ] H) (x : H) : ‖absoluteValue T x‖ = ‖T x‖ := by - have hP := absoluteValue_isSelfAdjoint T - let P := absoluteValue T - have h_inner : ‖P x‖^2 = ‖T x‖^2 := by - simp only [← inner_self_eq_norm_sq (𝕜 := ℂ) _] - change (⟪P x, P x⟫_ℂ).re = (⟪T x, T x⟫_ℂ).re - calc (⟪P x, P x⟫_ℂ).re - = (⟪P.adjoint (P x), x⟫_ℂ).re := by rw [adjoint_inner_left] - _ = (⟪P (P x), x⟫_ℂ).re := by rw [hP.adjoint_eq] - _ = (⟪(P * P) x, x⟫_ℂ).re := rfl - _ = (⟪(T.adjoint * T) x, x⟫_ℂ).re := by rw [absoluteValue_sq] - _ = (⟪T.adjoint (T x), x⟫_ℂ).re := rfl - _ = (⟪T x, T x⟫_ℂ).re := by rw [adjoint_inner_left] - exact (sq_eq_sq₀ (norm_nonneg _) (norm_nonneg _)).mp h_inner - -lemma absoluteValue_ker_eq_ker (T : H →L[ℂ] H) : - LinearMap.ker (absoluteValue T).toLinearMap = LinearMap.ker T.toLinearMap := by - ext x - simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe] - constructor - · intro h - rw [← norm_eq_zero, ← norm_absoluteValue_eq_norm, h, norm_zero] - · intro h - rw [← norm_eq_zero, norm_absoluteValue_eq_norm, h, norm_zero] - -lemma absoluteValue_adjoint_sq (T : H →L[ℂ] H) : - absoluteValue T.adjoint * absoluteValue T.adjoint = T * T.adjoint := by - simpa [adjoint_adjoint] using (absoluteValue_sq (T := T.adjoint)) - -/-! ### Polar decomposition identities -/ - -/-- (U|T|U†)² = T·T† when T = U|T| and U†U|T| = |T| -/ -lemma conjugate_abs_sq_eq {T U : H →L[ℂ] H} - (hT_polar : T = U * absoluteValue T) - (hU_adj_U_abs : U.adjoint * U * absoluteValue T = absoluteValue T) : - (U * absoluteValue T * U.adjoint) * (U * absoluteValue T * U.adjoint) = T * T.adjoint := by - have h2 : T.adjoint = absoluteValue T * U.adjoint := by - have h_adj : (U * absoluteValue T).adjoint = (absoluteValue T).adjoint * U.adjoint := by - change star (U * absoluteValue T) = star (absoluteValue T) * star U - rw [star_mul] - conv_lhs => rw [hT_polar] - rw [h_adj, (absoluteValue_isSelfAdjoint T).adjoint_eq] - set absT := absoluteValue T with h_absT_def - -- (U|T|U†)² = U|T|(U†U)|T|U† = U|T|²U† - have lhs_eq : (U * absT * U.adjoint) * (U * absT * U.adjoint) - = U * absT * absT * U.adjoint := by - have hU_adj_U_absT : U.adjoint * U * absT = absT := hU_adj_U_abs - calc (U * absT * U.adjoint) * (U * absT * U.adjoint) - = U * absT * (U.adjoint * U * absT) * U.adjoint := by simp only [mul_assoc] - _ = U * absT * absT * U.adjoint := by rw [hU_adj_U_absT] - -- T·T† = (U|T|)(|T|U†) = U|T|²U† - have h2' : T.adjoint = absT * U.adjoint := h2 - have rhs_eq : T * T.adjoint = U * absT * absT * U.adjoint := by - calc T * T.adjoint - = T * (absT * U.adjoint) := by rw [h2'] - _ = (U * absT) * (absT * U.adjoint) := by rw [hT_polar] - _ = U * absT * absT * U.adjoint := by simp only [mul_assoc] - rw [lhs_eq, rhs_eq] - -/-- U|T|U† is a positive operator when |T| is the absolute value -/ -lemma conjugate_abs_nonneg {T U : H →L[ℂ] H} : - 0 ≤ U * absoluteValue T * U.adjoint := by - set absT := absoluteValue T with h_absT_def - rw [ContinuousLinearMap.le_def] - constructor - · -- Symmetry: (U|T|U†)† = U|T|†U†† = U|T|U† - simp only [sub_zero] - change ((U * absT * U.adjoint) : H →L[ℂ] H).toLinearMap.IsSymmetric - have h_self_adj : IsSelfAdjoint (U * absT * U.adjoint) := by - rw [isSelfAdjoint_iff'] - -- Need: (U * absT * U†)† = U * absT * U† - have step1 : (U * absT * U.adjoint).adjoint = U.adjoint.adjoint * (U * absT).adjoint := by - change star _ = star (adjoint U) * star (U * absT) - rw [star_mul] - have step2 : (U * absT).adjoint = absT.adjoint * U.adjoint := by - change star _ = star absT * star U - rw [star_mul] - rw [step1, step2, ContinuousLinearMap.adjoint_adjoint] - rw [h_absT_def, (absoluteValue_isSelfAdjoint T).adjoint_eq, ← h_absT_def] - simp only [mul_assoc] - exact h_self_adj.isSymmetric - · intro x - simp only [ContinuousLinearMap.reApplyInnerSelf, sub_zero, mul_apply] - -- Goal: 0 ≤ re⟨U(|T|(U†x)), x⟩ = re⟨|T|(U†x), U†x⟩ - have eq : ⟪U (absT (U.adjoint x)), x⟫_ℂ = ⟪absT (U.adjoint x), U.adjoint x⟫_ℂ := by - rw [← adjoint_inner_left U.adjoint, ContinuousLinearMap.adjoint_adjoint] - rw [eq, h_absT_def] - have h_abs_pos := absoluteValue_isPositive T - rw [ContinuousLinearMap.isPositive_def] at h_abs_pos - have h := h_abs_pos.2 (U.adjoint x) - simp only [ContinuousLinearMap.reApplyInnerSelf_apply] at h - exact h - -/-- For polar decomposition T = U|T| with U partial isometry, |T†| = U|T|U† -/ -lemma absoluteValue_adjoint_eq_conjugate_by_partial_isometry {T U : H →L[ℂ] H} - (hT_polar : T = U * absoluteValue T) - (hU_adj_U_abs : U.adjoint * U * absoluteValue T = absoluteValue T) : - absoluteValue T.adjoint = U * absoluteValue T * U.adjoint := by - set absT := absoluteValue T with h_absT_def - have h_sq : (U * absT * U.adjoint) * (U * absT * U.adjoint) = T * T.adjoint := - conjugate_abs_sq_eq hT_polar hU_adj_U_abs - have h_pos : 0 ≤ U * absT * U.adjoint := conjugate_abs_nonneg - have h_absT_adj_sq : absoluteValue T.adjoint * absoluteValue T.adjoint = T * T.adjoint := - absoluteValue_adjoint_sq T - have h_both_positive : 0 ≤ absoluteValue T.adjoint := absoluteValue_nonneg T.adjoint - -- Both U|T|U† and |T†| are positive with same square, so equal by sqrt uniqueness - calc absoluteValue T.adjoint - = CFC.sqrt (absoluteValue T.adjoint * absoluteValue T.adjoint) := - (CFC.sqrt_sq (absoluteValue T.adjoint) h_both_positive).symm - _ = CFC.sqrt (T * T.adjoint) := by rw [h_absT_adj_sq] - _ = CFC.sqrt (U * absT * U.adjoint * (U * absT * U.adjoint)) := by - rw [← h_sq] - _ = U * absT * U.adjoint := - CFC.sqrt_sq (U * absT * U.adjoint) h_pos - -/-- S x = 0 when x ∈ ker |T| and S = √|T| -/ -lemma cfc_sqrt_absoluteValue_ker {T : H →L[ℂ] H} - (x : H) (hx : absoluteValue T x = 0) : - CFC.sqrt (absoluteValue T) x = 0 := by - let S := CFC.sqrt (absoluteValue T) - have hS_sq : S * S = absoluteValue T := CFC.sqrt_mul_sqrt_self _ (absoluteValue_nonneg T) - have hS_sa : IsSelfAdjoint S := (CFC.sqrt_nonneg _).isSelfAdjoint - have h_norm_sq : ‖S x‖^2 = (⟪absoluteValue T x, x⟫_ℂ).re := by - have h1 : ‖S x‖^2 = Complex.re ⟪S x, S x⟫_ℂ := by - rw [(inner_self_eq_norm_sq (𝕜 := ℂ) (S x)).symm]; simp - calc ‖S x‖^2 = Complex.re ⟪S x, S x⟫_ℂ := h1 - _ = Complex.re ⟪S (S x), x⟫_ℂ := by rw [← adjoint_inner_left, hS_sa.adjoint_eq] - _ = Complex.re ⟪(S * S) x, x⟫_ℂ := by rfl - _ = Complex.re ⟪absoluteValue T x, x⟫_ℂ := by rw [hS_sq] - rw [hx, inner_zero_left, Complex.zero_re] at h_norm_sq - exact norm_eq_zero.mp (eq_zero_of_pow_eq_zero h_norm_sq) - -end AbsoluteValue diff --git a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PartialIsometry.lean b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PartialIsometry.lean deleted file mode 100644 index e4d4ef7..0000000 --- a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PartialIsometry.lean +++ /dev/null @@ -1,341 +0,0 @@ -module - -public import Mathlib.Analysis.InnerProductSpace.Adjoint - -@[expose] public section - -open ContinuousLinearMap InnerProductSpace - -variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] - -/-- A continuous linear map `U` is a partial isometry if `U†U` is a projection. -/ -def IsPartialIsometry (U : H →L[ℂ] H) : Prop := - U.adjoint * U * (U.adjoint * U) = U.adjoint * U - -/-- Alternative characterization: U is a partial isometry iff UU†U = U. -/ -lemma isPartialIsometry_iff (U : H →L[ℂ] H) : - IsPartialIsometry U ↔ U * U.adjoint * U = U := by - constructor - · intro h - -- U†U is a projection P. - -- We want to show U P = U. - -- Is equivalent to U (1 - P) = 0. - -- || U (1 - P) x ||² = ⟨ (1-P)x, U† U (1-P) x ⟩ = ⟨ (1-P)x, P (1-P) x ⟩ = 0 - let P := U.adjoint * U - have hP : P * P = P := h - have h_orth : U * (1 - P) = 0 := by - ext x - refine norm_eq_zero.mp ?_ - have h0 : ‖(U * (1 - P)) x‖ ^ 2 = 0 := by - rw [← inner_self_eq_norm_sq (𝕜 := ℂ) (x := (U * (1 - P)) x)] - change Complex.re ⟪U ((1 - P) x), U ((1 - P) x)⟫_ℂ = 0 - rw [← adjoint_inner_right] - -- rewrite `U† (U ((1 - P) x))` as `P ((1 - P) x)` - simp only [P] - -- U† U (x - P x) = P (x - P x) = P x - P² x = P x - P x = 0 - have hPP : P (P x) = P x := by - simpa [P, ContinuousLinearMap.mul_apply, mul_assoc] using - congrArg (fun T => T x) hP - have h1 : (1 - P) x = x - P x := by - simp [sub_eq_add_neg] - have h2 : P ((1 - P) x) = P x - P (P x) := by - rw [h1, map_sub] - have h3 : U.adjoint (U (x - P x)) = P (x - P x) := by rfl - rw [h1, h3, map_sub, hPP, sub_self, inner_zero_right, Complex.zero_re] - exact (sq_eq_zero_iff.mp h0) - have h_orth' : U * (P - 1) = 0 := by - calc - U * (P - 1) = U * (-(1 - P)) := by - simp [sub_eq_add_neg, add_comm] - _ = - (U * (1 - P)) := by - simp [sub_eq_add_neg, mul_add, mul_one, add_comm] - _ = 0 := by simp [h_orth] - -- `U * (P - 1) = 0` rewrites to `U * U† * U - U = 0` - have h_orth'' : U * U.adjoint * U - U = 0 := by - simpa [P, mul_sub, mul_assoc, mul_one] using h_orth' - have h_eq : U * U.adjoint * U = U := by - calc - U * U.adjoint * U = (U * U.adjoint * U - U) + U := by abel - _ = 0 + U := by simp [h_orth''] - _ = U := by simp - exact h_eq - · intro h - -- We want IsPartialIsometry U, i.e., U† U U† U = U† U - have h_adj : U.adjoint * U * U.adjoint = U.adjoint := by - -- take adjoint of `U * U† * U = U` - simpa [mul_def, adjoint_comp, adjoint_adjoint, mul_assoc] using - congrArg ContinuousLinearMap.adjoint h - calc - U.adjoint * U * (U.adjoint * U) - = (U.adjoint * U * U.adjoint) * U := by simp [mul_assoc] - _ = U.adjoint * U := by simp [h_adj] - -/-- The adjoint of a partial isometry is a partial isometry. -/ -lemma IsPartialIsometry.adjoint {U : H →L[ℂ] H} (hU : IsPartialIsometry U) : - IsPartialIsometry U.adjoint := by - rw [isPartialIsometry_iff] at hU ⊢ - -- We want U† U†† U† = U†. - -- i.e., U† U U† = U†. - rw [adjoint_adjoint] - -- We know U U† U = U. Taking adjoint gives U† U U† = U†. - -- take adjoint of `U * U† * U = U` - simpa [mul_def, adjoint_comp, adjoint_adjoint, mul_assoc] using - congrArg ContinuousLinearMap.adjoint hU - -/-- A partial isometry is an isometry on the orthogonal complement of its kernel. - Note: (ker U)ᗮ = range U†. -/ -lemma IsPartialIsometry.norm_of_mem_initialSpace {U : H →L[ℂ] H} - (hU : IsPartialIsometry U) (x : H) (hx : x ∈ (LinearMap.ker U.toLinearMap)ᗮ) : - ‖U x‖ = ‖x‖ := by - -- ‖U x‖² = ⟨x, U† U x⟩. - -- If x ∈ (ker U)ᗮ, then x ∈ range U† (since range U† is closed? In finite dim yes. - -- In general, (ker U)ᗮ = closure (range U†). - -- Wait, U† U is a projection onto range U†. - -- Let P = U† U. If hU, P is a projection. - -- range P = range U† (standard fact for P = T* T? No, range P ⊆ range U†. Also range U† U ⊆ range U†. - -- Actually range U† = range (U† U) because range U† U ⊆ range U†. - -- And if y = U† z, U† U y = U† U U† z = U† z = y using U U† U = U. - -- So range U† ⊆ range P. Thus range P = range U†. - -- P is an orthogonal projection onto range U†. - -- So for x ∈ range U†, P x = x. - -- ‖U x‖² = ⟨x, P x⟩ = ⟨x, x⟩ = ‖x‖². - - -- But argument x is in (ker U)ᗮ. - -- We need to know P is the projection onto (ker U)ᗮ. - -- ker P = ker (U† U) = ker U. - -- So range P = (ker P)ᗮ = (ker U)ᗮ. - -- So x ∈ range P. - let P := U.adjoint * U - have hP_proj : P * P = P := hU - -- We want to show ⟨x, P x⟩ = ⟨x, x⟩ - have h_P_mem : P x = x := by - -- P is orthogonal projection onto range P. - -- range P = (ker P)ᗮ = (ker U)ᗮ. - -- Wait, P is self-adjoint idempotent, so it is orthogonal projection onto range P. - -- range P = (ker P)ᗮ because P is self-adjoint. - -- ker P = ker U? - -- ker U ⊆ ker P: U x = 0 → U† U x = 0. - -- ker P ⊆ ker U: U† U x = 0 → ⟨x, U† U x⟩ = 0 → ‖U x‖² = 0 → U x = 0. - -- So ker P = ker U. - -- Therefore range P = (ker U)ᗮ. - -- Since x ∈ (ker U)ᗮ, x ∈ range P. - -- So P x = x. - - -- Let's prove ker P = ker U formally. - have h_ker : LinearMap.ker P.toLinearMap = LinearMap.ker U.toLinearMap := by - ext y - simp only [LinearMap.mem_ker] -- `P.toLinearMap y` is definitionaly `P y` - constructor - · intro hy - -- U† U y = 0 ⇒ U y = 0 - have h0 : ‖U y‖ ^ 2 = 0 := by - rw [← inner_self_eq_norm_sq (𝕜 := ℂ) (x := U y)] - rw [← adjoint_inner_right] - -- `U† (U y) = P y = 0` - have hy' : U.adjoint (U y) = 0 := by - simpa [P, ContinuousLinearMap.mul_apply] using hy - simp [hy', inner_zero_right] - have h1 : ‖U y‖ = 0 := (sq_eq_zero_iff.mp h0) - exact (norm_eq_zero.mp h1) - · intro hy - dsimp [P] - calc - (U.adjoint) (U y) = U.adjoint 0 := by - have hy' : U y = 0 := by simpa using hy - simp [hy'] - _ = 0 := by simp - -- P is self-adjoint - have h_sa : IsSelfAdjoint P := by - -- `(U†U)† = U†U` - simpa [P, star_eq_adjoint] using (IsSelfAdjoint.star_mul_self U) - -- range P is closed? - -- range P = ker(1-P). Since P is continuous, ker(1-P) is closed. - -- So range P is closed. - -- For orthogonal projection P, range P = (ker P)ᗮ. - -- We need this fact from Mathlib. `LinearMap.isProj_iff_idempotent_and_...`? - -- Actually `OrthogonalProjection` exists. - -- But we defined IsPartialIsometry manually. - - -- Let's rely on P x = x iff x ∈ (ker P)ᗮ. - -- Mathlib has `orthogonalProjection_eq_self_iff`. - -- We can construct the `orthogonalProjection` structure from P. - - -- Or just prove manually: - -- If x ∈ (ker P)ᗮ, then P x - x ∈ ? - -- P(P x - x) = P x - P x = 0. So P x - x ∈ ker P. - -- Also P x - x ∈ range P + x? No. - -- P x ∈ range P = (ker P)ᗮ. - -- So P x - x ∈ (ker P)ᗮ. - -- The only vector in intersection of ker P and (ker P)ᗮ is 0. - -- So P x = x. - - -- Use the idempotent+symmetry characterization to identify the range. - have hP_idem : IsIdempotentElem P := hP_proj - have hP_symm : (P : H →ₗ[ℂ] H).IsSymmetric := - (ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric).1 h_sa - have h_orth : (LinearMap.range P.toLinearMap)ᗮ = LinearMap.ker P.toLinearMap := - (LinearMap.IsIdempotentElem.isSymmetric_iff_orthogonal_range hP_idem.toLinearMap).1 hP_symm - have h_range_closed : IsClosed (LinearMap.range P.toLinearMap : Set H) := - (IsIdempotentElem.isClosed_range (p := P) hP_idem) - -- Hence (ker P)ᗮ = range P - have h_eq_range : (LinearMap.ker P.toLinearMap)ᗮ = LinearMap.range P.toLinearMap := by - calc - (LinearMap.ker P.toLinearMap)ᗮ = (LinearMap.range P.toLinearMap)ᗮᗮ := by - simp [h_orth] - _ = (LinearMap.range P.toLinearMap).topologicalClosure := by - simpa using (Submodule.orthogonal_orthogonal_eq_closure - (K := LinearMap.range P.toLinearMap)) - _ = LinearMap.range P.toLinearMap := - (IsClosed.submodule_topologicalClosure_eq h_range_closed) - -- rewrite `hx` into membership in range P - have hx' : x ∈ (LinearMap.ker P.toLinearMap)ᗮ := by - simpa [h_ker] using hx - have hx_range : x ∈ LinearMap.range P.toLinearMap := by - simpa [h_eq_range] using hx' - rcases hx_range with ⟨y, rfl⟩ - -- `P (P y) = P y` - simpa [P, mul_assoc, ContinuousLinearMap.mul_apply] using - congrArg (fun T => T y) hP_proj - -- Now compute squared norms using `P x = x`. - have hsq : ‖U x‖ ^ 2 = ‖x‖ ^ 2 := by - rw [← inner_self_eq_norm_sq (𝕜 := ℂ) (x := U x)] - change Complex.re ⟪U x, U x⟫_ℂ = ‖x‖ ^ 2 - rw [← adjoint_inner_right] - have hPx : U.adjoint (U x) = P x := by rfl - rw [hPx, h_P_mem] - simpa using (inner_self_eq_norm_sq (𝕜 := ℂ) (x := x)) - have hnonneg1 : 0 ≤ ‖U x‖ := norm_nonneg _ - have hnonneg2 : 0 ≤ ‖x‖ := norm_nonneg _ - have hsq' : ‖U x‖ = ‖x‖ := by - have h' := sq_eq_sq_iff_eq_or_eq_neg.mp (by simpa [pow_two] using hsq) - cases h' with - | inl h => exact h - | inr h => nlinarith - exact hsq' - -/-- For a partial isometry U, U†U = id on (ker U)ᗮ -/ -lemma IsPartialIsometry.adjoint_mul_self_apply_of_mem_ker_orthogonal {U : H →L[ℂ] H} - (hU : IsPartialIsometry U) - (x : H) (hx : x ∈ (LinearMap.ker U.toLinearMap)ᗮ) : - U.adjoint (U x) = x := by - let P := U.adjoint * U - have hP_proj : P * P = P := hU - have h_sa : IsSelfAdjoint P := IsSelfAdjoint.star_mul_self U - have h_ker_P : LinearMap.ker P.toLinearMap = LinearMap.ker U.toLinearMap := by - ext y - simp only [LinearMap.mem_ker, ContinuousLinearMap.coe_coe] - constructor - · intro hy - have h0 : ‖U y‖ ^ 2 = 0 := by - rw [← inner_self_eq_norm_sq (𝕜 := ℂ), ← adjoint_inner_right] - have : U.adjoint (U y) = P y := rfl - rw [this, hy, inner_zero_right] - rfl - exact norm_eq_zero.mp (sq_eq_zero_iff.mp h0) - · intro hy - change P y = 0 - calc P y = (U.adjoint * U) y := rfl - _ = U.adjoint (U y) := rfl - _ = U.adjoint 0 := by rw [hy] - _ = 0 := map_zero _ - have hP_idem : IsIdempotentElem P := hP_proj - have hP_symm : (P : H →ₗ[ℂ] H).IsSymmetric := - ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric.1 h_sa - have h_orth : (LinearMap.range P.toLinearMap)ᗮ = LinearMap.ker P.toLinearMap := - (LinearMap.IsIdempotentElem.isSymmetric_iff_orthogonal_range hP_idem.toLinearMap).1 hP_symm - have h_range_closed : IsClosed (LinearMap.range P.toLinearMap : Set H) := - IsIdempotentElem.isClosed_range hP_idem - have h_eq_range : (LinearMap.ker P.toLinearMap)ᗮ = LinearMap.range P.toLinearMap := by - calc (LinearMap.ker P.toLinearMap)ᗮ = (LinearMap.range P.toLinearMap)ᗮᗮ := by simp [h_orth] - _ = (LinearMap.range P.toLinearMap).topologicalClosure := by - simpa using Submodule.orthogonal_orthogonal_eq_closure (K := LinearMap.range P.toLinearMap) - _ = LinearMap.range P.toLinearMap := IsClosed.submodule_topologicalClosure_eq h_range_closed - have hx' : x ∈ (LinearMap.ker P.toLinearMap)ᗮ := by rw [h_ker_P]; exact hx - have hx_range : x ∈ LinearMap.range P.toLinearMap := by rw [h_eq_range] at hx'; exact hx' - rcases hx_range with ⟨y, hy⟩ - calc U.adjoint (U x) = P x := rfl - _ = P (P y) := by - congr 1 - exact hy.symm - _ = P y := by - have := congrArg (· y) hP_proj - simp only [mul_apply] at this - exact this - _ = x := hy - -/-- A partial isometry has operator norm at most 1. - This follows from ‖U x‖ = ‖x‖ on (ker U)ᗮ and ‖U x‖ = 0 on ker U. -/ -lemma IsPartialIsometry.norm_le_one {U : H →L[ℂ] H} (hU : IsPartialIsometry U) : - ‖U‖ ≤ 1 := by - apply ContinuousLinearMap.opNorm_le_bound _ zero_le_one - intro x - simp only [one_mul] - -- Use U†U is a self-adjoint idempotent (projection) - let P := U.adjoint * U - have hP_idem : P * P = P := hU - have hP_sa : IsSelfAdjoint P := IsSelfAdjoint.star_mul_self U - -- ‖U x‖² = ⟨x, U†U x⟩ = ⟨x, P x⟩ - have h_norm_sq : ‖U x‖ ^ 2 = (⟪x, P x⟫_ℂ).re := by - rw [← inner_self_eq_norm_sq (𝕜 := ℂ)] - change Complex.re ⟪U x, U x⟫_ℂ = (⟪x, P x⟫_ℂ).re - rw [← adjoint_inner_right] - rfl - -- For self-adjoint idempotent P: 0 ≤ ⟨x, P x⟩ ≤ ⟨x, x⟩ - -- ⟨x, Px⟩ = ⟨Px, Px⟩ since P² = P and P = P† - have h_Px_eq : (⟪x, P x⟫_ℂ).re = ‖P x‖ ^ 2 := by - have hPsa : P.adjoint = P := hP_sa.adjoint_eq - have hPPx : P (P x) = P x := by - have := congrArg (fun T => T x) hP_idem - simp only [mul_apply] at this - exact this - -- ⟨x, Px⟩ = ⟨x, PPx⟩ = ⟨P†x, Px⟩ = ⟨Px, Px⟩ - calc (⟪x, P x⟫_ℂ).re - _ = (⟪x, P (P x)⟫_ℂ).re := by rw [hPPx] - _ = (⟪P.adjoint x, P x⟫_ℂ).re := by rw [adjoint_inner_left] - _ = (⟪P x, P x⟫_ℂ).re := by rw [hPsa] - _ = ‖P x‖ ^ 2 := by rw [inner_self_eq_norm_sq_to_K]; norm_cast - -- Similarly for 1 - P - have h_1mP : (⟪x, (1 - P) x⟫_ℂ).re = ‖(1 - P) x‖ ^ 2 := by - let Q := 1 - P - have hQ_idem : Q * Q = Q := by - ext y - simp only [Q, mul_apply, sub_apply, one_apply] - have hPPy : P (P y) = P y := by - have := congrArg (fun T => T y) hP_idem - simp only [mul_apply] at this - exact this - simp [hPPy] - have hQ_sa : IsSelfAdjoint Q := by - simp only [Q, IsSelfAdjoint, star_sub, star_one] - rw [hP_sa.star_eq] - have hQsa : Q.adjoint = Q := hQ_sa.adjoint_eq - have hQQx : Q (Q x) = Q x := by - have := congrArg (fun T => T x) hQ_idem - simp only [mul_apply] at this - exact this - calc (⟪x, Q x⟫_ℂ).re - _ = (⟪x, Q (Q x)⟫_ℂ).re := by rw [hQQx] - _ = (⟪Q.adjoint x, Q x⟫_ℂ).re := by rw [adjoint_inner_left] - _ = (⟪Q x, Q x⟫_ℂ).re := by rw [hQsa] - _ = ‖Q x‖ ^ 2 := by rw [inner_self_eq_norm_sq_to_K]; norm_cast - -- ⟨x, x⟩ = ⟨x, P x⟩ + ⟨x, (1-P) x⟩ - have h_decomp : (⟪x, x⟫_ℂ).re = (⟪x, P x⟫_ℂ).re + (⟪x, (1 - P) x⟫_ℂ).re := by - have h1 : ⟪x, x⟫_ℂ = ⟪x, P x⟫_ℂ + ⟪x, (1 - P) x⟫_ℂ := by - rw [← inner_add_right] - congr 1 - simp [sub_apply] - rw [h1, Complex.add_re] - have h_Px_le : (⟪x, P x⟫_ℂ).re ≤ (⟪x, x⟫_ℂ).re := by - rw [h_decomp, h_1mP] - linarith [sq_nonneg ‖(1 - P) x‖] - have h_inner_xx : (⟪x, x⟫_ℂ).re = ‖x‖ ^ 2 := by - rw [inner_self_eq_norm_sq_to_K]; norm_cast - -- ‖U x‖² ≤ ‖x‖² - have h_sq_le : ‖U x‖ ^ 2 ≤ ‖x‖ ^ 2 := by - rw [h_norm_sq] - calc (⟪x, P x⟫_ℂ).re ≤ (⟪x, x⟫_ℂ).re := h_Px_le - _ = ‖x‖ ^ 2 := h_inner_xx - -- ‖U x‖ ≤ ‖x‖ - have h_nonneg_Ux : 0 ≤ ‖U x‖ := norm_nonneg _ - have h_nonneg_x : 0 ≤ ‖x‖ := norm_nonneg _ - nlinarith [sq_nonneg (‖U x‖ - ‖x‖), sq_nonneg (‖U x‖ + ‖x‖)] diff --git a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/Pow/Real.lean b/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/Pow/Real.lean deleted file mode 100644 index 3eb8918..0000000 --- a/QuantumSystem/ForMathlib/Analysis/SpecialFunctions/Pow/Real.lean +++ /dev/null @@ -1,152 +0,0 @@ -module - -public import Mathlib.Analysis.MeanInequalities - -/-! -# Inequalities for Real Power Functions - -This file proves scalar inequalities about `Real.rpow` used in the proof of -Lieb's joint concavity theorem. - -## Main results - -- `Real.convexCombo_rpow_mul_rpow_le`: joint concavity of (x, y) ↦ xᵖ y¹⁻ᵖ - for 0 < p < 1. --/ - -@[expose] public section - -namespace Real - -/-- Joint concavity of (x, y) ↦ xᵖ · y¹⁻ᵖ for 0 < p < 1: -the convex combination of values is at most the value at the convex combination. -/ -lemma convexCombo_rpow_mul_rpow_le {p t x₁ x₂ y₁ y₂ : ℝ} - (hp0 : 0 < p) (hp1 : p < 1) - (ht0 : 0 ≤ t) (ht1 : t ≤ 1) - (hx₁ : 0 ≤ x₁) (hx₂ : 0 ≤ x₂) (hy₁ : 0 ≤ y₁) (hy₂ : 0 ≤ y₂) : - t * (x₁ ^ p * y₁ ^ (1 - p)) + (1 - t) * (x₂ ^ p * y₂ ^ (1 - p)) ≤ - (t * x₁ + (1 - t) * x₂) ^ p * (t * y₁ + (1 - t) * y₂) ^ (1 - p) := by - have h1p : 0 < 1 - p := by linarith - have hconj : p + (1 - p) = 1 := by ring - -- Handle boundary cases for t - rcases eq_or_lt_of_le ht0 with rfl | ht0' - · simp - rcases eq_or_lt_of_le ht1 with rfl | ht1' - · simp - have h1t : 0 < 1 - t := by linarith - have htne : t ≠ 0 := ne_of_gt ht0' - have h1tne : 1 - t ≠ 0 := ne_of_gt h1t - -- Handle zero cases - by_cases hsum_x_zero : t * x₁ + (1 - t) * x₂ = 0 - · -- If the x-sum is zero, both t*x₁ and (1-t)*x₂ must be zero - have htx₁ : t * x₁ = 0 := by - have h1 := mul_nonneg (le_of_lt ht0') hx₁ - have h2 := mul_nonneg (le_of_lt h1t) hx₂ - linarith - have hx₁_zero : x₁ = 0 := (mul_eq_zero.mp htx₁).resolve_left htne - have htx₂ : (1 - t) * x₂ = 0 := by linarith - have hx₂_zero : x₂ = 0 := (mul_eq_zero.mp htx₂).resolve_left h1tne - -- LHS = 0 + 0 = 0, RHS = 0^p * (...)^(1-p) ≥ 0 - simp only [hx₁_zero, hx₂_zero, Real.zero_rpow (ne_of_gt hp0), mul_zero, zero_mul, add_zero] - exact le_refl 0 - by_cases hsum_y_zero : t * y₁ + (1 - t) * y₂ = 0 - · -- If the y-sum is zero, both t*y₁ and (1-t)*y₂ must be zero - have hty₁ : t * y₁ = 0 := by - have h1 := mul_nonneg (le_of_lt ht0') hy₁ - have h2 := mul_nonneg (le_of_lt h1t) hy₂ - linarith - have hy₁_zero : y₁ = 0 := (mul_eq_zero.mp hty₁).resolve_left htne - have hty₂ : (1 - t) * y₂ = 0 := by linarith - have hy₂_zero : y₂ = 0 := (mul_eq_zero.mp hty₂).resolve_left h1tne - -- LHS = 0 + 0 = 0, RHS = (...)^p * 0^(1-p) ≥ 0 - simp only [hy₁_zero, hy₂_zero, Real.zero_rpow (ne_of_gt h1p), mul_zero, add_zero] - exact le_refl 0 - -- Main case: both sums are positive - have hsum_x_pos : 0 < t * x₁ + (1 - t) * x₂ := by - have := add_nonneg (mul_nonneg (le_of_lt ht0') hx₁) (mul_nonneg (le_of_lt h1t) hx₂) - exact lt_of_le_of_ne this (Ne.symm hsum_x_zero) - have hsum_y_pos : 0 < t * y₁ + (1 - t) * y₂ := by - have := add_nonneg (mul_nonneg (le_of_lt ht0') hy₁) (mul_nonneg (le_of_lt h1t) hy₂) - exact lt_of_le_of_ne this (Ne.symm hsum_y_zero) - -- Define normalized weights - set X := t * x₁ + (1 - t) * x₂ with hX_def - set Y := t * y₁ + (1 - t) * y₂ with hY_def - set α := (t * x₁) / X with hα_def - set β := (t * y₁) / Y with hβ_def - have hX_ne : X ≠ 0 := ne_of_gt hsum_x_pos - have hY_ne : Y ≠ 0 := ne_of_gt hsum_y_pos - have hα_nonneg : 0 ≤ α := div_nonneg (mul_nonneg (le_of_lt ht0') hx₁) (le_of_lt hsum_x_pos) - have hα_le_one : α ≤ 1 := by - rw [div_le_one (by positivity)] - exact le_add_of_nonneg_right (mul_nonneg (le_of_lt h1t) hx₂) - have hβ_nonneg : 0 ≤ β := div_nonneg (mul_nonneg (le_of_lt ht0') hy₁) (le_of_lt hsum_y_pos) - have hβ_le_one : β ≤ 1 := by - rw [div_le_one (by positivity)] - exact le_add_of_nonneg_right (mul_nonneg (le_of_lt h1t) hy₂) - have h1α_nonneg : 0 ≤ 1 - α := by linarith - have h1β_nonneg : 0 ≤ 1 - β := by linarith - -- Key identities - have htx₁_eq : t * x₁ = α * X := by - rw [hα_def] - exact (div_mul_cancel₀ (t * x₁) hX_ne).symm - have htx₂_eq : (1 - t) * x₂ = (1 - α) * X := by - have h : (1 - t) * x₂ = X - t * x₁ := by rw [hX_def]; ring - rw [h, htx₁_eq]; ring - have hty₁_eq : t * y₁ = β * Y := by - rw [hβ_def] - exact (div_mul_cancel₀ (t * y₁) hY_ne).symm - have hty₂_eq : (1 - t) * y₂ = (1 - β) * Y := by - have h : (1 - t) * y₂ = Y - t * y₁ := by rw [hY_def]; ring - rw [h, hty₁_eq]; ring - -- Use weighted AM-GM to bound the sum of geometric means - have hAMGM1 := geom_mean_le_arith_mean2_weighted (le_of_lt hp0) (le_of_lt h1p) hα_nonneg hβ_nonneg hconj - have hAMGM2 := geom_mean_le_arith_mean2_weighted (le_of_lt hp0) (le_of_lt h1p) h1α_nonneg h1β_nonneg hconj - have hbound : α ^ p * β ^ (1 - p) + (1 - α) ^ p * (1 - β) ^ (1 - p) ≤ 1 := by - calc α ^ p * β ^ (1 - p) + (1 - α) ^ p * (1 - β) ^ (1 - p) - ≤ (p * α + (1 - p) * β) + (p * (1 - α) + (1 - p) * (1 - β)) := add_le_add hAMGM1 hAMGM2 - _ = p * (α + (1 - α)) + (1 - p) * (β + (1 - β)) := by ring - _ = p * 1 + (1 - p) * 1 := by simp - _ = 1 := by ring - -- Key calculation: Express products in terms of α, β - have hprod1 : (t * x₁) ^ p * (t * y₁) ^ (1 - p) = α ^ p * β ^ (1 - p) * X ^ p * Y ^ (1 - p) := by - rw [htx₁_eq, hty₁_eq] - rw [Real.mul_rpow hα_nonneg (le_of_lt hsum_x_pos)] - rw [Real.mul_rpow hβ_nonneg (le_of_lt hsum_y_pos)] - ring - have hprod2 : ((1 - t) * x₂) ^ p * ((1 - t) * y₂) ^ (1 - p) = - (1 - α) ^ p * (1 - β) ^ (1 - p) * X ^ p * Y ^ (1 - p) := by - rw [htx₂_eq, hty₂_eq] - rw [Real.mul_rpow h1α_nonneg (le_of_lt hsum_x_pos)] - rw [Real.mul_rpow h1β_nonneg (le_of_lt hsum_y_pos)] - ring - -- Show t * (x₁ ^ p * y₁ ^ (1 - p)) = (t * x₁) ^ p * (t * y₁) ^ (1 - p) - have ht_rpow : t ^ p * t ^ (1 - p) = t := by - rw [← Real.rpow_add ht0', hconj, Real.rpow_one] - have h1t_rpow : (1 - t) ^ p * (1 - t) ^ (1 - p) = 1 - t := by - rw [← Real.rpow_add h1t, hconj, Real.rpow_one] - have hfinal1 : t * (x₁ ^ p * y₁ ^ (1 - p)) = (t * x₁) ^ p * (t * y₁) ^ (1 - p) := by - rw [Real.mul_rpow (le_of_lt ht0') hx₁, Real.mul_rpow (le_of_lt ht0') hy₁] - have h : t ^ p * x₁ ^ p * (t ^ (1 - p) * y₁ ^ (1 - p)) = - t ^ p * t ^ (1 - p) * x₁ ^ p * y₁ ^ (1 - p) := by ring - rw [h, ht_rpow, mul_assoc] - have hfinal2 : (1 - t) * (x₂ ^ p * y₂ ^ (1 - p)) = ((1 - t) * x₂) ^ p * ((1 - t) * y₂) ^ (1 - p) := by - rw [Real.mul_rpow (le_of_lt h1t) hx₂, Real.mul_rpow (le_of_lt h1t) hy₂] - have h : (1 - t) ^ p * x₂ ^ p * ((1 - t) ^ (1 - p) * y₂ ^ (1 - p)) = - (1 - t) ^ p * (1 - t) ^ (1 - p) * x₂ ^ p * y₂ ^ (1 - p) := by ring - rw [h, h1t_rpow, mul_assoc] - -- Combine everything - calc t * (x₁ ^ p * y₁ ^ (1 - p)) + (1 - t) * (x₂ ^ p * y₂ ^ (1 - p)) - = (t * x₁) ^ p * (t * y₁) ^ (1 - p) + ((1 - t) * x₂) ^ p * ((1 - t) * y₂) ^ (1 - p) := by - rw [hfinal1, hfinal2] - _ = α ^ p * β ^ (1 - p) * X ^ p * Y ^ (1 - p) + - (1 - α) ^ p * (1 - β) ^ (1 - p) * X ^ p * Y ^ (1 - p) := by - rw [hprod1, hprod2] - _ = X ^ p * Y ^ (1 - p) * (α ^ p * β ^ (1 - p) + (1 - α) ^ p * (1 - β) ^ (1 - p)) := by ring - _ ≤ X ^ p * Y ^ (1 - p) * 1 := by - apply mul_le_mul_of_nonneg_left hbound - apply mul_nonneg - · exact Real.rpow_nonneg (le_of_lt hsum_x_pos) _ - · exact Real.rpow_nonneg (le_of_lt hsum_y_pos) _ - _ = X ^ p * Y ^ (1 - p) := mul_one _ - -end Real diff --git a/QuantumSystem/ForMathlib/Analysis/VonNeumannAlgebra/Commutant.lean b/QuantumSystem/ForMathlib/Analysis/VonNeumannAlgebra/Commutant.lean new file mode 100644 index 0000000..1dfa2af --- /dev/null +++ b/QuantumSystem/ForMathlib/Analysis/VonNeumannAlgebra/Commutant.lean @@ -0,0 +1,279 @@ +module + +public import Mathlib.Analysis.VonNeumannAlgebra.Basic +public import Mathlib.Analysis.InnerProductSpace.Adjoint +public import Mathlib.Algebra.Group.Center +public import Mathlib.Algebra.Group.Equiv.Defs + +/-! +# The commutant of a set, the generated von Neumann algebra, and unitary conjugation + +Mathlib's `VonNeumannAlgebra.commutant` takes the commutant of a von Neumann algebra. This file +provides the companion constructions that take an *arbitrary* set of bounded operators, together +with the transport of a von Neumann algebra along a linear isometric equivalence: + +* `VonNeumannAlgebra.commutantSet s` — the commutant `s'` of a set `s`, as a von Neumann algebra. + The double-commutant property is automatic (`Set.centralizer_centralizer_centralizer`), and + `StarSubalgebra.centralizer` already symmetrises by `s ∪ star s`, so no self-adjointness + hypothesis is needed. +* `VonNeumannAlgebra.generated s` — the von Neumann algebra `s''` generated by `s`, defined as the + commutant of `commutantSet s`. +* `VonNeumannAlgebra.conj U N` — the spatial conjugate `U N U⋆` of a von Neumann algebra `N` on + `H` by a linear isometric equivalence `U : H ≃ₗᵢ H'`, a von Neumann algebra on `H'`. Conjugation + by the induced `*`-isomorphism `U.conjStarAlgEquiv` commutes with taking commutants + (`conj_commutant`), hence with the generated-algebra construction (`conj_generated`): this is the + statement that a unitary equivalence of representations carries `s''` to `(U s U⋆)''`. + +These let one build `B(H₁) ⊗̄ 1` and `1 ⊗̄ B(H₂)` (and any concretely-generated von Neumann +algebra) without unfolding the bicommutant by hand, and transport the generation theorem +`N = ⟨matrix units⟩''` of a type I factor along the spatial isomorphism. + +## Notation + +The prime notation of the operator-algebra literature lives in the opt-in `VonNeumannAlgebra` +scope; activate it with `open scoped VonNeumannAlgebra`. + +| Symbol | Expansion | How to activate | +|---|---|---| +| `M′` | `VonNeumannAlgebra.commutant M` | `open scoped VonNeumannAlgebra` | +| `s″` | `VonNeumannAlgebra.generated s`, i.e. `(s ∪ s⋆)''` | `open scoped VonNeumannAlgebra` | + +`⊗̄` is documentation shorthand for the von Neumann (spatial) tensor product of algebras; that +convention is stated in full in `QuantumSystem.Algebra.VonNeumannAlgebra.TensorFactor`, downstream +of this file, where the algebras it names are defined. +-/ + +@[expose] public section + +/-- A multiplicative bijection carries the centralizer of a set onto the centralizer of the image: +`φ '' s' = (φ '' s)'`. This is the algebraic core of the fact that a spatial isomorphism of von +Neumann algebras commutes with taking commutants. -/ +lemma Set.image_centralizer {M₁ M₂ F : Type*} [Mul M₁] [Mul M₂] + [EquivLike F M₁ M₂] [MulEquivClass F M₁ M₂] (φ : F) (s : Set M₁) : + ⇑φ '' Set.centralizer s = Set.centralizer (⇑φ '' s) := by + ext y + simp only [Set.mem_image, Set.mem_centralizer_iff] + constructor + · rintro ⟨x, hx, rfl⟩ _ ⟨a, ha, rfl⟩ + rw [← map_mul, ← map_mul, hx a ha] + · intro hy + obtain ⟨x, rfl⟩ := EquivLike.surjective φ y + refine ⟨x, fun a ha => ?_, rfl⟩ + have h := hy (φ a) ⟨a, ha, rfl⟩ + apply EquivLike.injective φ + rw [map_mul, map_mul, h] + +/-- **The range of a `star`-preserving map is star-closed.** The hypothesis is stated for a bare +map together with its `star`-compatibility rather than for a bundled `*`-homomorphism, so that it +applies to composites assembled on the spot — the generating sets `π(𝔄(O))` of the local von +Neumann algebras of a net are of exactly that shape — and it is what discharges the star-closedness +hypothesis of `VonNeumannAlgebra.coe_commutantSet_of_star_eq` and +`VonNeumannAlgebra.coe_generated_of_star_eq` below. -/ +theorem Set.star_range {A B : Type*} [InvolutiveStar A] [InvolutiveStar B] {f : A → B} + (hf : ∀ a, f (star a) = star (f a)) : star (Set.range f) = Set.range f := by + ext x + simp only [Set.mem_star, Set.mem_range] + constructor + · rintro ⟨a, ha⟩ + exact ⟨star a, by rw [hf, ha, star_star]⟩ + · rintro ⟨a, ha⟩ + exact ⟨star a, by rw [hf, ha]⟩ + +namespace VonNeumannAlgebra + +variable {H : Type*} [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] + +/-- A von Neumann algebra is closed under scalar multiplication. This upgrades the `SubringClass` +and `StarMemClass` instances of `VonNeumannAlgebra` with the missing `ℂ`-scalar closure, so that +the subtype `↥N` inherits an `Algebra ℂ ↥N` structure (via `SubalgebraClass.toAlgebra`) and +`*`-algebra equivalences `N ≃⋆ₐ[ℂ] _` typecheck. -/ +instance instSMulMemClass : SMulMemClass (VonNeumannAlgebra H) ℂ (H →L[ℂ] H) where + smul_mem {s} c _ hx := s.toStarSubalgebra.smul_mem hx c + +/-- The commutant `(s ∪ s⋆)′` of the *symmetrized* set of an arbitrary set of bounded operators, +packaged as a von Neumann algebra. For star-closed `s` — in particular for every self-adjoint +generating set — this coincides with the commutant `s′` of the operator-algebra literature; for +non-star-closed `s` the literal `s′` is a larger, non-star-closed algebra, and symmetrizing is +what makes the construction land in `VonNeumannAlgebra H`. -/ +noncomputable def commutantSet (s : Set (H →L[ℂ] H)) : VonNeumannAlgebra H where + toStarSubalgebra := StarSubalgebra.centralizer ℂ s + centralizer_centralizer' := by simp + +@[simp] lemma coe_commutantSet (s : Set (H →L[ℂ] H)) : + (commutantSet s : Set (H →L[ℂ] H)) = (s ∪ star s).centralizer := + StarSubalgebra.coe_centralizer ℂ s + +lemma mem_commutantSet_iff {s : Set (H →L[ℂ] H)} {z : H →L[ℂ] H} : + z ∈ commutantSet s ↔ ∀ g ∈ s, g * z = z * g ∧ star g * z = z * star g := + StarSubalgebra.mem_centralizer_iff ℂ + +/-- On a **star-closed** set the symmetrization built into `commutantSet` is vacuous: it is then +literally the commutant `s′` of the operator-algebra literature. -/ +lemma coe_commutantSet_of_star_eq {s : Set (H →L[ℂ] H)} (hs : star s = s) : + (commutantSet s : Set (H →L[ℂ] H)) = s.centralizer := by + rw [coe_commutantSet, hs, Set.union_self] + +/-- The von Neumann algebra `s''` generated by a set `s` of bounded operators, defined as the +commutant of its commutant. -/ +noncomputable def generated (s : Set (H →L[ℂ] H)) : VonNeumannAlgebra H := + (commutantSet s).commutant + +/-- For a **star-closed** generating set `generated s` is literally the double commutant `s″` of +the operator-algebra literature, with no symmetrization step. -/ +lemma coe_generated_of_star_eq {s : Set (H →L[ℂ] H)} (hs : star s = s) : + (generated s : Set (H →L[ℂ] H)) = s.centralizer.centralizer := by + rw [generated, VonNeumannAlgebra.coe_commutant, coe_commutantSet_of_star_eq hs] + +/-- The carrier of a von Neumann algebra is norm-closed: it is a double centralizer, and +centralizers are closed. (Von Neumann algebras are even weakly closed, but the norm-closedness +is what the subalgebra-membership lemmas of the continuous functional calculus consume.) -/ +lemma isClosed_coe (N : VonNeumannAlgebra H) : IsClosed (N : Set (H →L[ℂ] H)) := by + rw [show (N : Set (H →L[ℂ] H)) = Set.centralizer (Set.centralizer (N : Set (H →L[ℂ] H))) from + N.centralizer_centralizer.symm] + exact Set.isClosed_centralizer _ + +/-- `M′` denotes the commutant `VonNeumannAlgebra.commutant M`, the prime of the operator-algebra +literature. -/ +scoped postfix:max "′" => VonNeumannAlgebra.commutant + +/-- `s″` denotes the von Neumann algebra `VonNeumannAlgebra.generated s` generated by a set `s`. +For star-closed `s` this is the double prime `s''` of the operator-algebra literature; in general +it is `(s ∪ s⋆)''`, the smallest von Neumann algebra containing `s` (see `generated_le`). -/ +scoped postfix:max "″" => VonNeumannAlgebra.generated + +/-- The commutant of the generated von Neumann algebra `s''` is the commutant `s'`. -/ +@[simp] lemma commutant_generated (s : Set (H →L[ℂ] H)) : + (generated s).commutant = commutantSet s := + VonNeumannAlgebra.commutant_commutant (commutantSet s) + +/-- A set is contained in the von Neumann algebra it generates. -/ +lemma mem_generated_of_mem {s : Set (H →L[ℂ] H)} {x : H →L[ℂ] H} (hx : x ∈ s) : + x ∈ generated s := by + have h1 : x ∈ (commutantSet s : Set (H →L[ℂ] H)).centralizer := by + rw [coe_commutantSet] + exact Set.subset_centralizer_centralizer (Set.mem_union_left _ hx) + rwa [← VonNeumannAlgebra.coe_commutant] at h1 + +/-- Taking the commutant of von Neumann algebras is antitone. Named to match the Mathlib +`centralizer_le` family (`Subalgebra.centralizer_le`, `Subring.centralizer_le`, …). -/ +lemma commutant_le {P Q : VonNeumannAlgebra H} (h : P ≤ Q) : + Q.commutant ≤ P.commutant := by + intro x hx + rw [← SetLike.mem_coe, VonNeumannAlgebra.coe_commutant] at hx ⊢ + exact Set.centralizer_subset (SetLike.coe_subset_coe.2 h) hx + +/-- The von Neumann algebra generated by `s` is the smallest von Neumann algebra containing `s`: +if a von Neumann algebra `M` contains `s`, then it contains `generated s`. -/ +lemma generated_le {s : Set (H →L[ℂ] H)} {M : VonNeumannAlgebra H} + (hsM : s ⊆ (M : Set (H →L[ℂ] H))) : generated s ≤ M := by + have hstar : s ∪ star s ⊆ (M : Set (H →L[ℂ] H)) := by + refine Set.union_subset hsM fun x hx => ?_ + rw [Set.mem_star] at hx + simpa using star_mem (hsM hx) + have h1 : M.commutant ≤ (generated s).commutant := by + rw [commutant_generated] + intro x hx + rw [← SetLike.mem_coe, coe_commutantSet] + rw [← SetLike.mem_coe, VonNeumannAlgebra.coe_commutant] at hx + exact Set.centralizer_subset hstar hx + have h2 := commutant_le h1 + rwa [VonNeumannAlgebra.commutant_commutant, VonNeumannAlgebra.commutant_commutant] at h2 + +/-- The generated von Neumann algebra is monotone in the generating set. -/ +lemma generated_mono {s t : Set (H →L[ℂ] H)} (h : s ⊆ t) : generated s ≤ generated t := + generated_le fun _ hx => SetLike.mem_coe.mpr (mem_generated_of_mem (h hx)) + +/-! ### Unitary conjugation of a von Neumann algebra -/ + +variable {H' : Type*} [NormedAddCommGroup H'] [InnerProductSpace ℂ H'] [CompleteSpace H'] + +/-- The **spatial conjugate** `U N U⋆` of a von Neumann algebra `N ⊆ B(H)` by a linear isometric +equivalence `U : H ≃ₗᵢ H'`, as a von Neumann algebra on `H'`. This is the adjoint action `Ad U`, +written `U N U⋆` (and `U • N` in informal notes); since `U` is unitary, `U⋆ = U.symm`. Its carrier +is the image of `N` under the conjugation `*`-isomorphism `U.conjStarAlgEquiv : B(H) ≃⋆ₐ B(H')`; the +double-commutant property is inherited from `N` because conjugation commutes with taking +centralizers. -/ +noncomputable def conj (U : H ≃ₗᵢ[ℂ] H') (N : VonNeumannAlgebra H) : VonNeumannAlgebra H' where + toStarSubalgebra := + N.toStarSubalgebra.map (U.conjStarAlgEquiv : (H →L[ℂ] H) →⋆ₐ[ℂ] H' →L[ℂ] H') + centralizer_centralizer' := by + have hcoe : ((N.toStarSubalgebra.map + (U.conjStarAlgEquiv : (H →L[ℂ] H) →⋆ₐ[ℂ] H' →L[ℂ] H')).carrier) + = ⇑U.conjStarAlgEquiv '' (N : Set (H →L[ℂ] H)) := + StarSubalgebra.coe_map _ _ + rw [hcoe, ← Set.image_centralizer, ← Set.image_centralizer, N.centralizer_centralizer] + +@[simp] lemma coe_conj (U : H ≃ₗᵢ[ℂ] H') (N : VonNeumannAlgebra H) : + (conj U N : Set (H' →L[ℂ] H')) = ⇑U.conjStarAlgEquiv '' (N : Set (H →L[ℂ] H)) := + StarSubalgebra.coe_map _ _ + +/-- **Spatial conjugation is monotone.** If `P ≤ Q` then `U P U⋆ ≤ U Q U⋆`: conjugation transports +inclusions, since its carrier is the image of the carrier under `U.conjStarAlgEquiv`. -/ +lemma conj_mono (U : H ≃ₗᵢ[ℂ] H') {P Q : VonNeumannAlgebra H} (h : P ≤ Q) : + conj U P ≤ conj U Q := by + intro x hx + rw [← SetLike.mem_coe, coe_conj] at hx ⊢ + exact Set.image_mono (SetLike.coe_subset_coe.2 h) hx + +/-- **Conjugation commutes with the commutant.** `(U N U⋆)' = U N' U⋆`. -/ +lemma conj_commutant (U : H ≃ₗᵢ[ℂ] H') (N : VonNeumannAlgebra H) : + (conj U N).commutant = conj U N.commutant := by + apply SetLike.coe_injective + rw [VonNeumannAlgebra.coe_commutant, coe_conj, coe_conj, VonNeumannAlgebra.coe_commutant, + Set.image_centralizer] + +/-- **Conjugation commutes with the commutant of a set.** `U s' U⋆ = (U s U⋆)'`. -/ +lemma conj_commutantSet (U : H ≃ₗᵢ[ℂ] H') (s : Set (H →L[ℂ] H)) : + conj U (commutantSet s) = commutantSet (⇑U.conjStarAlgEquiv '' s) := by + have hstar : ⇑U.conjStarAlgEquiv '' star s = star (⇑U.conjStarAlgEquiv '' s) := by + ext z + simp only [Set.mem_image, Set.mem_star] + constructor + · rintro ⟨w, hw, rfl⟩ + exact ⟨star w, hw, by rw [map_star]⟩ + · rintro ⟨x, hx, hz⟩ + exact ⟨star x, by simpa using hx, by rw [map_star, hz, star_star]⟩ + apply SetLike.coe_injective + rw [coe_conj, coe_commutantSet, coe_commutantSet, Set.image_centralizer, Set.image_union, hstar] + +/-- **Conjugation commutes with the generated von Neumann algebra.** A spatial isomorphism carries +`s''` to `(U s U⋆)''`: `U ⟨s⟩'' U⋆ = ⟨U s U⋆⟩''`. -/ +lemma conj_generated (U : H ≃ₗᵢ[ℂ] H') (s : Set (H →L[ℂ] H)) : + conj U (generated s) = generated (⇑U.conjStarAlgEquiv '' s) := by + rw [generated, ← conj_commutant, conj_commutantSet, generated] + +/-! ### Transport of a von Neumann algebra as a `*`-algebra -/ + +/-- **Spatial conjugation as a `*`-algebra equivalence.** The conjugation `*`-isomorphism +`U.conjStarAlgEquiv : B(H) ≃⋆ₐ B(H')` restricts to a `*`-isomorphism `N ≃⋆ₐ U N U⋆` of the +subtypes: the carrier of `conj U N` is exactly the image of `N` (`coe_conj`), so `x ↦ U x U⋆` +is a bijection between the two. -/ +noncomputable def conjEquiv (U : H ≃ₗᵢ[ℂ] H') (N : VonNeumannAlgebra H) : + N ≃⋆ₐ[ℂ] conj U N where + toFun x := ⟨U.conjStarAlgEquiv (x : H →L[ℂ] H), by + rw [← SetLike.mem_coe, coe_conj]; exact ⟨x, x.2, rfl⟩⟩ + invFun y := ⟨U.conjStarAlgEquiv.symm (y : H' →L[ℂ] H'), by + have hy : (y : H' →L[ℂ] H') ∈ ⇑U.conjStarAlgEquiv '' (N : Set (H →L[ℂ] H)) := by + rw [← coe_conj]; exact y.2 + obtain ⟨x, hx, hxy⟩ := hy + rw [← SetLike.mem_coe, ← hxy, StarAlgEquiv.symm_apply_apply]; exact hx⟩ + left_inv x := Subtype.ext (U.conjStarAlgEquiv.symm_apply_apply (x : H →L[ℂ] H)) + right_inv y := Subtype.ext (U.conjStarAlgEquiv.apply_symm_apply (y : H' →L[ℂ] H')) + map_mul' x y := Subtype.ext (map_mul U.conjStarAlgEquiv (x : H →L[ℂ] H) (y : H →L[ℂ] H)) + map_add' x y := Subtype.ext (map_add U.conjStarAlgEquiv (x : H →L[ℂ] H) (y : H →L[ℂ] H)) + map_smul' c x := Subtype.ext (map_smul U.conjStarAlgEquiv c (x : H →L[ℂ] H)) + map_star' x := Subtype.ext (map_star U.conjStarAlgEquiv (x : H →L[ℂ] H)) + +/-- **Definitional-equality transport as a `*`-algebra equivalence.** Two equal von Neumann +algebras have `*`-isomorphic subtypes via the identity on operators. -/ +def equivOfEq {N M : VonNeumannAlgebra H} (h : N = M) : N ≃⋆ₐ[ℂ] M where + toFun x := ⟨x, h ▸ x.2⟩ + invFun x := ⟨x, h ▸ x.2⟩ + left_inv _ := rfl + right_inv _ := rfl + map_mul' _ _ := rfl + map_add' _ _ := rfl + map_smul' _ _ := rfl + map_star' _ := rfl + +end VonNeumannAlgebra diff --git a/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/PartialTrace.lean b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/PartialTrace.lean new file mode 100644 index 0000000..2708daa --- /dev/null +++ b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/PartialTrace.lean @@ -0,0 +1,98 @@ +module + +public import Mathlib.Analysis.Complex.Basic +public import Mathlib.LinearAlgebra.Matrix.Trace + +/-! +# Partial trace of a matrix over a tensor factor + +For a matrix whose row/column indices are product types sharing one factor, the **partial +traces** over that common factor: + +* `Matrix.traceRight (M : Matrix (l × n) (c × n) R) : Matrix l c R` — trace out the **right** + factor `n`, entry-wise `traceRight M i j = ∑ k, M (i, k) (j, k)`; +* `Matrix.traceLeft (M : Matrix (n × l) (n × c) R) : Matrix l c R` — trace out the **left** + factor `n`, entry-wise `traceLeft M i j = ∑ k, M (k, i) (k, j)`. + +These are the concrete matrix-entry forms of the basis-free operator partial trace +`TensorProduct.partialTraceRight` (`QuantumSystem.ForMathlib.LinearAlgebra.Trace`). The row and +column "kept" indices `l`, `c` are allowed to differ, so the operations apply to rectangular +blocks; they are stated over an arbitrary `AddCommMonoid` so they specialise to scalars in any +finite-dimensional quantum system. + +## Main results + +* `Matrix.traceLeft_eq_traceRight_prodComm` — tracing out the left factor equals tracing out + the right factor after swapping the two factors with `Equiv.prodComm`. +-/ + +@[expose] public section + +namespace Matrix + +variable {R : Type*} [AddCommMonoid R] + +/-- **Partial trace over the right factor**: trace out the common right factor `n` of a matrix +with rows indexed by `l × n` and columns by `c × n`, leaving a matrix on `l × c`. Entry-wise +`traceRight M i j = ∑ k, M (i, k) (j, k)`. -/ +def traceRight {l c n : Type*} [Fintype n] (M : Matrix (l × n) (c × n) R) : Matrix l c R := + Matrix.of fun i j => ∑ k, M (i, k) (j, k) + +/-- **Partial trace over the left factor**: trace out the common left factor `n` of a matrix +with rows indexed by `n × l` and columns by `n × c`, leaving a matrix on `l × c`. Entry-wise +`traceLeft M i j = ∑ k, M (k, i) (k, j)`. -/ +def traceLeft {l c n : Type*} [Fintype n] (M : Matrix (n × l) (n × c) R) : Matrix l c R := + Matrix.of fun i j => ∑ k, M (k, i) (k, j) + +@[simp] lemma traceRight_apply {l c n : Type*} [Fintype n] (M : Matrix (l × n) (c × n) R) + (i : l) (j : c) : + traceRight M i j = ∑ k, M (i, k) (j, k) := rfl + +@[simp] lemma traceLeft_apply {l c n : Type*} [Fintype n] (M : Matrix (n × l) (n × c) R) + (i : l) (j : c) : + traceLeft M i j = ∑ k, M (k, i) (k, j) := rfl + +/-- The right partial trace of the zero matrix is zero. -/ +@[simp] lemma traceRight_zero {l c n : Type*} [Fintype n] : + traceRight (0 : Matrix (l × n) (c × n) ℂ) = 0 := by + ext i j; simp [traceRight_apply] + +/-- The left partial trace of the zero matrix is zero. -/ +@[simp] lemma traceLeft_zero {l c n : Type*} [Fintype n] : + traceLeft (0 : Matrix (n × l) (n × c) ℂ) = 0 := by + ext i j; simp [traceLeft_apply] + +/-- The right partial trace is `ℝ`-linear in the matrix. -/ +@[simp] lemma traceRight_smul {l n : Type*} [Fintype n] (c : ℝ) (M : Matrix (l × n) (l × n) ℂ) : + traceRight (c • M) = c • traceRight M := by + ext i j; simp only [traceRight_apply, Matrix.smul_apply]; exact Finset.smul_sum.symm + +/-- The left partial trace is `ℝ`-linear in the matrix. -/ +@[simp] lemma traceLeft_smul {l n : Type*} [Fintype n] (c : ℝ) (M : Matrix (n × l) (n × l) ℂ) : + traceLeft (c • M) = c • traceLeft M := by + ext i j; simp only [traceLeft_apply, Matrix.smul_apply]; exact Finset.smul_sum.symm + +/-- Tracing out the **left** factor equals tracing out the **right** factor after swapping the +two factors with `Equiv.prodComm`. -/ +theorem traceLeft_eq_traceRight_prodComm {l c n : Type*} [Fintype n] + (M : Matrix (n × l) (n × c) R) : + traceLeft M = traceRight (M.reindex (Equiv.prodComm n l) (Equiv.prodComm n c)) := by + ext i j + simp [Matrix.reindex_apply] + +/-- The right partial trace preserves the full trace: `Tr (traceRight M) = Tr M`. -/ +@[simp] lemma trace_traceRight {l n : Type*} [Fintype l] [Fintype n] + (M : Matrix (l × n) (l × n) R) : + (traceRight M).trace = M.trace := by + simp only [Matrix.trace, Matrix.diag_apply, traceRight_apply] + exact (Fintype.sum_prod_type fun p : l × n => M p p).symm + +/-- The left partial trace preserves the full trace: `Tr (traceLeft M) = Tr M`. -/ +@[simp] lemma trace_traceLeft {l n : Type*} [Fintype l] [Fintype n] + (M : Matrix (n × l) (n × l) R) : + (traceLeft M).trace = M.trace := by + simp only [Matrix.trace, Matrix.diag_apply, traceLeft_apply] + rw [Finset.sum_comm] + exact (Fintype.sum_prod_type fun p : n × l => M p p).symm + +end Matrix diff --git a/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/PosDef.lean b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/PosDef.lean new file mode 100644 index 0000000..672cbb2 --- /dev/null +++ b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/PosDef.lean @@ -0,0 +1,32 @@ +module + +public import Mathlib.Analysis.Complex.Basic +public import Mathlib.LinearAlgebra.Matrix.PosDef + +/-! +# Positive semidefinite matrix lemmas + +This file collects small positive-semidefinite matrix lemmas that are candidates for Mathlib. +-/ + +@[expose] public section + +namespace Matrix + +open scoped ComplexOrder + +/-- A nonnegative real multiple of a positive semidefinite matrix is positive semidefinite. -/ +lemma posSemidef_smul_nonneg {n : Type*} [Finite n] {c : ℝ} (hc : 0 ≤ c) + {M : Matrix n n ℂ} (hM : M.PosSemidef) : (c • M).PosSemidef := by + classical + have : Fintype n := Fintype.ofFinite n + suffices h' : (((c : ℝ) : ℂ) • M).PosSemidef by rwa [Complex.coe_smul] at h' + have key : ((c : ℝ) : ℂ) • M = (((Real.sqrt c : ℝ) : ℂ) • (1 : Matrix n n ℂ)) * M + * (((Real.sqrt c : ℝ) : ℂ) • (1 : Matrix n n ℂ))ᴴ := by + rw [Matrix.conjTranspose_smul, Matrix.conjTranspose_one, Complex.star_def, Complex.conj_ofReal, + smul_mul_assoc, Matrix.one_mul, smul_mul_assoc, mul_smul_comm, Matrix.mul_one, smul_smul, + ← Complex.ofReal_mul, Real.mul_self_sqrt hc] + rw [key] + exact hM.mul_mul_conjTranspose_same _ + +end Matrix diff --git a/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/StarAlgEquiv.lean b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/StarAlgEquiv.lean index d7efa2e..74cf195 100644 --- a/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/StarAlgEquiv.lean +++ b/QuantumSystem/ForMathlib/LinearAlgebra/Matrix/StarAlgEquiv.lean @@ -76,7 +76,7 @@ private lemma trace_reindex {α : Type*} [AddCommMonoid α] (e : m ≃ n) (M : M omit [DecidableEq m] [DecidableEq n] in /-- Trace is preserved when reindexing the matrix indices via a bijection. This is the `submatrix _ e e` form (vs. `reindex` form in `trace_reindex`). -/ -theorem trace_submatrix_eq {α : Type*} [AddCommMonoid α] (M : Matrix m m α) (e : n ≃ m) : +lemma trace_submatrix_eq {α : Type*} [AddCommMonoid α] (M : Matrix m m α) (e : n ≃ m) : (M.submatrix e e).trace = M.trace := by rw [show (M.submatrix e e) = reindex e.symm e.symm M from rfl] exact trace_reindex e.symm M @@ -89,7 +89,7 @@ theorem trace_reindexStarAlgEquiv [CommSemiring R] [Semiring A] [StarRing A] [Al exact trace_reindex e M /-- Trace is preserved under unitary conjugation `Unitary.conjStarAlgAut`. -/ -theorem trace_conjStarAlgAut [CommSemiring R] [CommSemiring A] [StarRing A] [Algebra R A] +lemma trace_conjStarAlgAut [CommSemiring R] [CommSemiring A] [StarRing A] [Algebra R A] (u : unitary (Matrix n n A)) (M : Matrix n n A) : (Unitary.conjStarAlgAut R (Matrix n n A) u M).trace = M.trace := by rw [Unitary.conjStarAlgAut_apply, trace_mul_cycle, @@ -152,12 +152,12 @@ theorem PosDef.map_starAlgEquiv {M : Matrix m m ℂ} (hM : M.PosDef) `Equiv`-flavoured restatements derived from `PosSemidef.map_starAlgEquiv` and `PosDef.map_starAlgEquiv`, useful when an index-set bijection `e : n ≃ m` -is naturally available (e.g. from `LocalNet` region equivalences). -/ +is naturally available (e.g. from region index-set equivalences). -/ omit [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] in /-- `Matrix.PosSemidef` is preserved by reindexing the index set via a bijection. Specialisation of `PosSemidef.map_starAlgEquiv` to `reindexStarAlgEquiv`. -/ -theorem PosSemidef.mapEquiv [Finite m] {M : Matrix m m ℂ} (hM : M.PosSemidef) (e : n ≃ m) : +lemma PosSemidef.mapEquiv [Finite m] {M : Matrix m m ℂ} (hM : M.PosSemidef) (e : n ≃ m) : (M.submatrix e e).PosSemidef := by classical letI := Fintype.ofFinite m @@ -168,7 +168,7 @@ theorem PosSemidef.mapEquiv [Finite m] {M : Matrix m m ℂ} (hM : M.PosSemidef) omit [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] in /-- `Matrix.PosDef` is preserved by reindexing the index set via a bijection. Specialisation of `PosDef.map_starAlgEquiv` to `reindexStarAlgEquiv`. -/ -theorem PosDef.mapEquiv [Finite m] {M : Matrix m m ℂ} (hM : M.PosDef) (e : n ≃ m) : +lemma PosDef.mapEquiv [Finite m] {M : Matrix m m ℂ} (hM : M.PosDef) (e : n ≃ m) : (M.submatrix e e).PosDef := by classical letI := Fintype.ofFinite m diff --git a/QuantumSystem/ForMathlib/LinearAlgebra/Trace.lean b/QuantumSystem/ForMathlib/LinearAlgebra/Trace.lean new file mode 100644 index 0000000..9de355c --- /dev/null +++ b/QuantumSystem/ForMathlib/LinearAlgebra/Trace.lean @@ -0,0 +1,320 @@ +module + +public import Mathlib.Algebra.Algebra.Subalgebra.Centralizer +public import Mathlib.Algebra.Central.End +public import Mathlib.Data.Complex.Basic +public import Mathlib.LinearAlgebra.Trace + +/-! +# Tensor product of endomorphism algebras, partial trace, and the commutant + +For finite-dimensional free modules `M`, `N` over a commutative ring `R`, the canonical algebra +homomorphism `Module.endTensorEndAlgHom : End R M ⊗ End R N →ₐ End R (M ⊗ N)` is an isomorphism. +This file packages it as an `AlgEquiv` and uses it to define the **partial trace** over the second +factor `partialTraceRight : End R (M ⊗ N) →ₗ End R M`, characterised on operator tensors by +`Y ⊗ Z ↦ (trace Z) • Y`. + +It also records the **commutant of `End A ⊗ 1`**: for finite-dimensional `ℂ`-spaces `A`, `B`, an +operator on `A ⊗ B` commuting with every ampliation `f ⊗ 1` is itself an ampliation `1 ⊗ g` of the +second factor (equivalently, the commutant of `B(A) ⊗ 1` is `1 ⊗ B(B)`). This is the +operator-algebraic uniqueness underlying the fact that a partial trace depends only on the subsystem +traced out, not on the chosen factorisation of its complement; it is assembled from Mathlib's +`Subalgebra.centralizer_coe_range_includeLeft_eq_center_tensorProduct` together with the centrality +of `End ℂ A` (`Algebra.IsCentral`), so that `Z(End ℂ A) = ℂ·1`. + +These are the abstract, basis-free ingredients of the tensor factorisation of operator algebras +on (finite-dimensional) Hilbert spaces. + +## Main statements + +* `TensorProduct.endTensorEndAlgEquiv` — the algebra isomorphism `End R M ⊗ End R N ≃ End R (M ⊗ N)`. +* `LinearMap.partialTrace` — the partial trace along an explicit decomposition `e : ℋ ≃ A ⊗ B`. +* `Algebra.TensorProduct.exists_includeRight_of_commute_includeLeft` — algebra form of the commutant: + in `End A ⊗ End B`, an element commuting with all `includeLeft f` is `includeRight g`. +* `LinearMap.exists_map_one_of_commute_map_id` — operator form: `T : End (A ⊗ B)` commuting with + all `f ⊗ 1` equals `1 ⊗ g`. +-/ + +@[expose] public section + +open scoped TensorProduct + +namespace TensorProduct + +variable {R : Type*} [CommRing R] {M N : Type*} + [AddCommGroup M] [Module R M] [Module.Finite R M] [Module.Free R M] + [AddCommGroup N] [Module R N] [Module.Finite R N] [Module.Free R N] + +/-- For finite-dimensional free modules, `Module.endTensorEndAlgHom` is an algebra isomorphism +`End R M ⊗ End R N ≃ₐ End R (M ⊗ N)`. -/ +noncomputable def endTensorEndAlgEquiv : + Module.End R M ⊗[R] Module.End R N ≃ₐ[R] Module.End R (M ⊗[R] N) := + AlgEquiv.ofBijective Module.endTensorEndAlgHom <| by + have h : (Module.endTensorEndAlgHom (R := R) (S := R) (A := R) (M := M) (N := N)).toLinearMap + = (homTensorHomEquiv R M N M N).toLinearMap := by + apply TensorProduct.ext' + intro f g + rw [AlgHom.toLinearMap_apply, Module.endTensorEndAlgHom_apply, + LinearEquiv.coe_coe, homTensorHomEquiv_apply] + apply TensorProduct.ext' + intro m n + rw [TensorProduct.AlgebraTensorModule.map_tmul, TensorProduct.homTensorHomMap_apply, + TensorProduct.map_tmul] + have hcoe : ⇑(Module.endTensorEndAlgHom (R := R) (S := R) (A := R) (M := M) (N := N)) + = ⇑(homTensorHomEquiv R M N M N) := funext fun x => LinearMap.congr_fun h x + rw [hcoe] + exact (homTensorHomEquiv R M N M N).bijective + +@[simp] +lemma endTensorEndAlgEquiv_tmul (f : Module.End R M) (g : Module.End R N) : + (endTensorEndAlgEquiv (R := R) (M := M) (N := N)) (f ⊗ₜ[R] g) = TensorProduct.map f g := by + rw [endTensorEndAlgEquiv, AlgEquiv.coe_ofBijective, Module.endTensorEndAlgHom_apply] + apply TensorProduct.ext' + intro m n + rw [TensorProduct.AlgebraTensorModule.map_tmul, TensorProduct.map_tmul] + +/-- The **partial trace over the second factor** `End R M ⊗ End R N →ₗ End R M`: trace out the +`N`-factor of an operator tensor, characterised by `Y ⊗ Z ↦ (trace Z) • Y`. This is the abstract +partial trace paired with the operator tensor decomposition `endTensorEndAlgEquiv`. -/ +noncomputable def partialTraceRight : + Module.End R M ⊗[R] Module.End R N →ₗ[R] Module.End R M := + (TensorProduct.rid R (Module.End R M)).toLinearMap ∘ₗ + TensorProduct.map LinearMap.id (LinearMap.trace R N) + +omit [Module.Finite R M] [Module.Free R M] [Module.Finite R N] [Module.Free R N] in +@[simp] +lemma partialTraceRight_tmul (Y : Module.End R M) (Z : Module.End R N) : + partialTraceRight (Y ⊗ₜ[R] Z) = (LinearMap.trace R N Z) • Y := by + rw [partialTraceRight, LinearMap.comp_apply, TensorProduct.map_tmul, LinearMap.id_apply, + LinearEquiv.coe_coe, TensorProduct.rid_tmul] + +omit [Module.Finite R M] [Module.Free R M] [Module.Finite R N] [Module.Free R N] in +/-- **Left module property** (pull-out): the partial trace pulls a left factor `Y ⊗ 1` out of the +first tensor factor, `partialTraceRight ((Y ⊗ 1) * S) = Y * partialTraceRight S`. Internal building +block for `LinearMap.partialTrace_ampliate_mul`. -/ +private lemma partialTraceRight_includeLeft_mul (Y : Module.End R M) + (S : Module.End R M ⊗[R] Module.End R N) : + partialTraceRight ((Y ⊗ₜ[R] (1 : Module.End R N)) * S) = Y * partialTraceRight S := by + induction S using TensorProduct.induction_on with + | zero => simp + | tmul Y' Z' => + rw [Algebra.TensorProduct.tmul_mul_tmul, one_mul, partialTraceRight_tmul, + partialTraceRight_tmul, mul_smul_comm] + | add S₁ S₂ h₁ h₂ => rw [mul_add, map_add, map_add, h₁, h₂, mul_add] + +end TensorProduct + +namespace LinearMap + +open scoped TensorProduct + +variable {𝕜 ℋ A B : Type*} [Field 𝕜] + [AddCommGroup ℋ] [Module 𝕜 ℋ] + [AddCommGroup A] [Module 𝕜 A] [Module.Finite 𝕜 A] [Module.Free 𝕜 A] + [AddCommGroup B] [Module 𝕜 B] [Module.Finite 𝕜 B] [Module.Free 𝕜 B] + +/-- **Partial trace along an explicit decomposition** `e : ℋ ≃ₗ A ⊗ B`: trace out the named second +factor `B` and retain `A`. This is the operator analogue of `Matrix.partialTrace (e : X ≃ A × B)` — +the subspace traced out is named by `e`, and the retained subsystem `A` appears in the codomain. -/ +noncomputable def partialTrace (e : ℋ ≃ₗ[𝕜] A ⊗[𝕜] B) : + Module.End 𝕜 ℋ →ₗ[𝕜] Module.End 𝕜 A := + TensorProduct.partialTraceRight ∘ₗ + (TensorProduct.endTensorEndAlgEquiv (R := 𝕜) (M := A) (N := B)).symm.toLinearMap ∘ₗ + (e.conjAlgEquiv 𝕜).toLinearMap + +lemma partialTrace_apply (e : ℋ ≃ₗ[𝕜] A ⊗[𝕜] B) (ρ : Module.End 𝕜 ℋ) : + partialTrace e ρ = TensorProduct.partialTraceRight + ((TensorProduct.endTensorEndAlgEquiv (R := 𝕜) (M := A) (N := B)).symm (e.conjAlgEquiv 𝕜 ρ)) := + rfl + +/-- **Ampliation along `e`**: lift an operator `M` on the retained factor `A` to `ℋ`, acting as +`M` on `A` and the identity on the traced-out factor `B` (i.e. `M ⊗ 1` transported by `e`). -/ +noncomputable def ampliate (e : ℋ ≃ₗ[𝕜] A ⊗[𝕜] B) (M : Module.End 𝕜 A) : Module.End 𝕜 ℋ := + (e.conjAlgEquiv 𝕜).symm (TensorProduct.map M 1) + +/-- **Co-ampliation along `e`**: lift an operator `M` on the *complementary* factor `B` to `ℋ`, +acting as the identity on `A` and as `M` on `B` (i.e. `1 ⊗ M` transported by `e`). -/ +noncomputable def coampliate (e : ℋ ≃ₗ[𝕜] A ⊗[𝕜] B) (M : Module.End 𝕜 B) : Module.End 𝕜 ℋ := + (e.conjAlgEquiv 𝕜).symm (TensorProduct.map 1 M) + +private theorem endTensorEndAlgEquiv_symm_map_one (M : Module.End 𝕜 A) : + (TensorProduct.endTensorEndAlgEquiv (R := 𝕜) (M := A) (N := B)).symm + (TensorProduct.map M 1) = M ⊗ₜ[𝕜] 1 := by + rw [← TensorProduct.endTensorEndAlgEquiv_tmul] + exact (TensorProduct.endTensorEndAlgEquiv (R := 𝕜) (M := A) (N := B)).symm_apply_apply _ + +/-- **`Tr_B((M⊗1)·ρ) = M·Tr_B(ρ)`**: the partial trace along `e` is a left module map over the +ampliation, with `B` (the traced-out subspace) named explicitly by `e`. -/ +lemma partialTrace_ampliate_mul (e : ℋ ≃ₗ[𝕜] A ⊗[𝕜] B) (M : Module.End 𝕜 A) + (ρ : Module.End 𝕜 ℋ) : + partialTrace e (ampliate e M * ρ) = M * partialTrace e ρ := by + rw [partialTrace_apply, partialTrace_apply, ampliate, map_mul (e.conjAlgEquiv 𝕜), + AlgEquiv.apply_symm_apply, + map_mul (TensorProduct.endTensorEndAlgEquiv (R := 𝕜) (M := A) (N := B)).symm, + endTensorEndAlgEquiv_symm_map_one, TensorProduct.partialTraceRight_includeLeft_mul] + +end LinearMap + +/-! ### The commutant of `End A ⊗ 1` in `End (A ⊗ B)` -/ + +namespace Algebra.TensorProduct + +variable {A B : Type*} [AddCommGroup A] [Module ℂ A] [Module.Finite ℂ A] [Module.Free ℂ A] + [AddCommGroup B] [Module ℂ B] [Module.Finite ℂ B] [Module.Free ℂ B] + +omit [Module.Finite ℂ A] [Module.Finite ℂ B] [Module.Free ℂ B] in +/-- **Commutant of `End A ⊗ 1`, algebra form.** An element of `End ℂ A ⊗ End ℂ B` that commutes +with every `includeLeft f = f ⊗ 1` is of the form `includeRight g = 1 ⊗ g`. -/ +theorem exists_includeRight_of_commute_includeLeft + (S : Module.End ℂ A ⊗[ℂ] Module.End ℂ B) + (hS : ∀ f : Module.End ℂ A, + (Algebra.TensorProduct.includeLeft (R := ℂ) (S := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) f) * S + = S * Algebra.TensorProduct.includeLeft (R := ℂ) (S := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) f) : + ∃ g, S = Algebra.TensorProduct.includeRight g := by + -- Every element of `(⊥ : Subalgebra) ⊗ End B` is `1 ⊗ g`. + have hext : ∀ x : (⊥ : Subalgebra ℂ (Module.End ℂ A)) ⊗[ℂ] Module.End ℂ B, + ∃ g, Algebra.TensorProduct.map (⊥ : Subalgebra ℂ (Module.End ℂ A)).val + (AlgHom.id ℂ (Module.End ℂ B)) x = Algebra.TensorProduct.includeRight g := by + intro x + induction x with + | zero => exact ⟨0, by simp⟩ + | tmul a' g => + obtain ⟨c, hc⟩ := Algebra.mem_bot.mp a'.2 + refine ⟨c • g, ?_⟩ + simp only [Algebra.TensorProduct.map_tmul, AlgHom.id_apply, + Algebra.TensorProduct.includeRight_apply] + rw [show (Subalgebra.val ⊥) a' = algebraMap ℂ (Module.End ℂ A) c from hc.symm, + Algebra.algebraMap_eq_smul_one, TensorProduct.smul_tmul] + | add x y hx hy => + obtain ⟨g₁, h₁⟩ := hx; obtain ⟨g₂, h₂⟩ := hy + exact ⟨g₁ + g₂, by rw [map_add, h₁, h₂, ← map_add]⟩ + -- `S` lies in the centralizer of `range includeLeft`, which (by centrality of `End A`) is + -- `range includeRight`. + have hmem : S ∈ Subalgebra.centralizer ℂ + (↑(Algebra.TensorProduct.includeLeft (R := ℂ) (S := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B)).range : Set _) := by + rw [Subalgebra.mem_centralizer_iff] + rintro s ⟨f, rfl⟩ + exact hS f + rw [Subalgebra.centralizer_coe_range_includeLeft_eq_center_tensorProduct, + Algebra.IsCentral.center_eq_bot, AlgHom.mem_range] at hmem + obtain ⟨x, hx⟩ := hmem + obtain ⟨g, hg⟩ := hext x + exact ⟨g, by rw [← hx, hg]⟩ + +omit [Module.Finite ℂ A] [Module.Free ℂ A] [Module.Finite ℂ B] [Module.Free ℂ B] in +/-- **Commutant of `1 ⊗ End B`, algebra form.** An element of `End ℂ A ⊗ End ℂ B` that commutes +with every `includeRight g = 1 ⊗ g` is of the form `includeLeft f = f ⊗ 1`. -/ +lemma exists_includeLeft_of_commute_includeRight + (S : Module.End ℂ A ⊗[ℂ] Module.End ℂ B) + (hS : ∀ g : Module.End ℂ B, + (Algebra.TensorProduct.includeRight (R := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) g) * S + = S * Algebra.TensorProduct.includeRight (R := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) g) : + ∃ f, S = Algebra.TensorProduct.includeLeft (R := ℂ) (S := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) f := by + have hext : ∀ x : Module.End ℂ A ⊗[ℂ] (⊥ : Subalgebra ℂ (Module.End ℂ B)), + ∃ f, Algebra.TensorProduct.map (AlgHom.id ℂ (Module.End ℂ A)) + (⊥ : Subalgebra ℂ (Module.End ℂ B)).val x + = Algebra.TensorProduct.includeLeft (R := ℂ) (S := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) f := by + intro x + induction x with + | zero => exact ⟨0, by simp⟩ + | tmul a b' => + obtain ⟨c, hc⟩ := Algebra.mem_bot.mp b'.2 + refine ⟨c • a, ?_⟩ + simp only [Algebra.TensorProduct.map_tmul, AlgHom.id_apply, + Algebra.TensorProduct.includeLeft_apply] + rw [show (Subalgebra.val ⊥) b' = algebraMap ℂ (Module.End ℂ B) c from hc.symm, + Algebra.algebraMap_eq_smul_one, TensorProduct.tmul_smul, TensorProduct.smul_tmul'] + | add x y hx hy => + obtain ⟨f₁, h₁⟩ := hx; obtain ⟨f₂, h₂⟩ := hy + exact ⟨f₁ + f₂, by rw [map_add, h₁, h₂, ← map_add]⟩ + have hmem : S ∈ Subalgebra.centralizer ℂ + (↑(Algebra.TensorProduct.includeRight (R := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B)).range : Set _) := by + rw [Subalgebra.mem_centralizer_iff] + rintro s ⟨g, rfl⟩ + exact hS g + rw [Subalgebra.centralizer_range_includeRight_eq_center_tensorProduct, + Algebra.IsCentral.center_eq_bot, AlgHom.mem_range] at hmem + obtain ⟨x, hx⟩ := hmem + obtain ⟨f, hf⟩ := hext x + exact ⟨f, by rw [← hx, hf]⟩ + +end Algebra.TensorProduct + +namespace LinearMap + +variable {A B : Type*} [AddCommGroup A] [Module ℂ A] [Module.Finite ℂ A] [Module.Free ℂ A] + [AddCommGroup B] [Module ℂ B] [Module.Finite ℂ B] [Module.Free ℂ B] + +/-- **Commutant of `End A ⊗ 1`, operator form.** An operator `T : End (A ⊗ B)` commuting with every +ampliation `f ⊗ 1 = TensorProduct.map f 1` is an ampliation `1 ⊗ g = TensorProduct.map 1 g` of the +second factor. -/ +theorem exists_map_one_of_commute_map_id + (T : Module.End ℂ (A ⊗[ℂ] B)) + (hT : ∀ f : Module.End ℂ A, + T ∘ₗ TensorProduct.map f LinearMap.id = TensorProduct.map f LinearMap.id ∘ₗ T) : + ∃ g : Module.End ℂ B, T = TensorProduct.map LinearMap.id g := by + have hcomm : ∀ f : Module.End ℂ A, + Algebra.TensorProduct.includeLeft (R := ℂ) (S := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) f * TensorProduct.endTensorEndAlgEquiv.symm T + = TensorProduct.endTensorEndAlgEquiv.symm T + * Algebra.TensorProduct.includeLeft (R := ℂ) (S := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) f := by + intro f + have key : TensorProduct.endTensorEndAlgEquiv (Algebra.TensorProduct.includeLeft + (R := ℂ) (S := ℂ) (A := Module.End ℂ A) (B := Module.End ℂ B) f) + = TensorProduct.map f (LinearMap.id : Module.End ℂ B) := by + rw [Algebra.TensorProduct.includeLeft_apply, TensorProduct.endTensorEndAlgEquiv_tmul, + Module.End.one_eq_id] + apply TensorProduct.endTensorEndAlgEquiv.injective + rw [map_mul, map_mul, AlgEquiv.apply_symm_apply, key] + exact (hT f).symm + obtain ⟨g, hg⟩ := + Algebra.TensorProduct.exists_includeRight_of_commute_includeLeft + (TensorProduct.endTensorEndAlgEquiv.symm T) hcomm + refine ⟨g, ?_⟩ + have heq := congrArg TensorProduct.endTensorEndAlgEquiv hg + rw [AlgEquiv.apply_symm_apply, Algebra.TensorProduct.includeRight_apply, + TensorProduct.endTensorEndAlgEquiv_tmul] at heq + exact heq + +/-- **Commutant of `1 ⊗ End B`, operator form.** An operator `T : End (A ⊗ B)` commuting with every +`1 ⊗ g = TensorProduct.map 1 g` is an ampliation `f ⊗ 1 = TensorProduct.map f 1` of the first +factor. -/ +lemma exists_map_id_of_commute_map_one + (T : Module.End ℂ (A ⊗[ℂ] B)) + (hT : ∀ g : Module.End ℂ B, + T ∘ₗ TensorProduct.map LinearMap.id g = TensorProduct.map LinearMap.id g ∘ₗ T) : + ∃ f : Module.End ℂ A, T = TensorProduct.map f LinearMap.id := by + have hcomm : ∀ g : Module.End ℂ B, + Algebra.TensorProduct.includeRight (R := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) g * TensorProduct.endTensorEndAlgEquiv.symm T + = TensorProduct.endTensorEndAlgEquiv.symm T + * Algebra.TensorProduct.includeRight (R := ℂ) (A := Module.End ℂ A) + (B := Module.End ℂ B) g := by + intro g + have key : TensorProduct.endTensorEndAlgEquiv (Algebra.TensorProduct.includeRight + (R := ℂ) (A := Module.End ℂ A) (B := Module.End ℂ B) g) + = TensorProduct.map (LinearMap.id : Module.End ℂ A) g := by + rw [Algebra.TensorProduct.includeRight_apply, TensorProduct.endTensorEndAlgEquiv_tmul, + Module.End.one_eq_id] + apply TensorProduct.endTensorEndAlgEquiv.injective + rw [map_mul, map_mul, AlgEquiv.apply_symm_apply, key] + exact (hT g).symm + obtain ⟨f, hf⟩ := + Algebra.TensorProduct.exists_includeLeft_of_commute_includeRight + (TensorProduct.endTensorEndAlgEquiv.symm T) hcomm + refine ⟨f, ?_⟩ + have heq := congrArg TensorProduct.endTensorEndAlgEquiv hf + rw [AlgEquiv.apply_symm_apply, Algebra.TensorProduct.includeLeft_apply, + TensorProduct.endTensorEndAlgEquiv_tmul, Module.End.one_eq_id] at heq + exact heq + +end LinearMap diff --git a/QuantumSystem/ForMathlib/Topology/Algebra/CStarCompletion.lean b/QuantumSystem/ForMathlib/Topology/Algebra/CStarCompletion.lean new file mode 100644 index 0000000..c0bc9c2 --- /dev/null +++ b/QuantumSystem/ForMathlib/Topology/Algebra/CStarCompletion.lean @@ -0,0 +1,179 @@ +module + +public import Mathlib.Analysis.CStarAlgebra.Classes +public import Mathlib.Analysis.Normed.Module.Completion + +/-! +# C⋆-algebra structure on a completion + +The completion of a normed `*`-algebra is again a normed `*`-algebra, and the completion of a +(possibly incomplete) C⋆-normed algebra is a `CStarAlgebra`. Mathlib already provides the +`NormedRing`, `NormedAlgebra` and `CompleteSpace` instances on `UniformSpace.Completion`; this +file adds the missing `Star`, `StarRing`, `NormedStarGroup`, `CStarRing` and `StarModule` +instances, obtained by extending the operations on the dense image by continuity, and assembles +them into a `CStarAlgebra` instance. It also records the **functoriality** of the completion for +`*`-algebra equivalences (`mapStarAlgEquiv`): a uniformly continuous `*`-isomorphism with uniformly +continuous inverse extends to a `*`-isomorphism of the completions. + +These are general facts about completions and are candidates for upstreaming to Mathlib. +-/ + +@[expose] public section + +namespace UniformSpace.Completion + +variable {A : Type*} + +section StarRing + +variable [NormedRing A] [StarRing A] [NormedStarGroup A] + +lemma uniformContinuous_star : UniformContinuous (star : A → A) := by + have h : Isometry (star : A → A) := + AddMonoidHomClass.isometry_of_norm (starAddEquiv (R := A)) fun x => norm_star x + exact h.uniformContinuous + +/-- Involution on a completion, the continuous extension of the involution on the dense image. -/ +noncomputable instance : Star (Completion A) := ⟨Completion.map star⟩ + +@[simp] lemma star_coe (a : A) : star (↑a : Completion A) = (↑(star a) : Completion A) := + map_coe uniformContinuous_star a + +instance : ContinuousStar (Completion A) := ⟨continuous_map⟩ + +noncomputable instance : InvolutiveStar (Completion A) where + star_involutive a := by + refine induction_on a (isClosed_eq (continuous_id.star.star) continuous_id) ?_ + intro a + rw [star_coe, star_coe, star_star] + +noncomputable instance : StarMul (Completion A) where + star_mul a b := by + refine induction_on₂ a b + (isClosed_eq continuous_mul.star ((continuous_snd.star).mul (continuous_fst.star))) ?_ + intro a b + rw [← coe_mul, star_coe, star_coe, star_coe, ← coe_mul, star_mul] + +noncomputable instance : StarRing (Completion A) where + star_add a b := by + refine induction_on₂ a b + (isClosed_eq continuous_add.star ((continuous_fst.star).add (continuous_snd.star))) ?_ + intro a b + rw [← coe_add, star_coe, star_coe, star_coe, ← coe_add, star_add] + +instance : NormedStarGroup (Completion A) where + norm_star_le a := by + refine induction_on a (isClosed_le continuous_id.star.norm continuous_norm) ?_ + intro a + rw [star_coe, norm_coe, norm_coe] + exact (norm_star a).le + +end StarRing + +section CStarRing + +variable [NormedRing A] [StarRing A] [NormedStarGroup A] [CStarRing A] + +instance : CStarRing (Completion A) where + norm_mul_self_le a := by + refine induction_on a + (isClosed_le (continuous_norm.mul continuous_norm) + (continuous_id.star.mul continuous_id).norm) ?_ + intro a + rw [star_coe, ← coe_mul, norm_coe, norm_coe] + exact CStarRing.norm_mul_self_le a + +end CStarRing + +section StarModule + +variable {𝕜 : Type*} [NormedField 𝕜] [StarRing 𝕜] [NormedRing A] [StarRing A] + [NormedStarGroup A] [NormedAlgebra 𝕜 A] [StarModule 𝕜 A] + +noncomputable instance : StarModule 𝕜 (Completion A) where + star_smul c a := by + refine induction_on a + (isClosed_eq (continuous_const_smul c).star (continuous_id.star.const_smul (star c))) ?_ + intro a + rw [← coe_smul, star_coe, star_coe, ← coe_smul, star_smul] + +end StarModule + +/-- The completion of a normed algebra over a normed field is a normed algebra. (Mathlib only + provides this for commutative base rings; here `A` may be non-commutative.) -/ +noncomputable instance instNormedAlgebraOfNormedRing {𝕜 : Type*} [NormedField 𝕜] [NormedRing A] + [NormedAlgebra 𝕜 A] : NormedAlgebra 𝕜 (Completion A) := + { Completion.algebra A 𝕜 with + norm_smul_le := fun c x => by + refine induction_on x (isClosed_le ?_ ?_) ?_ + · exact (continuous_const_smul c).norm + · exact continuous_const.mul continuous_norm + · intro a + rw [← coe_smul, norm_coe, norm_coe] + exact norm_smul_le c a } + +/-- The completion of a (possibly incomplete) C⋆-normed `ℂ`-algebra is a `CStarAlgebra`. -/ +noncomputable instance instCStarAlgebra [NormedRing A] [StarRing A] [NormedStarGroup A] + [CStarRing A] [NormedAlgebra ℂ A] [StarModule ℂ A] : CStarAlgebra (Completion A) where + +section MapStarAlgEquiv + +variable {𝕜 A B : Type*} [NormedField 𝕜] + [NormedRing A] [NormedAlgebra 𝕜 A] [StarRing A] [NormedStarGroup A] + [NormedRing B] [NormedAlgebra 𝕜 B] [StarRing B] [NormedStarGroup B] + +/-- **Functoriality of the completion for star algebra equivalences**: a uniformly continuous +`*`-algebra equivalence whose inverse is also uniformly continuous extends, by continuity on the +dense image, to a `*`-algebra equivalence of the completions. (In particular every `*`-isomorphism +of C⋆-normed algebras, being isometric, extends to the completions.) -/ +noncomputable def mapStarAlgEquiv (e : A ≃⋆ₐ[𝕜] B) (he : UniformContinuous e) + (he' : UniformContinuous e.symm) : Completion A ≃⋆ₐ[𝕜] Completion B where + toFun := Completion.map e + invFun := Completion.map e.symm + left_inv x := by + refine induction_on x (isClosed_eq (continuous_map.comp continuous_map) continuous_id) ?_ + intro a + rw [map_coe he, map_coe he', StarAlgEquiv.symm_apply_apply] + right_inv x := by + refine induction_on x (isClosed_eq (continuous_map.comp continuous_map) continuous_id) ?_ + intro a + rw [map_coe he', map_coe he, StarAlgEquiv.apply_symm_apply] + map_mul' x y := by + refine induction_on₂ x y + (isClosed_eq ((continuous_map (f := ⇑e)).comp continuous_mul) + (((continuous_map (f := ⇑e)).comp continuous_fst).mul + ((continuous_map (f := ⇑e)).comp continuous_snd))) ?_ + intro a b + rw [← coe_mul, map_coe he, map_coe he, map_coe he, ← coe_mul, map_mul] + map_add' x y := by + refine induction_on₂ x y + (isClosed_eq ((continuous_map (f := ⇑e)).comp continuous_add) + (((continuous_map (f := ⇑e)).comp continuous_fst).add + ((continuous_map (f := ⇑e)).comp continuous_snd))) ?_ + intro a b + rw [← coe_add, map_coe he, map_coe he, map_coe he, ← coe_add, map_add] + map_smul' c x := by + refine induction_on x + (isClosed_eq (continuous_map.comp (continuous_const_smul c)) + ((continuous_const_smul c).comp continuous_map)) ?_ + intro a + rw [← coe_smul, map_coe he, map_coe he, ← coe_smul, map_smul] + map_star' x := by + refine induction_on x + (isClosed_eq (continuous_map.comp continuous_star) (continuous_star.comp continuous_map)) ?_ + intro a + rw [star_coe, map_coe he, map_coe he, star_coe, map_star] + +@[simp] lemma mapStarAlgEquiv_coe (e : A ≃⋆ₐ[𝕜] B) (he : UniformContinuous e) + (he' : UniformContinuous e.symm) (a : A) : + mapStarAlgEquiv e he he' (↑a : Completion A) = (↑(e a) : Completion B) := + map_coe he a + +lemma coe_mapStarAlgEquiv (e : A ≃⋆ₐ[𝕜] B) (he : UniformContinuous e) + (he' : UniformContinuous e.symm) : + ⇑(mapStarAlgEquiv e he he') = Completion.map e := + rfl + +end MapStarAlgEquiv + +end UniformSpace.Completion diff --git a/QuantumSystem/Notation.lean b/QuantumSystem/Notation.lean index 5d8d090..7ceaa7c 100644 --- a/QuantumSystem/Notation.lean +++ b/QuantumSystem/Notation.lean @@ -10,19 +10,22 @@ public import Mathlib.LinearAlgebra.Matrix.Trace Notations and abbreviations for quantum information theory. -| Symbol | Expansion | How to activate | Defined in | -|---|---|---|---| -| `Tr A` | `Matrix.trace A` | always available (prefix notation) | this file | -| `log ρ` | `DensityMatrix.log ρ` | `open scoped Matrix.QuantumInfo` | `State.lean` | -| `S(ρ)` | `Matrix.vonNeumannEntropy ρ` | `open scoped Matrix.QuantumInfo` | `Analysis/Entropy/VonNeumannEntropy.lean` | -| `D(ρ ∥ σ)` | `Matrix.relativeEntropy ρ σ` | `open scoped Matrix.QuantumInfo` | `Analysis/Entropy/RelativeEntropy.lean` | -| `⟪X, Y⟫_HS` | `Matrix.hsInnerProduct X Y` | `open scoped Matrix.QuantumInfo` | `Analysis/Matrix/LiebConcavity.lean` | -| `ρ ↾ Λ` | `DensityMatrix.restrict (by …) ρ` | `open scoped LocalNet.QuantumInfo` | `Algebra/LocalNet.lean` | - -`ρ ↾ Λ` is the AQFT-style **restriction of a density matrix to a sub-region** — -equivalently, the partial trace over the complementary region. -The subset proof `Λ ⊆ Λ_total` is auto-resolved by `Finset.subset_univ _`, `Finset.Subset.refl _`, or `decide`. -For complex hypotheses, write `DensityMatrix.restrict h ρ` directly. +<table> +<tr><th>Symbol</th><th>Expansion</th><th>How to activate</th><th>Defined in</th></tr> +<tr><td><code>Tr A</code></td><td><code>Matrix.trace A</code></td> + <td>always available (prefix notation)</td><td>this file</td></tr> +<tr><td><code>log ρ</code></td><td><code>DensityMatrix.log ρ</code></td> + <td><code>open scoped Matrix.QuantumInfo</code></td><td><code>State.lean</code></td></tr> +<tr><td><code>S(ρ)</code></td><td><code>Matrix.vonNeumannEntropy ρ</code></td> + <td><code>open scoped Matrix.QuantumInfo</code></td> + <td><code>Analysis/Entropy/VonNeumannEntropy.lean</code></td></tr> +<tr><td><code>D(ρ ∥ σ)</code></td><td><code>Matrix.relativeEntropy ρ σ</code></td> + <td><code>open scoped Matrix.QuantumInfo</code></td> + <td><code>Analysis/Entropy/RelativeEntropy.lean</code></td></tr> +<tr><td><code>⟪X, Y⟫_HS</code></td><td><code>Matrix.hsInnerProduct X Y</code></td> + <td><code>open scoped Matrix.QuantumInfo</code></td> + <td><code>Analysis/Matrix/LiebConcavity.lean</code></td></tr> +</table> ## `Tr` syntax diff --git a/QuantumSystem/State.lean b/QuantumSystem/State.lean index 5a34182..289359e 100644 --- a/QuantumSystem/State.lean +++ b/QuantumSystem/State.lean @@ -58,14 +58,14 @@ namespace DensityMatrix variable {n : Type*} [Fintype n] [DecidableEq n] /-- Two density matrices are equal iff their underlying matrices are equal. -/ -@[ext] theorem ext {ρ σ : DensityMatrix n} (h : ρ.toMatrix = σ.toMatrix) : ρ = σ := by +@[ext] lemma ext {ρ σ : DensityMatrix n} (h : ρ.toMatrix = σ.toMatrix) : ρ = σ := by cases ρ; cases σ; congr /-- A density matrix is Hermitian. -/ -theorem isHermitian (ρ : DensityMatrix n) : ρ.toMatrix.IsHermitian := ρ.posSemidef.1 +lemma isHermitian (ρ : DensityMatrix n) : ρ.toMatrix.IsHermitian := ρ.posSemidef.1 /-- All eigenvalues of a density matrix are non-negative. -/ -theorem eigenvalues_nonneg (ρ : DensityMatrix n) (i : n) : +lemma eigenvalues_nonneg (ρ : DensityMatrix n) (i : n) : 0 ≤ ρ.isHermitian.eigenvalues i := (ρ.isHermitian.posSemidef_iff_eigenvalues_nonneg.mp ρ.posSemidef) i @@ -96,34 +96,34 @@ lemma eigenvalue_le_one (ρ : DensityMatrix n) (i : n) : instance : Coe (DensityMatrix n) (Matrix n n ℂ) where coe := DensityMatrix.toMatrix -@[simp] theorem coe_eq_toMatrix (ρ : DensityMatrix n) : (↑ρ : Matrix n n ℂ) = ρ.toMatrix := rfl +@[simp] lemma coe_eq_toMatrix (ρ : DensityMatrix n) : (↑ρ : Matrix n n ℂ) = ρ.toMatrix := rfl /-- Density matrix times a complex matrix (coercion on the left). -/ noncomputable instance : HMul (DensityMatrix n) (Matrix n n ℂ) (Matrix n n ℂ) where hMul ρ A := ρ.toMatrix * A -@[simp] theorem densityMatrix_hmul_eq (ρ : DensityMatrix n) (A : Matrix n n ℂ) : +@[simp] lemma densityMatrix_hmul_eq (ρ : DensityMatrix n) (A : Matrix n n ℂ) : ρ * A = ρ.toMatrix * A := rfl /-- Real-power of a density matrix, delegated to matrix rpow. -/ noncomputable instance : HPow (DensityMatrix n) ℝ (Matrix n n ℂ) where hPow ρ s := ρ.toMatrix ^ s -theorem densityMatrix_hpow_eq (ρ : DensityMatrix n) (s : ℝ) : +lemma densityMatrix_hpow_eq (ρ : DensityMatrix n) (s : ℝ) : ρ ^ s = ρ.toMatrix ^ s := rfl -/-- Matrix logarithm of a density matrix: `log ρ = U diag(log λᵢ) U*`. - Computed via the spectral decomposition of `ρ`. -/ +/-- Matrix logarithm of a density matrix: `log ρ = U diag(log λᵢ) U*`, +Mathlib's continuous functional calculus applied to `Real.log`. -/ noncomputable def log (ρ : DensityMatrix n) : Matrix n n ℂ := - matrixLog ↑ρ ρ.isHermitian + cfc Real.log ↑ρ /-- The product `ρ * log ρ` is Hermitian. Both factors are Hermitian and commute because `log ρ` is a matrix function of `ρ`. -/ lemma mul_log_isHermitian (ρ : DensityMatrix n) : (ρ.toMatrix * log ρ).IsHermitian := by - simpa [DensityMatrix.log] using - (mul_matrixFunction_isHermitian ρ.isHermitian Real.log) + unfold log + exact mul_cfc_isHermitian ρ.isHermitian Real.log /-- Convex combination of two density matrices is a density matrix. -/ noncomputable def mix (ρ₁ ρ₂ : DensityMatrix n) @@ -152,7 +152,7 @@ noncomputable def mix (ρ₁ ρ₂ : DensityMatrix n) push_cast ring -@[simp] theorem mix_toMatrix (ρ₁ ρ₂ : DensityMatrix n) +@[simp] lemma mix_toMatrix (ρ₁ ρ₂ : DensityMatrix n) (p : ℝ) (hp : 0 ≤ p) (hp1 : p ≤ 1) : ↑(mix ρ₁ ρ₂ p hp hp1) = p • (↑ρ₁ : Matrix n n ℂ) + (1 - p) • ↑ρ₂ := rfl @@ -326,7 +326,7 @@ theorem regularize_eq_cfc (ρ : DensityMatrix n) {ε : ℝ} The regularization commutes with `mapEquiv`. -/ /-- For an `Equiv e : n ≃ m`, the regularization commutes with `mapEquiv`. -/ -theorem regularize_mapEquiv {m : Type*} [Fintype m] [DecidableEq m] [Nonempty m] +lemma regularize_mapEquiv {m : Type*} [Fintype m] [DecidableEq m] [Nonempty m] (ρ : DensityMatrix m) (e : n ≃ m) {ε : ℝ} (hε : 0 ≤ ε) (hε' : ε ≤ 1) : regularize (mapEquiv ρ e) hε hε' = mapEquiv (regularize ρ hε hε') e := by apply DensityMatrix.ext diff --git a/docs/math/README.md b/docs/math/README.md new file mode 100644 index 0000000..bb2b5e3 --- /dev/null +++ b/docs/math/README.md @@ -0,0 +1,40 @@ +# Extraction notes + +One note per mathematical object, recording what the literature says about it — +the definitional variants and the conventions behind them, the results and their +dependencies, each hypothesis sorted into provable / model-dependent / open, the +degenerate cases, and the formulations that were rejected together with the +object that rules each one out. + +These notes are written **before** the corresponding Lean, by the `math-extract` +skill. They contain mathematics only: no Lean types, no declaration names, no +docstring drafts. How the object is eventually formalized is decided when it is +formalized, and a note that guessed in advance would only add noise. + +They are read twice afterwards: when the object is designed (AGENTS.md *Think +before coding*), and by `/math-review`, which compares the elaborated Lean back +against the note's adopted general form, hypothesis classification and +degeneracy table — and which maintains the `Implemented as` column below. + +The format is specified in +`.claude/skills/math-extract/references/note-format.md`. The retrieval history +and locator adjudications for the sources these notes cite live in +`.claude/skills/math-extract/sources.md`. Fetched source texts are cached under +`references/`, which is gitignored — the cache is navigation, these notes are +the product. + +## Index + +| Object | Note | Implemented as | +|---|---|---| +| Causal index set of a Haag–Kastler net | [causal-index-set.md](causal-index-set.md) | `CausalIndexSet` | +| Split inclusion of von Neumann algebras | [split-inclusion.md](split-inclusion.md) | `VonNeumannAlgebra.IsSplitInclusion` | +| Faithful representation of a separable C\*-algebra on a separable Hilbert space | [separable-faithful-representation.md](separable-faithful-representation.md) | `CStarRep.exists_isometric_separable` | +| Von Neumann bicommutant theorem | [bicommutant-theorem.md](bicommutant-theorem.md) | `DoubleCommutant.bicommutant_tfae` | +| Umegaki relative entropy | [umegaki-relative-entropy.md](umegaki-relative-entropy.md) | none | + +<!-- One row per note. "Implemented as" is a fact recorded after the object is + formalized — the fully-qualified declaration that ended up carrying it, or + "none" while it is still only a note. It is a back-link, not a plan. + /math-review writes it, and resets it to "none" when the declaration is + gone; it must agree with the note's own `implemented-as:` frontmatter. --> diff --git a/docs/math/bicommutant-theorem.md b/docs/math/bicommutant-theorem.md new file mode 100644 index 0000000..4fb834e --- /dev/null +++ b/docs/math/bicommutant-theorem.md @@ -0,0 +1,718 @@ +--- +object: Von Neumann bicommutant theorem +slug: bicommutant-theorem +status: draft +worst-tier: b +mathlib-rev: 5450b53e5ddc75d46418fabb605edbf36bd0beb6 +implemented-as: DoubleCommutant.bicommutant_tfae +revisions: + - 2026-08-16 · 9b1810d · initial extraction · sources: SHI12, LAN98, HIA20, KOS13, SOR23, NAA13, HM06 + - 2026-08-16 · (working tree) · back-link set — the adopted general form (non-unital, non-degenerate) is now stated by `SOTClosedSubalgebra.doubleCommutant_eq_of_isSOTClosed` / `WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed` + - 2026-08-16 · (working tree) · back-link moved to `DoubleCommutant.bicommutant_tfae`, which states the adopted general form in full (the three-way equivalence, non-unital and non-degenerate); the density form (R2) is `SOTClosedSubalgebra.sotClosure_eq_doubleCommutant` / `wotClosure_eq_doubleCommutant` + - 2026-08-16 · (working tree) · (A2) — that $1 \in M$ is a conclusion, not a hypothesis — is now `WOTClosedSubalgebra.one_mem_of_isWOTClosed` / `DoubleCommutant.one_mem_of_isSOTClosed`, which is also the sharpened content of (X6); (X4)'s $F(H)' = \mathbb{C}1$ and the $F(H)'' = B(H)$ it yields are `InnerProductSpace.centralizer_finiteRankOperators` / `centralizer_centralizer_finiteRankOperators`, and the negative instance itself is `DoubleCommutant.not_isWOTClosed_finiteRankOperators` / `not_isSOTClosed_finiteRankOperators` / `centralizer_centralizer_ne_finiteRankOperators` +--- + +<!-- +Macros the quotes below need, copied from each source's own preamble: + \ra \c \s \cs \ss \sub \lan \ran \la \ep SHI12, references/arxiv-1211.3404/raw/main.tex:46,52,62,65,66,70,72,73,96,98 + \ll \ca \Hs \sta \vNa \raw \n \BH \dl \ep \lm \Ph \Ps \M \GN \H \CO \C \I \su + LAN98, references/arxiv-math-ph-9807030/raw/main.tex:34,53,58,62,66,78,86,113,211,213,221,228,232,246,247,269,276,288,291,28 + \cH \cA \Proj HIA20, references/arxiv-2004.02383/raw/main.tex:32,51,62 + \comps \A \H \B SOR23, references/arxiv-2302.01958/raw/type_classification.tex:26,30,32,38 + \idx \alg \mc NAA13, references/arxiv-1311.2717/raw/qlattice.tex:21,66,73 + \alg \norm \bh \2 \7 \al HM06, references/arxiv-math-ph-0602036/raw/reconstruction.tex:26,27,28,136,138,143 + KOS13's macros (\df \comm \N \C \BH \CC \II \zentr \limp \rpktarget) are defined + in the author's private packages rpk.sty and motta.sty, which are NOT in the + arXiv package (\usepackage lines at raw/wstarint-arxiv.tex:35-36). They cannot + be transcribed, and no KOS13 quote below can be rendered from this note; see + the tier note on (D9)/(D10). + \H and \M are defined by more than one source below; LAN98's and SOR23's \H + agree up to the spelling of the calligraphic alphabet, and only LAN98 uses \M + inside a quote, so a single definition of each is faithful here. + + The bodies below are the sources' own; only the defining primitive is this + note's, and it is forced by the renderer. KaTeX scopes \newcommand to the + expression it appears in, so a preamble written with \newcommand defines + nothing for the rest of the document; \gdef is the form that persists. \gdef + also overwrites silently, which is what \c, \ss, \sub and \H need — KaTeX + predefines all four (cedilla, sharp s, \subset, Hungarian umlaut) and rejects + \newcommand on them outright, aborting the whole block and leaving every + later macro undefined. That was the parse error this preamble first produced. +--> + +$$ +\gdef\ra{{\rightarrow}} +\gdef\c{{\mathbb{C}}} +\gdef\s{{^{\ast}}} +\gdef\cs{{C^{\ast}\text{-}}} +\gdef\ss{{\ast}} +\gdef\sub{{\subseteq }} +\gdef\lan{{\langle}} +\gdef\ran{{\rangle}} +\gdef\la{{\lambda}} +\gdef\ep{{\varepsilon}} +\gdef\sta{{\ast\text{-algebra}}} +\gdef\ca{{C^{\ast}\text{-algebra}}} +\gdef\Hs{{\text{Hilbert space}}} +\gdef\vNa{{\text{von Neumann algebra}}} +\gdef\raw{\rightarrow} +\gdef\BH{{\frak B}({\cal H})} +\gdef\dl{\delta} +\gdef\lm{\lambda} +\gdef\Ph{\Phi} +\gdef\Ps{\Psi} +\gdef\M{{\frak M}} +\gdef\GN{{\frak N}} +\gdef\H{{\cal H}} +\gdef\C{{\Bbb C}} +\gdef\I{{\Bbb I}} +\gdef\cH{\mathcal{H}} +\gdef\A{\mathcal{A}} +\gdef\B{\mathcal{B}} +\gdef\cA{\mathcal{A}} +\gdef\alg#1{\mathfrak{#1}} +\gdef\mc#1{\mathcal{#1}} +\gdef\bh{\mathfrak{B}(\mathcal{H})} +\gdef\2#1{{\mathcal #1}} +$$ + +# Von Neumann bicommutant theorem + +## What this object is for + +The bicommutant theorem identifies a purely algebraic condition on an operator +algebra $M \subseteq B(H)$ — that it is its own bicommutant — with a purely +topological one — that it is closed in the weak (equivalently strong) operator +topology. It is what makes the theory of von Neumann algebras possible at all: +it lets a definition be given algebraically and then used topologically, so that +weak limits, spectral projections and polar decompositions of elements of $M$ +stay inside $M$. Its generality matters in exactly one direction: the algebra is +required to act non-degenerately (a condition strictly weaker than containing +$1$), and nothing about separability, σ-finiteness, type, or commutativity +enters anywhere. + +## Definition + +### Variants as the sources write them + +Axes along which the corpus genuinely splits. The grid is a derived index and +carries no tier; where it disagrees with a block below, the grid is wrong. + +| (D#) | Source | Assumption on $M$ | Norm-closed? | Closure conditions listed | Which side is the definition | +|---|---|---|---|---|---| +| (D1) | SHI12 | none (unitality derived) | — | $M = M''$ | algebraic | +| (D2) | SHI12 | acts non-degenerately | **yes** ($C^{*}$-subalgebra) | $M=M''$ / WOT / SOT | theorem, both sides | +| (D3) | LAN98 | $1 \in M$ | no | $M''=M$ / WOT / SOT | theorem, both sides | +| (D4) | LAN98 (reporting von Neumann) | none | no | **sequential** WOT-completeness | topological | +| (D5) | HIA20 | $1 \in M$ | no | WOT / SOT / $M''=M$ | topological | +| (D6) | HM06 | $1 \in M$ | no | WOT / $R''=R$ — **no SOT** | algebraic, by election | +| (D7) | NAA13 | unital | no | WOT — **no SOT** | algebraic ($M = A''$) | +| (D8) | SOR23 | folded into "$*$-subalgebra" | no | WOT $\Rightarrow$ vN algebra (one direction) | algebraic | +| (D9) | KOS13 | unital | no | $N=N''$ / σ-weak / σ-strong / σ-strong-⋆ — **no WOT, no SOT** | algebraic | +| (D10) | KOS13 | — ($W^*$-algebra, abstract) | — | possession of a predual | Hilbert-space-free | +| (D11) | all | — | — | the six sources' own topology definitions | — | + +**(D1) [SHI12] — von Neumann algebra as $M = M''$, unitality derived** — tier (a) + +> `Let $H$ be a Hilbert space. An involutive subalgebra $M$ of $B(H)$ is called a {\bf von Neumann algebra on $H$} if $M=M''$.` + +> `One notes that every von Neumann algebra is necessarily unital.` + +SHI12 also fixes $VN(S) := C^*(S)''$ for the generated von Neumann algebra, and +calls $A''$ the enveloping von Neumann algebra of a $C^{*}$-subalgebra $A$. + +**(D2) [SHI12] — the theorem, non-degeneracy form** — tier (a) + +> `We say a {\bf \cs-subalgebra $A$ of $B(H)$ acts non-degenerately on $H$} if $x\in H$ and $Tx=0$ for all $T\in A$ implies $x=0$.` + +> `\label{thm:bicommutant} [The von Neumann bicommutant theorem] Let $M$ be a \cs-subalgebra of $B(H)$ acting non-degenerately on $H$. Then the following statements are equivalent:` + +with the three equivalents `$M=M^{\prime \prime}$`, `$M$ is weakly closed`, +`$M$ is strongly closed`. + +`differs from (D3) by:` SHI12 hypothesises non-degeneracy where every other +corpus source hypothesises $1 \in M$, and is the only source to require $M$ +norm-closed. +`sources claim equivalence:` not addressed — no corpus source compares the two +hypothesis packages. They are not equivalent; see (X1) and (X6). + +**(D3) [LAN98] — the double commutant theorem, unital $*$-algebra** — tier (a) + +> `Let $\M$ be a \sta\ in $\BH$, containing $\I$. The following are` + +equivalent: `$\M''=\M$`; `$\M$ is closed in the weak operator topology`; +`$\M$ is closed in the strong operator topology`. + +`differs from (D2) by:` unitality in place of non-degeneracy, and no +norm-closedness. +`sources claim equivalence:` not addressed. + +**(D4) [LAN98] — von Neumann's original definition, as LAN98 reports it** — tier (a) + +> `defines a {\bf ring of operators} $\M$ (nowadays called a {\bf von Neumann algebra}) as a $\mbox{}^*$-subalgebra of the algebra $\BH$ of all bounded operators on a \Hs\ $\H$ (i.e, a subalgebra which is closed under the involution $A\raw A^*$) that is closed (i.e., sequentially complete) in the weak operator topology.` + +LAN98 then changes its own definition mid-text: + +> `from now on we add to the definition of a von` + +> `Neumann algebra the condition that $\M$ contains $\I$.` + +`differs from (D3) by:` no unit, and closure phrased **sequentially**. WOT is not +first countable, so this is not a notational difference; see (X7), which +separates the two on a non-separable space. +`sources claim equivalence:` not addressed; LAN98 does not flag its own switch. + +**(D5) [HIA20] — von Neumann algebra defined topologically** — tier (a) + +> `A *-subalgebra of $B(\cH)$ is called a \emph{von Neumann algebra} (also \emph{$W^*$-algebra})` + +> `if it contains the identity operator $1$ and closed in the weak topology.` + +and, for a $*$-subalgebra $M$ with $1 \in M$, the + +> `commutation theorem} or \emph{von Neumann's density theorem} says that the following three conditions are equivalent:` + +namely weakly closed / strongly closed / $M''=M$. + +`differs from (D3) by:` the direction of definition is reversed — the definiens +is topological and $M''=M$ is derived. HIA20 also declares "von Neumann +algebra" and "$W^*$-algebra" synonyms, which (D10) refuses. +`sources claim equivalence:` yes, by its own statement of the theorem. + +**(D6) [HM06] — algebraic condition taken as basic, two conditions only** — tier (a) + +> `The standard definition of a von Neumann algebra involves reference to a topology, and it is then shown (by von Neumann's double commutant theorem) that this topological condition coincides with an algebraic condition (condition 2 in the Definition \ref{vNA}). But for present purposes, it will suffice to take the algebraic condition as basic.` + +> `For a $*$-algebra $\alg{R}$ on $\2H$ that contains $I$, the following are equivalent: (i) $\alg{R}$ is weakly closed; (ii) $\alg{R}''=\alg{R}$. This is von Neumann's double commutant theorem.` + +`differs from (D3) by:` the SOT clause is absent, though HM06 has defined SOT. +HM06 writes the commutant with a commutator, $[B,A]=0$. +`sources claim equivalence:` yes for its own two conditions. + +**(D7) [NAA13] — unital $*$-subalgebra, WOT only** — tier (a) + +> `a unital $*$-subalgebra of $\alg{B}(\mathcal{H})$ is a von Neumann algebra if and only if it is closed in the weak operator topology. This result, which is known as the \emph{bicommutant theorem}, relates the purely algebraic definition of von Neumann algebras given on page~\pageref{p:vna}, to a topological condition.` + +`differs from (D6) by:` NAA13's "purely algebraic definition" is not $M = M''$ +but the construction $M = A''$ from an ambient $*$-algebra $A$. +`sources claim equivalence:` yes, between its own two conditions. + +**(D8) [SOR23] — $A = A''$, with unitality folded into "$*$-subalgebra"** — tier (a) + +> `A subset of $\B(\H)$ is said to be a \textbf{$*$-subalgebra} if it is closed under scalar multiplication, operator multiplication, operator addition, and adjoints, and contains the identity operator.` + +> `A $*$-subalgebra $\A \subseteq \B(\H)$ is a \textbf{von Neumann algebra} if it is equal to its own double commutant, i.e., $\A = \A''.$` + +and the theorem, one-directional and stated inside a proof sketch: + +> `It uses the double commutant theorem, which says that any $*$-subalgebra of $\B(\H)$ that is topologically closed with respect to the ``weak operator topology''\footnote{The weak operator topology is explained in appendix \ref{app:operator-topologies}.} is a von Neumann algebra.` + +`differs from (D6) by:` unitality lives in the ambient notion, so SOR23's +"$*$-subalgebra" is not the standard one. **Read with the standard non-unital +meaning the statement is false**, witnesses $\{0\}$ and $\mathbb{C}p$ for a +proper projection $p$; read with SOR23's own definition it is the standard +unital statement and is true. See (X8). +`sources claim equivalence:` not addressed. + +**(D9) [KOS13] — commutant relative to an ambient algebra, σ-topologies** — tier (b) +(byte-verbatim, but KOS13's macros are unavailable, so the mathematical reading +of the quote is inferred) + +> `The \df{commutant} of a subalgebra $\N$ of any algebra $\C$ is defined as` + +> `\N^\comm:=\{y\in\C\mid xy=yx\;\forall x\in\N\},` + +> `A unital $*$-subalgebra $\N$ of an algebra $\BH$ is called the \df{von Neumann algebra} \cite{vonNeumann:1930:algebra,Murray:vonNeumann:1936} if{}f $\N=\N^\comm{}^\comm$. From von Neumann's double commutant theorem \cite{vonNeumann:1930:algebra} it follows that this is equivalent with any of the conditions: $\N$ is weakly-$\star$ closed, $\N$ is ultrastrongly closed, $\N$ is ultrastrongly-$\star$ closed. In particular, $\BH$ is a von Neumann algebra.` + +`differs from (D3) by:` two independent things. The commutant is taken relative +to an arbitrary ambient algebra, not inside $B(H)$; and the closure conditions +are the σ-topologies, WOT and SOT occurring nowhere in KOS13. That these +topologies are the standard σ-topologies is fixed by KOS13 itself: + +> `When considered in the context of $W^*$-algebras $\N\subseteq\BH$, these topologies are usually called \textit{$\sigma$-strong} and \textit{$\sigma$-strong-$\star$}, respectively.` + +`sources claim equivalence:` KOS13 asserts its four conditions equivalent, citing +von Neumann; **no corpus source bridges its list to the WOT/SOT list**. The note +supplies that bridge itself as (X9), at tier (d). + +**(D10) [KOS13] — $W^*$-algebra as the abstract notion, kept distinct** — tier (b) + +> `If for a given $C^*$-algebra $\C$ there exists a predual $\C_\star$, then it is a unique predual of $\C$, and in such case $\C$ is called a \df{$W^*$-algebra} \cite{Sakai:1956}.` + +> `An image $\pi(\N)$ of any representation $(\H,\pi)$ of a $W^*$-algebra $\N$ is a von Neumann algebra if{}f $\pi$ is normal and nondegenerate.` + +`differs from (D5) by:` KOS13 keeps $W^*$-algebra and von Neumann algebra as two +notions related by a cited theorem, where HIA20 declares them synonyms. This is +a definitional disagreement in the corpus, not a notational one. +`sources claim equivalence:` yes, by citing Sakai — see the external edge in +(R14). + +**(D11) — the operator topologies, per source** — tier (a) + +Filed as a variant because the theorem's content is exactly which topology is +named. SHI12 defines SOT, WOT and strong-⋆ and **no** σ-topology; LAN98 defines +norm, strong and weak by nets; HIA20 names norm/SOT/WOT without defining them and +adds the σ-weak topology as $\sigma(B(\mathcal{H}),\mathcal{C}_1(\mathcal{H}))$; SOR23 defines +norm, strong and weak by seminorms and no σ-topology; HM06 defines uniform, weak, +strong and **ultraweak**, the last concretely via density operators; KOS13 +defines only σ-topologies, on an abstract $W^*$-algebra via its predual. Three +mutually inequivalent ambient frameworks, therefore, and only (X9) connects the +third to the first. + +**Notation trap.** SHI12's "strong-`\ss` operator topology" is defined by the +seminorms $T \mapsto \|Tx\| + \|T^{*}x\|$ — that is the **strong-⋆** topology, +not KOS13's σ-strong. Matching the two by their spelling is a mistake. + +### Adopted general form + +Let $H$ be a complex Hilbert space and let $M \subseteq B(H)$ be a +$*$-subalgebra — a linear subspace closed under products and under the adjoint, +**not assumed norm-closed and not assumed to contain $1$** — which acts +non-degenerately on $H$, meaning: for $x \in H$, if $Tx = 0$ for every +$T \in M$, then $x = 0$. Write $M' = \{S \in B(H) : ST = TS \text{ for all } +T \in M\}$ and $M'' = (M')'$. Then the following three conditions are +equivalent: + +1. $M = M''$; +2. $M$ is closed in the weak operator topology of $B(H)$; +3. $M$ is closed in the strong operator topology of $B(H)$. + +The standing assumptions are (A3) self-adjointness, (A5) the algebra structure +and (A10) that $H$ is a complex Hilbert space; the one local hypothesis is (A1) +non-degeneracy. No separability, σ-finiteness, type or commutativity assumption +is carried, and none is available to be dropped. + +This is (D2) with SHI12's norm-closedness hypothesis deleted. The choice of +(D2) over the unital variants is justified by (X1): $K(H)$ for infinite +-dimensional $H$ acts non-degenerately and is not unital, so (D3), (D5), (D6), +(D7) and (D9) say nothing about it while (D2) does. The deletion of +norm-closedness is justified by (X4): no step of the corpus proof consumes it +and each of the three conditions implies it, so assuming it removes only +instances where all three fail — a class inhabited by the finite-rank operators +$F(H)$. + +### The density form the equivalence hides + +The corpus proof establishes more than the equivalence states, and the stronger +statement is the one a formalization is likely to want as the primary result: +for **every** non-degenerate $*$-subalgebra $M \subseteq B(H)$, with no closure +hypothesis at all, + +$$M'' = \overline{M}^{\,\mathrm{SOT}} = \overline{M}^{\,\mathrm{WOT}}.$$ + +The three-way equivalence is a corollary. See (R2) for the tier. + +## Notation and conventions + +| (C#) | Axis | This note | Per source | Translation | +|---|---|---|---|---| +| (C1) | conjugate-linear argument of the inner product | irrelevant to every statement here, provided the WOT seminorms range over all pairs | SHI12 linear in the first; LAN98 conjugate-linear in the first, explicit; SOR23, HIA20, NAA13 conjugate-linear in the first, implicit (tier b); HM06 and KOS13 unresolved | SHI12's $\langle x,y\rangle$ is LAN98's $(y,x)$ | +| (C2) | status of $1 \in M$ | a conclusion, not a hypothesis | conclusion (SHI12); hypothesis (LAN98, HIA20, HM06, NAA13, KOS13); part of the ambient notion (SOR23); added mid-text (LAN98) | (A2) | +| (C3) | self-adjointness of the set whose commutant is taken | assumed throughout | isolated only by SHI12; stated informally by LAN98 and SOR23; omitted by HIA20, HM06, NAA13, KOS13 | without it $M'$ need not be a $*$-algebra — (X2) | +| (C4) | name of the theorem | bicommutant theorem | bicommutant (SHI12, NAA13, KOS13); double commutant (LAN98, SOR23, HM06, KOS13); double commutation **or** von Neumann's density theorem (HIA20) | HIA20's "density theorem" is a name collision with Kaplansky density, which HIA20 states separately — see (R11), (R12) | +| (C5) | which topologies enter the statement | WOT and SOT | three conditions (SHI12, LAN98, HIA20); two (HM06, NAA13); one direction (SOR23); four σ-conditions (KOS13) | (X9) | +| (C6) | "von Neumann algebra" vs "$W^*$-algebra" | kept distinct | synonyms (HIA20); distinct notions related by a cited theorem (KOS13) | Sakai's theorem, (R14) | +| (C7) | algebraic vs topological primacy | algebraic condition stated first, topological conditions as equivalents | explicit, self-aware disagreement across the corpus — HM06 and NAA13 elect the algebraic side, HIA20 the topological, SHI12 the algebraic with a remark | presentation only, no content | +| (C8) | commutant notation | $M'$, $M''$ | $X'$/$X''$ everywhere; SHI12 continues $X''', X''''$; KOS13 uses a macro, not a prime; HM06 defines it by $[B,A]=0$ | — | +| (C9) | nets vs sequences | nets | LAN98's report of von Neumann's original definition is sequential; HM06, SOR23 and SHI12 all warn that WOT is not first countable | not interchangeable — (X7) | + +## Results and dependencies + +### (R1) The bicommutant theorem, non-degeneracy form + +For $M$ a norm-closed $*$-subalgebra of $B(H)$ acting non-degenerately: +$M = M''$ ⟺ $M$ WOT-closed ⟺ $M$ SOT-closed. + +- Source: [SHI12] Theorem `thm:bicommutant` · tier (a) · **proved in source** +- Depends on: (R3), (R5), (R6), (R7), (R8), (A1), (A3), (A5) +- Conventions: (C2), (C3), (C5) +- Proof route: + 1. (i)⇒(ii) and (ii)⇔(iii) in one line, consuming (R7) [WOT coarser than SOT], + (R5) [every commutant is WOT- and SOT-closed] and (R6) [for a convex set, + SOT-closed ⟺ WOT-closed; $M$ is a subspace, hence convex — (A5)]. + 2. One-vector case: fix $x_0$, put $X = \overline{Mx_0}$, $P = P_X$; then + $PTP = TP$ for $T \in M$ because $MX \subseteq X$ — consumes (A5). + 3. Adjoint trick $TP = (PT^{*}P)^{*} = (T^{*}P)^{*} = PT$, so $P \in M'$ — + consumes (A3), and consumes it **here only**. + 4. $T(1-P)x_0 = (1-P)Tx_0 = 0$ for all $T \in M$, so $(1-P)x_0 = 0$ — + consumes (A1), and consumes it **here only**. + 5. For $S \in M''$: $SP = PS$, so $Sx_0 = PSx_0 \in X$, giving $T \in M$ with + $\|(S-T)x_0\| < \varepsilon_0$. + 6. Amplify to $H^n$ and use $D(M)' = M_n(M')$ to place $D(S)$ in $D(M)''$ — + consumes (R8). + 7. Re-run steps 2–5 on $(H^n, D(M), D(S))$ and estimate + $\|(S-T)x_m\| \le \|(D(S)-D(T))x\| < \varepsilon$, so + $M'' \subseteq \overline{M}^{\,\mathrm{SOT}} = M$. + 8. $M \subseteq M''$ is (R3)(iii) — consumes (R3). +- Note: step 7 re-uses the one-vector argument on $D(M)$ without checking that + $D(M)$ acts non-degenerately on $H^n$. The check is one line and is written + out at (A8); the mathematics is unaffected. +- Verbatim: + > `The implications (i) $\Rightarrow$ (ii) $\Leftrightarrow$ (iii) follow from Proposition \ref{prop:strongweaktop}(i), Remark \ref{rem:continofoperations}(v), and Corollary \ref{cor:weakstrongconvexclosed}.` + + > `Since $M$ acts non-degenerately on $H$, $(1-P)x_0=0$.` + +### (R2) The density form + +For every non-degenerate $*$-subalgebra $M \subseteq B(H)$, with no closure +hypothesis, $M'' = \overline{M}^{\,\mathrm{SOT}} = \overline{M}^{\,\mathrm{WOT}}$. + +- Source: [SHI12] — the same proof, read as establishing more than it states · + tier (b) · **proved in source, but not stated there**: the proof is (R1)'s and + is quoted under it; the statement is this note's reading of that proof, which + is why the row is (b) +- Depends on: (R1) steps 2–7, (R5), (R6), (A1), (A3), (A5) +- Conventions: (C5) +- Nothing in steps 2–7 uses any of (i),(ii),(iii); they are consumed only in + step 1 and in the final identification $\overline{M}^{\,\mathrm{SOT}} = M$. + The corpus does not state this form, which is why the row is (b) and not (a). + +### (R3) Basic properties of the commutant, including $X''' = X'$ + +For subsets $X, X_1, X_2 \subseteq B(H)$: (i) $X_1 \subseteq X_2 \Rightarrow +X_2' \subseteq X_1'$; (ii) $X'$ is a norm-closed unital subalgebra; (iii) +$X \subseteq X'' = X'''' = \cdots$ and $X' = X''' = \cdots$; (iv) $X$ +self-adjoint $\Rightarrow$ $X'$ self-adjoint; (v) $X'' = B(H)$ iff +$X' = \mathbb{C}1$. + +- Source: [SHI12] Proposition `prop:basiccommutant` · tier (a) · **proved in source** +- Depends on: nothing, except (v) which consumes SHI12's Cauchy–Schwarz + equality case +- Conventions: (C3), (C8) +- Verbatim: + > `\item [(ii)] $X'$ is a closed unital subalgebra of $B(H)$.` + + > `\item [(iv)] If $X$ is a self adjoint subset of $B(H)$, then $X'$ is self adjoint, and consequently a unital \cs-subalgebra of $B(H)$.` + +- (ii) is what makes (A2) a theorem rather than a hypothesis: $M = M''$ forces + $1 \in M$. + +### (R4) The projection lemma + +For $M$ a $*$-algebra in $B(H)$ and $\Psi \in H$ non-zero, the orthogonal +projection onto $\overline{M\Psi}$ lies in $M'$. + +- Source: [LAN98] Lemma `DCTlemma` · tier (a) · **proved in source** +- Depends on: LAN98's decomposition of an element into self-adjoint parts +- Proof route: $A \in M$ gives $ApH \subseteq pH$, hence $p^{\perp}Ap = 0$, i.e. + $Ap = pAp$; for $A = A^{*}$ this yields $(Ap)^{*} = pA = pAp = Ap$; extend to + all of $M$ by linearity. +- Verbatim: + > `Let $\M$ be a \sta\ in $\BH$, take a nonzero vector $\Ps\in\H$, and` + + > `let $p$ be the projection onto the closure of $\M\Ps$. Then $p\in\M'$` + +- This is the same content SHI12 inlines as (R1) steps 2–3. LAN98 isolates it and + re-uses it in three further results; SHI12 never names it. + +### (R5) Every commutant is WOT- and SOT-closed + +- Source: [SHI12] Remark `rem:continofoperations`(v) · tier (a) · **asserted** + (SHI12 sets the proof as an exercise); [LAN98] proves the WOT half inline + inside its proof of (R9) · tier (a) · **proved in source** +- Depends on: separate continuity of multiplication +- The corpus therefore does contain a proof of this — in the other source than + the one that states it as a remark. +- Verbatim (LAN98): + > `the commutant $\GN'$ of a \sta\ $\GN$ is always weakly closed` + +### (R6) A convex set is SOT-closed iff WOT-closed + +- Source: [SHI12] Corollary `cor:weakstrongconvexclosed` · tier (a) · **proved in source** +- Depends on: Hahn–Banach separation in a locally convex space, and (R7) +- Conventions: (C5) +- Verbatim: + > `Let $X$ be a convex set in $B(H)$. Then $X$ is strongly closed if and only if it is weakly closed.` + +- This is the whole content of the (ii)⇔(iii) half of (R1); it consumes (A5) + and none of (A1), (A2), (A3). + +### (R7) WOT ⊆ SOT ⊆ norm topology + +- Source: [SHI12] Proposition `prop:strongweaktop` · tier (a) · **proved in source**; + the same two reasons appear unnumbered in [LAN98] +- Depends on: nothing +- This is what makes (A4) redundant: a WOT- or SOT-closed set is norm-closed. + +### (R8) Amplification: $B(H^n) \cong M_n(B(H))$ and $D(M)' = M_n(M')$ + +- Source: [SHI12] Problem `e:5-22` · tier (a) · **asserted** (set as an exercise, + no proof); [LAN98], inside its proof of (R10), proves the commutator half + inline and asserts $M_n(M')' = M_n(M'')$ · tier (a) · **proved in source** (in + part) +- Depends on: nothing +- Verbatim (LAN98): + > `Hence $\dl(\M)'=\M^n(\M')$.` + +- This is the one place where the two full proofs differ in rigour: SHI12 exiles + it to an unproved exercise, LAN98 proves half of it and waves at the rest. + +### (R9) The bicommutant theorem, unital form + +For $M$ a $*$-algebra in $B(H)$ containing $1$: $M'' = M$ ⟺ WOT-closed ⟺ +SOT-closed. + +- Source: [LAN98] Theorem `DCT` · tier (a) · **proved in source** +- Depends on: (R4), (R5), (R7), (R8), (R10), (A2). Self-adjointness (A3) and the + algebra structure (A5) enter this proof only through (R4), not through a step + of its own +- Conventions: (C2), (C5) +- Proof route: + 1. 1⇒2 via (R5), proved inline by the net computation + $(\Phi,[A,B]\Psi) = \lim_\alpha (\Phi,[A_\lambda,B]\Psi) = 0$. + 2. 2⇒3 trivially, WOT being coarser than SOT — consumes (R7). + 3. 3⇒1: $p = [\overline{M\Psi}] \in M'$ by (R4); $1 \in M$ gives + $\Psi = 1\Psi \in M\Psi$, so $p\Psi = \Psi$ and $A\Psi \in + \overline{M\Psi}$ for $A \in M''$ — consumes (A2), **and consumes it + here only**, at exactly the point where (R1) consumes (A1). + 4. Amplify as in (R10), giving $A_\varepsilon \in M$ inside a basic SOT + neighbourhood — consumes (R8). + 5. $M$ SOT-closed gives $M'' \subseteq M$. +- Not self-contained: LAN98 writes step 4 as a delta on the proof of (R10) and + does not re-derive the amplification. +- Verbatim: + > `Let $\M$ be a \sta\ in $\BH$, containing $\I$. The following are` + +### (R10) The finite-dimensional case + +For $M$ a $*$-algebra in $M_n(\mathbb{C})$ containing $1$, $n < \infty$: +$M'' = M$, with **no** topological hypothesis. + +- Source: [LAN98] Proposition `DCT1` · tier (a) · **proved in source** +- Depends on: (R4), (R8), (A2), (A6), (A7) +- Proof route: $M\Psi$ is already closed in finite dimensions, so no closure is + needed; $p = [M\Psi] \in M'$ by (R4); $1 \in M$ gives $p\Psi = \Psi$, hence + $A\Psi = A_0\Psi$ for some $A_0 \in M$; amplify by (R8) and run the argument + on $n$ spanning vectors to get $A = A_0$. +- Verbatim: + > `Let $\M$ be a \sta\ (and hence a \ca) in $\M^n(\C)$ containing $\I$` + +- LAN98 states the sharpness itself: + > `As it stands, Proposition \ref{DCT1} is not valid when $\M^n(\C)$ is replaced by $\BH$, where $\dim(\H)=\infty$.` + +### (R11) The theorem as the four other sources carry it + +- Sources: [HIA20] §1.2 (three conditions), [HM06] its `fact` (two conditions, + no SOT), [NAA13] (two conditions, no SOT), [SOR23] (one direction, inside a + proof sketch) · tier (a) each · **asserted** — none of the four gives a proof +- Depends on: [ext: VN30 — a $*$-subalgebra of the bounded operators on a + Hilbert space that contains the identity equals its own bicommutant if and + only if it is closed in the weak operator topology. tier (c), no locator, not + retrieved]; SOR23 additionally points its proof at a work of Conway, see + (R15) +- Conventions: (C4), (C5) +- Five of the seven corpus sources carry the theorem with no proof at all. + +### (R12) Kaplansky density — stated in the corpus, consumed by nothing in it + +For a $*$-subalgebra $\mathcal{A}$ of $B(H)$ containing $1$, the unit ball of +$\mathcal{A}$ is SOT-dense in the unit ball of $\mathcal{A}''$. + +- Source: [HIA20] §1.2 · tier (a) · **asserted** (no proof, no citation to one) +- Depends on: nothing stated +- Verbatim: + > `$\{a\in\cA:\|a\|\le1\}$ is strongly dense in $\{a\in\cA'':\|a\|\le1\}$.` + +- No result in any of the seven sources cites it. It becomes load-bearing only + in (X7) and (X9), which are this note's own arguments. + +### (R13) $K(H)'' = B(H)$ + +- Source: [SHI12] Proposition `prop:voncompact` · tier (a) · **proved in source** +- Depends on: (R3)(v) +- The corpus's own witness that the bicommutant operation is non-trivial on a + non-unital algebra, and the separating object of (X1). SHI12 also records, as + an exercise, that $K(H)$ is not unital for infinite-dimensional $H$. + +### (R14) Downstream consumers, and one name collision + +- [SHI12]: the Borel functional calculus of a normal $T \in M$ stays inside $M$ · + tier (a) · **asserted** ("an immediate consequence"), reconstructible from + SHI12's own proof that the calculus is WOT-continuous. +- [HIA20]: polar and spectral decompositions are taken inside $M$ · tier (a) · + **asserted**. +- [KOS13]: the projections generate, $\mathrm{Proj}(N)'' = N$; and approximate + finite-dimensionality reads $N = (\bigcup_i N_i)''$ · tier (b) · **asserted** — + the corpus's only use of the theorem in the direction "density ⟹ bicommutant + identity", which is the direction that matters downstream. +- [SOR23]: for any adjoint-closed $\mathcal{A} \subseteq B(H)$, $\mathcal{A}'$ is + a von Neumann algebra · tier (a) · **sketched**. True with no unitality caveat, + since $\mathcal{A}'$ always contains $1$, is a $*$-algebra when + $\mathcal{A} = \mathcal{A}^{*}$, and is WOT-closed by (R5). +- [HIA20]: the **commutant theorem** $(M_1 \otimes M_2)' = M_1' \otimes M_2'$ + for tensor products · tier (a) · **asserted**. **A different theorem** sharing + a similar name; recorded so it is never merged with this one. +- [ext: Sakai — a C\*-algebra is isomorphic to a von Neumann algebra if and only + if it is the dual Banach space of some Banach space, and the predual is then + unique. tier (c), no locator, not retrieved], attested by HIA20 and KOS13; it + is the bridge (D10) uses. + +### (R15) The single-generator theorem + +On a separable Hilbert space, every abelian von Neumann algebra is $\{T\}''$ for +a single Hermitian $T$ in it. + +- Source: [SOR23] · tier (a) for the statement · **cited elsewhere** +- Depends on: [ext: VN30 — on a separable Hilbert space every abelian von + Neumann algebra is the bicommutant of a single self-adjoint operator it + contains. tier (c), no locator, not retrieved]; SOR23 additionally claims the + result appears as an exercise with hints in a work of Dixmier — that locator + is **SOR23's claim**, reported here and not adopted +- Conventions: (A9) +- Carried because it is the only place in the corpus where separability is + load-bearing; (A9) records the witness and (A11) what remains open. + +## Hypotheses + +| (A#) | Statement | Class | Evidence | Witness | Scope | Tier | Used by | +|---|---|---|---|---|---|---|---| +| (A1) | $M$ acts non-degenerately on $H$ | model-dependent | SHI12 states it as the hypothesis of the theorem | fails for $M = B(H_1) \oplus 0 \subseteq B(H_1 \oplus H_2)$, where (ii),(iii) hold and (i) fails; holds for $K(H)$ and $B(H)$ | local | a | (R1) step 4 only, i.e. (iii)⇒(i) only | +| (A2) | $1 \in M$ | provable | from (A1) plus any one of (i),(ii),(iii): (iii)⇒(i) gives $M = M''$, and $M''$ is unital by (R3)(ii). SHI12 records the conclusion in prose | — | local in LAN98/HIA20/HM06/NAA13/KOS13; standing in LAN98 after its mid-text change, and in SOR23 | a | (R9) step 3, (R10) — and nothing else in either proof | +| (A3) | $M$ is self-adjoint | model-dependent | every corpus source imposes it | fails for the upper-triangular $2\times2$ complex matrices: unital, closed in every topology, $M' = \mathbb{C}1$, $M'' = M_2(\mathbb{C}) \supsetneq M$; holds for every von Neumann algebra | local, and standing in SOR23 | a | (R1) step 3, (R4), (R3)(iv) | +| (A4) | $M$ is norm-closed | provable, **and used by no step** | each of (i),(ii),(iii) implies it via (R7); a step-by-step reading of (R1) finds no step consuming it | — | local, SHI12 only | a | nothing | +| (A5) | $M$ is a subalgebra, hence convex | provable (the convexity half is immediate) | a subalgebra is a linear subspace | fails for $V = \operatorname{span}\{1, e_{12}+e_{21}\} \subseteq M_3(\mathbb{C})$: self-adjoint, unital, closed, $\dim V = 2$ but $\dim V'' = 3$ | local, all sources | a | (R1) step 2, (R4), (R6) | +| (A6) | $\dim H < \infty$ | model-dependent | LAN98 imposes it on the finite-dimensional case and states its sharpness itself | fails for $M = K(H) + \mathbb{C}1$ on infinite-dimensional $H$: unital, norm-closed, $M' = \mathbb{C}1$, $M'' = B(H) \supsetneq M$; holds for $M_n(\mathbb{C})$ | local, (R10) only | a | (R10), to know $M\Psi$ is closed — it is what hypothesis (iii) is substituted for | +| (A7) | the vectors in (R10) span | provable | choose an orthonormal basis; nothing else in the argument constrains them | — | local, implicit — LAN98 says "arbitrary" | a | (R10)'s final step | +| (A8) | $D(M)$ acts non-degenerately on $H^n$ | provable, in one line | if $D(T)y = (Ty_1,\dots,Ty_n) = 0$ for all $T \in M$ then each $y_k$ is annihilated by all of $M$, so $y_k = 0$ by (A1) | — | local, implicit — SHI12 never checks it | a | (R1) step 7 | +| (A9) | $H$ separable | model-dependent | SOR23 declares it in its front matter | fails for $\ell^2(I)$, $I$ uncountable; holds for $L^2(\mathbb{R}^n)$ | standing in SOR23 | a | **not** the bicommutant theorem — no step of either full proof and no statement of the theorem uses it; used by (R15) and by the direct-integral decomposition | +| (A10) | $H$ is a complex Hilbert space | provable (it delimits the class rather than constraining members) | completeness is what gives the projection $P_X$ | — | standing | a | (R1) step 2, (R4) | +| (A11) | sharpness of (A9) below $2^{\aleph_0}$ | open | for $\lvert I \rvert > 2^{\aleph_0}$, $\ell^\infty(I)$ on $\ell^2(I)$ has no single Hermitian generator (argument written out in the lane notes); for $\lvert I \rvert \le 2^{\aleph_0}$ an injective $I \to \mathbb{R}$ exists and the obstruction vanishes | — | local | b | (R15) | + +Cleared, after checking: irreducibility and factoriality; cyclic and separating +vectors; faithfulness and normality of representations; σ-finiteness, +hyperfiniteness, proper infiniteness and the type conditions; boundedness. None +is a hypothesis of this theorem in any corpus source. + +## Degeneracies and boundary cases + +| Case | Effect on the adopted form | Tier | +|---|---|---| +| intended case is nonvacuous | $B(H)$ for every $\dim H$ (HM06's type I$_\kappa$ factor); $\{T\}''$ for a single Hermitian $T$ (SOR23); $VN(S) = C^*(S)''$ (SHI12); hyperfinite $(\bigcup_a R_a)''$ (HM06); the type III local algebras of QFT (HM06) | a | +| zero object — $H = \{0\}$ | no effect. $B(\{0\}) = \{0\}$, non-degeneracy is vacuous, the identity is $0$ and lies in $M$, so **both** hypothesis packages hold and all three conditions are true. There is no edge case here | — | +| zero object — $M = \{0\}$, $H \ne \{0\}$ | excluded by every corpus variant. It is the minimal witness for dropping non-degeneracy: $M$ is WOT-closed but $M'' = \mathbb{C}1 \supsetneq M$ | — | +| scalars — $M = \mathbb{C}1$ | no effect; non-vacuous but trivial ($M' = B(H)$, $M'' = M$) | — | +| finite-dimensional | holds, and the topological content evaporates: LAN98 proves this case with no topological hypothesis, and HM06 states that the four topologies coincide iff $H$ is finite-dimensional. In finite dimensions non-degeneracy and unitality coincide for $*$-subalgebras — see (X5) | a | +| commutative | no effect; neither proof mentions commutativity, and the theorem keeps its content ($C[0,1]$ on $L^2[0,1]$ is neither WOT-closed nor its own bicommutant) | a | +| non-separable / non-σ-finite | no effect. Neither full proof assumes separability; $\ell^\infty(X)$ on $\ell^2(X)$ for uncountable $X$ is maximal abelian, hence satisfies the form and is not σ-finite | a | +| type III | no effect; the type classification is downstream of this theorem and never upstream of it | a | +| non-unital | **this is where the variants differ, and the difference is real.** $K(H)$ for infinite-dimensional $H$ is non-degenerate and non-unital, and satisfies the equivalence negatively: all three conditions fail together, since $K(H)'' = B(H)$ | a | +| degenerate representation | the hypothesis bites — see (X1) | — | +| universally orthogonal index element (here: large joint kernel or a reducing subspace) | a reducing subspace alone has no effect ($\mathbb{C}1$ on $\mathbb{C}^2$); a summand on which $M$ acts as zero breaks (i) and only (i). NAA13's failure of Haag duality for infinite regions is **not** a boundary of this theorem — it is the identification of $\pi(A(\Lambda))''$ with another algebra's commutant that fails, not the bicommutant construction | a | +| quantifier swap: non-degeneracy $\forall x \forall T \mapsto \forall T \forall x$ | meaning changes and the swapped form is wrong: it says every element of $M$ is injective, which fails for $M = B(\mathbb{C}^2)$, $T = e_{11}$ — the paradigm von Neumann algebra. See (X10) | — | +| quantifier swap: inside the commutant | degenerate probe. The two universals are separated by an implication and by different sorts; exchanging them is not type-correct. No row | — | +| hypothesis dropped: (A1) non-degeneracy | theorem false; (i)⇒(ii) and (ii)⇔(iii) survive, (iii)⇒(i) dies. See (X1) | — | +| hypothesis dropped: (A3) self-adjointness | theorem false. See (X2) | — | +| hypothesis dropped: (A5) algebra structure | theorem false. See (X3) | — | +| hypothesis dropped: (A4) norm-closedness | **droppable — this is what the adopted form does.** The enlarged class is inhabited by $F(H)$, for which all three conditions fail together. See (X4) | — | +| hypothesis dropped: (A9) separability | already dropped; no effect | a | +| hypothesis dropped: complex scalars | **open** — every corpus source works over $\mathbb{C}$, none states the real case, and neither a proof nor a counterexample was found | — | + +## Rejected formulations and refuted claims + +**Append-only.** + +| id | Candidate or claim | Disposition | Discriminator | Tier | Date | +|---|---|---|---|---|---| +| (X1) | the theorem with (A1) dropped — equivalently, the unital variants (D3),(D5),(D6),(D7),(D9) as a *general* form | rejected | **(X1) separating object** — $M = \mathbb{C}e_{11} \subseteq M_2(\mathbb{C})$ is a closed self-adjoint algebra with $M' = $ the diagonal, so $M'' = \mathbb{C}e_{11} \oplus \mathbb{C}e_{22} \supsetneq M$ while (ii),(iii) hold; in infinite dimensions $B(H_0) \oplus 0$. Against the unital variants the separating object is $K(H)$, $\dim H = \infty$, which they do not cover | a | 2026-08-16 | +| (X2) | the theorem with (A3) dropped | rejected | **(X1) separating object** — the upper-triangular $2\times2$ complex matrices: unital, closed in every topology, $M' = \mathbb{C}1$, $M'' = M_2(\mathbb{C}) \supsetneq M$ | a | 2026-08-16 | +| (X3) | the theorem for a self-adjoint *subspace* rather than an algebra | rejected | **(X1) separating object** — $V = \operatorname{span}_{\mathbb{C}}\{1, e_{12}+e_{21}\} \subseteq M_3(\mathbb{C})$: $\dim V = 2$, and $e_{12}+e_{21}$ has three simple eigenvalues so $V''$ is a $3$-dimensional maximal abelian algebra. In $M_2$ the same construction gives no separation | a | 2026-08-16 | +| (X4) | keeping SHI12's norm-closedness hypothesis | rejected | **(X3) generality loss** — the finite-rank operators $F(H)$ on infinite-dimensional $H$ form a non-degenerate $*$-subalgebra that is not norm-closed, with $F(H)' = \mathbb{C}1$, so all three conditions fail together and the equivalence holds. Keeping the hypothesis drops that class and buys nothing: no proof step consumes it and each condition implies it | a | 2026-08-16 | +| (X5) | the unital form as a *distinct* theorem in finite dimensions | equivalent | — (in finite dimensions $M$ is automatically SOT-closed, so (iii)⇒(i) applies and delivers $M = M''$, which is unital; conditional equivalence with the assumption $\dim H < \infty$ named) | a | 2026-08-16 | +| (X6) | *claim*: the non-degenerate form is strictly more general than the unital form | adopted, sharpened | its entire extra content is the implication "non-degenerate and WOT-closed $\Rightarrow 1 \in M$": granted, it yields the adopted form; conversely the adopted form yields it because $X'$ is always unital. The extra scope contains only negative instances, and $K(H)$ inhabits it | a | 2026-08-16 | +| (X7) | von Neumann's original **sequential** WOT-completeness (D4) as a formulation of closedness | rejected | **(X1) separating object** — on a non-separable $H$ take $S = \{T : \operatorname{ran} T, \operatorname{ran} T^{*} \text{ separable}\}$ and $M = \mathbb{C}1 + S$: a unital $*$-subalgebra, sequentially WOT-closed, with $M' = \mathbb{C}1$ so $M'' = B(H)$, yet $M \ne B(H)$. For **separable** $H$ the two coincide, by Kaplansky density (R12) plus metrizability of SOT on the unit ball | b | 2026-08-16 | +| (X8) | *claim*: SOR23's statement of the theorem is false as written | refuted | SOR23's own definition of "$*$-subalgebra" includes the identity, so its statement is the standard unital one and is true. The witnesses $\{0\}$ and $\mathbb{C}p$ refute it only under the standard non-unital reading — they are a convention discriminator, recorded at (D8), not a defect | a | 2026-08-16 | +| (X9) | KOS13's σ-weak / σ-strong / σ-strong-⋆ form (D9) as a *different* theorem | equivalent | — (for a non-degenerate $*$-subalgebra all six closure conditions coincide; the argument runs SHI12's proof on the infinite ampliation $T \mapsto T \otimes 1$, whose seminorms pull back to the σ-topologies. **No corpus source states this**, and the ampliation identity it consumes is an unretrieved external, so the row is tier (d) as a whole. A bounded-set cross-check grounds the σ-weak and σ-strong branches for *unital* $M$ without that external; the σ-strong-⋆ branch does not survive the cross-check and keeps the (d) input) | d | 2026-08-16 | +| (X10) | non-degeneracy read as "every element of $M$ is injective" | rejected | **(X2) degeneracy** — the reading excludes $M = B(H)$ for $\dim H \ge 2$ (take $T = e_{11}$), i.e. the paradigm case the form must cover | a | 2026-08-16 | +| (X11) | non-degeneracy read as "$MH$ is dense in $H$" | equivalent | — (equivalent given (A3); the two come apart without it, separating object $\operatorname{span}\{e_{11}, e_{12}\} \subseteq M_2(\mathbb{C})$, an algebra with trivial joint kernel whose range is not dense. Conditional equivalence with the assumption $M = M^{*}$ named) | a | 2026-08-16 | +| (X12) | *claim*: HM06's fact that the weak, ultraweak and norm closures of a bounded convex set agree | refuted | the closed unit ball of $K(H)$, $\dim H = \infty$, is bounded and convex; its norm closure is itself and its weak closure is the unit ball of $B(H)$, by (R13) and (R12). Deleting "norm" makes the statement true | a | 2026-08-16 | + +## Prior art + +| System | Found | Relation to variants | How searched | Measured at | +|---|---|---|---|---| +| Mathlib | `VonNeumannAlgebra`, a structure extending `StarSubalgebra ℂ (H →L[ℂ] H)` whose defining field states that the centralizer of the centralizer of the carrier is the carrier | same as (D1)/(D8) — algebraic, and **unital by construction** since `StarSubalgebra` is; non-degeneracy is nowhere mentioned, so relative to (D2) it is stronger on hypotheses and $K(H)$ separates them | `grep -rn "VonNeumannAlgebra"` over `.lake/packages/mathlib`, whole file read; `lean_leansearch`; `lean_loogle` on the centralizer pattern | mathlib rev `5450b53e` | +| Mathlib | `WStarAlgebra`, a separate structure with a single field asserting a predual exists | same as (D10) — Mathlib follows KOS13's separation of the two notions, not HIA20's identification | same sweep | mathlib rev `5450b53e` | +| Mathlib | could not find any declaration equating WOT- or SOT-closedness with the bicommutant condition, having searched `grep -rni "bicommutant\|double.commutant\|doubleCommutant"`, `grep -rni "commutant"`, `lean_leansearch`, `lean_loogle` and `lean_leanfinder`. Mathlib's own module docstring says the equivalence and the theorem remain to be proved, and names a (D6)-shaped target | — | as listed | mathlib rev `5450b53e` | +| Mathlib | `Set.centralizer`, `Set.centralizer_centralizer_centralizer`, `StarSubalgebra.centralizer`, `VonNeumannAlgebra.commutant_commutant` (which holds by definition, not by a topological argument), `StarSubalgebra.topologicalClosure_adjoin_le_centralizer_centralizer` (an inclusion in the **norm** topology) | weaker than every corpus variant | same sweep | mathlib rev `5450b53e` | +| Mathlib | WOT is present (`→WOT[𝕜]`, `toWOT`); SOT is present under the name of the topology of pointwise convergence, with a docstring saying the term "strong operator topology" is deliberately avoided | — | `grep -rn "StrongOperatorTopology"` returned nothing, which prompted `grep -rni "strong operator topology"` | mathlib rev `5450b53e` | +| Mathlib | could not find Kaplansky density, a Borel/measurable functional calculus, spectral measures, direct integrals, σ-weak or σ-strong topologies, or normal states. The spectral theorem is present only in a finite-dimensional diagonalization form; the **continuous** functional calculus is present in full | — | `grep -rni` per term | mathlib rev `5450b53e` | +| This repository | `WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed`, `SOTClosedSubalgebra.doubleCommutant_eq_of_isSOTClosed`, and `SOTClosedSubalgebra.mem_sotClosure_of_mem_doubleCommutant` under `QuantumSystem/Algebra/Star/DoubleCommutant/`, with no `sorry` or `axiom` in that directory | same as (D3)/(D5) — all three take a `StarSubalgebra`, hence **unital**; none is stated for a non-unital or non-degenerate algebra. The third is the density statement (R2) in unital form, carrying no closedness hypothesis. The SOT theorem is derived from the WOT one, so the two are not independent proofs | file read; `grep -rn "sorry\|axiom "`; repo-wide grep for non-degeneracy finds it only in unrelated files. Statement-level measurement only — no `lake build` was run | repo `9b1810d` | +| This repository | could not find any equivalence (`iff`/TFAE) packaging the three conditions, nor any lemma constructing a `VonNeumannAlgebra` from a closed `StarSubalgebra`. Both main theorems are leaves: the WOT one is called only by the SOT one, and the SOT one is called nowhere | — | `grep -rn "TFAE\|IsWOTClosed.*↔\|↔ IsSOTClosed"`, call-site grep | repo `9b1810d` | +| mathlib4 GitHub | PR #35538, "feat(Analysis/VonNeumannAlgebra): double commutant theorem", opened 2026-02-19 and **closed unmerged** 2026-03-09. Its main statement is a two-condition WOT form for a unital `StarSubalgebra`, by the same diagonal-amplification route. Closed over a review disagreement; no mathematical obstruction appears in the thread | same as (D6) | unauthenticated GitHub REST search, then the PR, comments and files endpoints | 2026-08-16 | +| Lean AI leaderboard | the three-condition unital form is listed as a benchmark problem and is **solved**, first on 2026-05-09 and seven times since | same as (D3)/(D5) | fetched the problem page | 2026-08-16 | +| Isabelle AFP | `Complex_Bounded_Operators` provides complex Hilbert spaces, bounded operators, adjoints and projections; its abstract mentions no von Neumann algebra, commutant or operator topology | unrelated — substrate only | two web searches plus the entry abstract; the AFP search page returned only site chrome, so this negative rests on a weak instrument | 2026-08-16 | +| Coq / Rocq / mathcomp | could not find a von Neumann algebra or commutant development, having run **one** query. This is a single-query result, not an absence | — | one web search | 2026-08-16 | +| Lean Zulip | no archive page was retrieved by any of four searches; the archive appears poorly indexed and the Zulip API was not queried | — | four web searches | 2026-08-16 | + +## Open questions + +- Does the adopted form hold over a **real** Hilbert space? No corpus source + treats it, and neither a proof nor a counterexample was found. +- (A11): for which density characters between $\aleph_1$ and $2^{\aleph_0}$ does + (R15) fail? The argument on file settles only $> 2^{\aleph_0}$. +- (X9) is tier (d) as a whole because its σ-strong-⋆ branch consumes an + unretrieved external (the infinite-ampliation commutant identity). Grounding + it needs either that identity or a self-adjoint form of Kaplansky density, + which HIA20 does not state. +- Whether von Neumann's paper is dated 1929 or 1930 cannot be settled here: the + corpus's two dates attach to bibliographically identical data, so there is one + paper in play, and only the original could say whether 1929 is a submission + year against a 1930 issue. + +## Sources + +**Append-only.** + +| Key | Work | Status | Cache | Version | Tier reached | Retrieved | +|---|---|---|---|---|---|---| +| SHI12 | V. Shirbisheh, *Lectures on C\*-algebras*, arXiv:1211.3404 | retrieved | `references/arxiv-1211.3404/` | arXiv | a | 2026-08-16 | +| LAN98 | N. P. Landsman, *Lecture Notes on C\*-algebras, Hilbert C\*-modules and Quantum Mechanics*, arXiv math-ph/9807030 | retrieved | `references/arxiv-math-ph-9807030/` | arXiv | a | 2026-08-16 | +| HIA20 | F. Hiai, *Concise lectures on selected topics of von Neumann algebras*, arXiv:2004.02383 | retrieved | `references/arxiv-2004.02383/` | arXiv | a | 2026-08-16 | +| KOS13 | R. P. Kostecki, *W\*-algebras and noncommutative integration*, arXiv:1307.4818 | retrieved | `references/arxiv-1307.4818/` | arXiv | b (macros unavailable) | 2026-08-16 | +| SOR23 | J. Sorce, *Notes on the type classification of von Neumann algebras*, arXiv:2302.01958 | retrieved | `references/arxiv-2302.01958/` | arXiv | a | 2026-08-16 | +| NAA13 | P. Naaijkens, *Quantum spin systems on infinite lattices*, arXiv:1311.2717 | retrieved | `references/arxiv-1311.2717/` | arXiv | a | 2026-08-16 | +| HM06 | H. Halvorson, M. Müger, *Algebraic Quantum Field Theory*, arXiv math-ph/0602036 | retrieved | `references/arxiv-math-ph-0602036/` | arXiv | a | 2026-08-16 | +| VN30 | J. von Neumann, *Zur Algebra der Funktionaloperationen und Theorie der normalen Operatoren*, Math. Ann. 102 (1930) 370–427 | not retrieved — tried the GDZ id and mets endpoints (JavaScript-only shell, no METS served) and EUDML doc/159384 (HTTP 403); Springer paywall not pursued | — | — | c | 2026-08-16 | +| CON00 | J. B. Conway, *A Course in Operator Theory*, American Mathematical Society, 2000 | not retrieved | — | — | d | 2026-08-16 | +| PED79 | G. K. Pedersen, *C\*-algebras and their Automorphism Groups* | not retrieved | — | — | b (method attribution only) | 2026-08-16 | +| MUR90 / DIX77 / TAK79 / BLA06 | the standard C\*-algebra and operator-algebra textbooks | not retrieved | — | — | c | 2026-08-16 | + +## Not investigated + +- **The `[ext: …]` edges lane 5 never reached.** Formalization status was + measured for Kaplansky density, the spectral theorem, the Borel functional + calculus, Sakai's characterisation, the reduction/direct-integral theorem and + the σ-topologies. It was **not** measured for: the single-generator theorem; + the Dixmier and Conway pointers; Pedersen's proof strategy; Wedderburn's + decomposition result; Murray–von Neumann 1936; Bratteli–Robinson's + infinite-region statement; the infinite-ampliation commutant identity that (X9) consumes; and + the identification $C[0,1]'' = L^\infty[0,1]$ that the commutative-case row + uses. +- **Sources listed as not retrieved, and what rests on them.** VN30 carries the + attribution of the theorem itself and of (R15); every row citing it is (c) and + carries a statement instead of a locator. CON00 carries SOR23's only proof + pointer. PED79 carries SHI12's method attribution — nothing in SHI12's proof + depends on it for correctness. The textbook row is why no note in this + repository may carry a locator into any of them. +- **Degeneracy checklist items skipped:** none. The real-scalar case is filed + `open` rather than skipped. +- **Variants sighted and not pursued.** KOS13's relative commutant inside an + arbitrary ambient algebra is recorded at (D9) but not developed — the note + treats only the case where the ambient algebra is $B(H)$. KOS13's later + sections (spatial derivatives, Connes cocycles) and HIA20's later chapters + were not swept for restatements of the theorem carrying different hypotheses. +- **Internal edges never opened**, so the floor of the proof readings is: SHI12's + Hahn–Banach separation for locally convex spaces, its Riesz representation for + bounded sesquilinear forms, its Cauchy–Schwarz equality case, and two of its + remarks; LAN98's decomposition into self-adjoint parts and its refined + Gelfand–Naimark decomposition. (R6), (R3)(v) and (R4) therefore have + unverified floors. +- **The unexamined base.** Everything above stands on: the tier (c) attribution + of the theorem to VN30, which no one here has opened; the tier (d) row (X9), + which is the only bridge between the corpus's WOT/SOT framework and its + σ-topology framework; the tier (b) reading (R2) that the corpus proof + establishes the density form, which no source states; the tier (b) status of + every mathematical reading of a KOS13 quote, whose macros cannot be + reconstructed from the arXiv package; and, for the prior-art rows, a + statement-level measurement of this repository that was never elaborated by + `lake build`. diff --git a/docs/math/causal-index-set.md b/docs/math/causal-index-set.md new file mode 100644 index 0000000..89ff782 --- /dev/null +++ b/docs/math/causal-index-set.md @@ -0,0 +1,1021 @@ +--- +object: Causal index set of a Haag–Kastler net +slug: causal-index-set +status: draft +worst-tier: b +mathlib-rev: 5450b53e5ddc75d46418fabb605edbf36bd0beb6 +implemented-as: CausalIndexSet +revisions: + - 2026-08-15 · 4ec09ec · initial extraction · sources: HM06, GLRV99, NAA13, BGL93, BFV01, KOE03, dB74, DL84 + - 2026-08-15 · <working tree, uncommitted> · prior-art refresh after CausalIndexSet mixin addition · no new claims +--- + +<!-- +No document-level macro preamble: measured through +@vscode/markdown-it-katex (the plugin VS Code's own Markdown preview uses), +no macro definition form -- \newcommand, \gdef, \global\def -- survives +from one math span to the next, so a preamble here would leave every quote +using it broken (see check_render.py / render_check.js). Instead, every +verbatim-quote math span that needs a source's own macro carries a local, +self-contained \gdef of exactly that macro, e.g. +`$\gdef\lok#1{{\mathcal #1}}\lok{B}$` -- defined and used inside the same +$...$ pair, so it renders correctly without any state surviving to the next +span. The \gdef prefix is presentation, not content: strip it before +comparing a quote's tex against source.flat.txt for the quote check, and +audit it against the source's own definition below. + +Source macro catalogue (name[arity] = body, source, file:line): + \2[1] = {{\mathcal #1}} (provenance: see prior revision / sources.md) + \7[1] = {{\mathbb #1}} (provenance: see prior revision / sources.md) + \A = {{\cal A}} BGL93, references/arxiv-funct-an-9302008/raw/main.tex:58,64,72,77,81 + \C = {{\cal C}} BGL93, references/arxiv-funct-an-9302008/raw/main.tex:58,64,72,77,81 + \K = {{\cal K}} BGL93, references/arxiv-funct-an-9302008/raw/main.tex:58,64,72,77,81 + \O = {{\cal O}} BGL93, references/arxiv-funct-an-9302008/raw/main.tex:58,64,72,77,81 + \Om = {\Omega} (provenance: see prior revision / sources.md) + \R = {{\cal R}} BGL93, references/arxiv-funct-an-9302008/raw/main.tex:58,64,72,77,81 + \Seins = {\mathsf{S}^1} KOE03, references/arxiv-math-ph-0308031/raw/mathphkoediss.tex:144,135 + \al[1] = {{\mathfrak #1}} (provenance: see prior revision / sources.md) + \alg[1] = {\mathfrak{#1}} (provenance: see prior revision / sources.md) + \cA = {{\cal A}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cB = {{\cal B}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cH = {{\cal H}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cK = {{\cal K}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cL = {{\cal L}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cO = {{\cal O}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cP = {{\cal P}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cS = {{\cal S}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cW = {{\cal W}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \cX = {{\cal X}} GLRV99, references/arxiv-math-ph-9906019/raw/main.tex:69,70,76,79,80,83,85,88,92,93 + \calA = {{\mathcal A}} BFV01, references/arxiv-math-ph-0112041/raw/main.tex:7,26,31,89,82 (FIRST document body) + \calK = {{\mathcal K}} BFV01, references/arxiv-math-ph-0112041/raw/main.tex:7,26,31,89,82 (FIRST document body) + \frakM = {{\mathfrak{Man}}} BFV01, references/arxiv-math-ph-0112041/raw/main.tex:7,26,31,89,82 (FIRST document body) + \gb = {\boldsymbol{g}} BFV01, references/arxiv-math-ph-0112041/raw/main.tex:7,26,31,89,82 (FIRST document body) + \lok[1] = {{\mathcal #1}} KOE03, references/arxiv-math-ph-0308031/raw/mathphkoediss.tex:144,135 + \mc[1] = {\mathcal{#1}} NAA13, references/arxiv-1311.2717/raw/qlattice.tex:73 + \norm[1] = {\| #1\|} (provenance: see prior revision / sources.md) + \obj = {\mbox{\rm Obj}} BFV01, references/arxiv-math-ph-0112041/raw/main.tex:7,26,31,89,82 (FIRST document body) + \ol[1] = {{\overline #1}} (provenance: see prior revision / sources.md) + \p = {\pi} GLRV99, main.tex:127 + \wt[1] = {{\tilde #1}} (provenance: see prior revision / sources.md) +--> + +# Causal index set of a Haag–Kastler net + +## What this object is for + +A Haag–Kastler net assigns algebras to spacetime regions, and every axiom that +makes the assignment physics rather than bookkeeping — locality, duality, the +DHR selection criterion, the split property — is a statement about *which +regions stand in which relation to which*. The index set is where that +information lives: a partially ordered set of regions carried with a relation of +causal disjointness, and, for the independence properties, one or more +strengthenings of that relation. Its generality matters because the corpus's +main structural results split precisely along its axes — whether the order is +directed decides whether a quasi-local algebra exists at all, and which +strengthening of causal disjointness is in force decides whether an +independence theorem applies to a given pair of regions. + +## Definition + +### Variants as the sources write them + +| (D#) | Source | Level | Primitive | Membership conditions | Directed | ∅ ∈ 𝒦 | Tier | +|---|---|---|---|---|---|---|---| +| (D1) | [GLRV99] | abstract | relation ⊥ on a poset | a), b), c) | not required | not excluded | a | +| (D2) | [GLRV99] | concrete | ⊥ on subsets of M | regular diamond; `𝒪^⊥ ≠ ∅` | not in general; never if the Cauchy surface is compact | no | a | +| (D3) | [GLRV99] | pointwise | ⊥ on points of M | — (neighbourhood-buffer definition) | — | — | a | +| (D4) | [GLRV99] | operation | `Q^⊥ := M ∖ J̄(Q)` | — | — | — | a | +| (D5) | [GLRV99] | derived relations | ⊥̃, ⊥̂ from ⊥ | — | — | — | a | +| (D6) | [HM06] | concrete | "spacelike separated", primitive | open double cone in Minkowski | yes (Minkowski) | no | a | +| (D7) | [HM06] | strengthened relation | translation buffer | — | — | — | a | +| (D8) | [HM06] | strengthened relation | closures + enlargement | — | — | — | a | +| (D9) | [HM06] | separation for split | `Ō₁ ⊆ O₂` | — | — | — | a | +| (D10) | [NAA13] | concrete | "spacelike separated", primitive | double cone; records `𝒪 = 𝒪″` | yes | no | a | +| (D11) | [NAA13] | concrete, discrete | disjointness | finite subset of a countable Γ | yes | **yes** | a | +| (D12) | [BGL93] | concrete ×2 | causal complement | `𝒦̃`: open contractible precompact, closed under `′`; `𝒦`: double cones | `𝒦̃` no, `𝒦` yes | no | a | +| (D13) | [BGL93] | operation on S¹ | `I′ =` interior of the complement | — | — | — | a | +| (D14) | [KOE03] | concrete | operation `I ↦ I′` | connected, open, non-dense; `I′ ≠ ∅` | **no** | no | a | +| (D15) | [KOE03] | separation for split | `Ī₁ ⊂ I₂` | — | — | — | a | +| (D16) | [BFV01] | concrete | closure-based ⊥ | relatively compact, causally convex | not addressed | yes, read literally | a/b | +| (D17) | [BFV01] | operation | `O^⊥ =` largest causally separated open set | — | — | — | a | +| (D18) | [dB74] | separation ×3 | translation buffer / closure / positive distance | open, `𝒪′ ≠ ∅` | — | no | b | +| (D19) | [GLRV99] | concrete, net-dependent | ⊥ on sieves | causally closed, `𝒮^⊥ ≠ ∅`, ⊥̃-duality for `𝒮` or `𝒮^⊥` | — | no | a | +| (D20) | [GLRV99] | concrete, mixed | ⊥ across two families | regular diamonds ∪ wedges | — | no | a | + +**(D1) [GLRV99] §3.1, `source.txt` 1214–1226** — tier (a) — the abstract form + +> The causal structure enters in the form of the relation $\perp$ of causal disjointness, defined in Ch.\ 2, and here to be considered as a relation on the ordered set $\cal{K}$, satisfying \begin{description} \item{$a)$} $\gdef\cO{{\cal O}}\cO_1\perp\cO_2\Rightarrow\cO_2\perp\cO_1$. \item{$b)$} $\gdef\cO{{\cal O}}\cO_1\subset\cO_2$ and $\gdef\cO{{\cal O}}\cO_2\perp\cO_3 \Rightarrow\cO_1\perp\cO_3$. \item{$c)$} Given $\gdef\cO{{\cal O}}\gdef\cK{{\cal K}}\cO_1\in\cK$, there exists an $\gdef\cO{{\cal O}}\gdef\cK{{\cal K}}\cO_2\in\cK$ such that $\gdef\cO{{\cal O}}\cO_1\perp\cO_2$. \end{description} We write $\gdef\cO{{\cal O}}\gdef\cK{{\cal K}}\cO^\perp:=\{\cO_1\in\cK:\cO_1\perp\cO\}$. + +Restated at the head of the Appendix to Ch. 3 with the gloss that b) says +`$\cal{O}^\perp$ is a sieve of $\cal{K}$`. Here `𝒪^⊥` is a **subset of 𝒦**, not +a region — the opposite convention from (D4); see (C4). The abstraction is +deliberate: + +> Our discussion of superselection theory in this and in subsequent sections is in terms of a partially ordered set $\cal{K}$ together with a binary relation $\perp$. The necessary properties will be introduced as needed and there will be no specific reference to spacetime. + +**The list is therefore a floor, not a ceiling, and the source says so.** A +fourth condition appears later in the same chapter and is recorded as (D5′). + +**(D2) [GLRV99] §2.1 and §3.1** — tier (a) — regular diamonds + +> A set of the form $\gdef\cO{{\cal O}}\cO = {\rm int}\,D(G)$ is a regular diamond provided $\gdef\cO{{\cal O}}\cO^\perp$ is non-void and + +with (i) `Ḡ` compact and contractible to a point in `G`, `G` open in an acausal +Cauchy surface `C`; (ii) `∂G` a locally flat, two-sided topological +submanifold of `C`, smooth near points of each connected component. Then +`We let $\gdef\cK{{\cal K}}\cK$ denote the set of regular diamonds in $M$, ordered under inclusion.` +Non-void causal complement is **in the definition**, which is how axiom c) is +secured. GLRV99 declines double cones and says why: + +> For these reasons, we have chosen to use the collection $\gdef\cK{{\cal K}}\cK$ of regular diamonds rather than the collection of double cones whose causal complement has non-empty interior as an index set in a globally hyperbolic spacetime. + +`differs from (D6)/(D10) by:` a Cauchy-surface base with compactness, +contractibility and a two-sidedness condition, plus `𝒪^⊥ ≠ ∅`. Note the source +uses two different provisos in one sentence — `𝒪^⊥` **non-void** for diamonds, +**non-empty interior** for the declined double cones — and does not reconcile +them. + +**(D3) [GLRV99] §2.1** — tier (a) — causal disjointness of points, with a buffer + +> One says that two points $p$ and $q$ in $M$ are {\it causally disjoint}, in symbols $p \perp q$, if there are open neighbourhoods $U$ of $p$ and $V$ of $q$ such that there is no causal curve connecting $U$ and $V$ (i.e.\ $U \cap J(V) = \emptyset = V \cap J(U)$). Correspondingly one calls two subsets $P$ and $Q$ of $M$ causally disjoint if $p \perp q$ holds for all pairs $p \in P$ and $q \in Q$; this will be abbreviated as $P \perp Q$. + +The **open-neighbourhood buffer** makes this strictly stronger than "no causal +curve joins `p` and `q`", and (R4)'s proof consumes exactly that strength. As +defined, `p ⊥ p` is impossible, so `P ⊥ P` fails for every non-empty `P` and +holds vacuously for `P = ∅`. + +`differs from (D17) by:` GLRV99 buffers with neighbourhoods and quantifies over +points of `P`, `Q`; BFV01 uses no buffer and quantifies over points of the +**closures**. **The corpus contains two inequivalent ⊥'s and no source compares +them.** + +**(D4) [GLRV99] §2.1** — tier (a) — the causal complement as an operation on subsets + +> It is moreover worth mentioning that for any two subsets $P$ and $Q$ of a globally hyperbolic spacetime $(M,g)$ we have $P \perp Q$ if and only if $P \subset Q^{\perp}$, where the causal complement $Q^{\perp}$ of $Q \subset M$ is defined by $Q^{\perp} := M \backslash \overline{J(Q)}$ + +Complement of the **closure** of the causal hull, hence automatically open. + +**(D5) [GLRV99] Appendix to Ch. 3** — tier (a) — the two derived relations + +> There are two derived binary relations $\tilde\perp$ and $\hat\perp$ defined by supplementing $\gdef\cO{{\cal O}}\cO_1\perp\cO_2$ by requiring that there exists an $\gdef\cO{{\cal O}}\cO_3\in\cal{K}$ such that $$\gdef\cO{{\cal O}}\cO_1\perp\cO_3,\,\,\cO_2\perp\cO_3$$ or such that $$\gdef\cO{{\cal O}}\cO_1,\,\,\cO_2\subset\cO_3,$$ respectively. These relations automatically satisfy a) and b) but c) remains to be checked and will not prove to be a problem in our applications to curved spacetime. The operation of passing from $\perp$ to $\tilde\perp$ or $\hat\perp$ is idempotent and if $\cal{K}$ is directed, all three relations coincide. + +So `O₁ ⊥̃ O₂` demands a common ⊥-partner and `O₁ ⊥̂ O₂` a common upper bound. +Both are **purely order/⊥-theoretic**: neither mentions closures or distance. +The whole ⊥/⊥̃/⊥̂ distinction is the price of dropping directedness — see (R9). + +**(D5′) [GLRV99] Ch. 3, before Theorem 3.13, `source.txt` 1878–1882** — tier (a) +— the corpus's only collar-shaped condition on 𝒦 itself + +> In fact, the following result is valid for a directed set $\cal{K}$ with a binary relation $\perp$ such that given $\cal{O}\in\cal{K}$, there exists $\gdef\cO{{\cal O}}\cO_1,\cO_2\in\cal{K}$ with $\gdef\cO{{\cal O}}\cO,\cO_1\subset\cO_2$ and $\gdef\cO{{\cal O}}\cO\perp\cO_1$. This condition is related to our use of the Borchers Property. + +For every `O` there are `O₁, O₂` with `O ⊆ O₂`, `O₁ ⊆ O₂` and `O ⊥ O₁` — in the +vocabulary of (D5), **every `O` has an `O₁` with `O ⊥̂ O₁`**, i.e. axiom c) +strengthened from ⊥ to ⊥̂. Stated as a hypothesis of one theorem alongside +directedness, not as a standing axiom. + +`differs from (D5) by:` (D5) is a relation *between two given regions*; this is +a **∀∃ statement about 𝒦**. Every other collar in the corpus is of the former +kind or is topological ((D8), (D9)). **No source compares the two shapes.** + +**(D6) [HM06] §2.1** — tier (a) — open double cones in Minkowski + +> An open \emph{double cone} in Minkowski spacetime is the intersection of the causal future of a point $x$ with the causal past of a point $y$ to the future of $x$. Let $\gdef\2#1{{\mathcal #1}}\2K$ be the set of open double cones in Minkowski spacetime + +No poset axioms are stated; the order is inclusion, used silently. **HM06 never +defines "spacelike separated"** — it is an undefined primitive from the +Microcausality assumption onward, and the causal complement is written `O'` with +no formula. See (C1). + +`differs from (D2) by:` no Cauchy-surface base, no regularity, and **no +requirement that the causal complement be non-void**. + +**(D7) [HM06] §3.3** — tier (a) — strictly spacelike separated + +> Two double cones $O_1,O_2$ are said to be \emph{strictly spacelike separated} just in case there is a neighborhood $N$ of zero such that $O_1+x$ is spacelike separated from $O_2$ for all $x\in N$. + +A translation-buffer condition; **requires a translation group**. + +**(D8) [HM06] §3.3** — tier (a) — strongly spacelike separated + +> Two double cones $O_1$ and $O_2$ are said to be \emph{strongly spacelike separated} just in case there are double cones $\gdef\wt#1{{\tilde #1}}\wt O_i$ such that $\gdef\ol#1{{\overline #1}}\gdef\wt#1{{\tilde #1}}\ol O_i\subseteq \wt O_i$, and $\gdef\wt#1{{\tilde #1}}\wt O_1,\wt O_2$ are spacelike. + +with the ordering claim + +> In terms of logical strength, the following concept lies between spacelike separation and strict spacelike separation; furthermore, this concept makes sense for spacetimes without a translation group. + +`differs from (D5) by:` the shapes are not the same. (D5)'s clauses ask for +**one** further region related to both by ⊥ or by ≤; (D8) asks for **two** +enlargements, one per region, each containing the region's **closure**. (D8) +uses the topological closure, **which is not available in (D1)'s data**, so +"strongly spacelike separated" is not expressible in GLRV99's abstract language. +`sources claim equivalence:` no — no source in the corpus mentions the other's +relation. + +**(D9) [HM06] §2.4** — tier (a) — the separation carrying the split property + +> the \emph{funnel property} if for any double cones $O_1,O_2$ with $\gdef\ol#1{{\overline #1}}\ol O_1$ contained in $O_2$, the pair $\gdef\al#1{{\mathfrak #1}}(\al R(O_1),\al R(O_2))$ is a split inclusion. + +The relation is `closure(O₁) ⊆ O₂`. **HM06 gives it no name and no symbol.** +Purely topological: no causal structure, no metric, no positive distance. + +**(D10) [NAA13] §1.2** — tier (a) — double cones, recorded as causally complete + +> As the basic regions we consider \emph{double cones}\index{double cone} $\gdef\mc#1{\mathcal{#1}}\mc{O}$, defined as the intersection of (the interior of) a forward and backward light-cone. Note that a double cone is causally complete: $\gdef\mc#1{\mathcal{#1}}\mc{O} = \mc{O}''$, where a prime $'$ denotes taking the causal complement. + +`differs from (D6) by:` the same family, presented as an intersection of cones +rather than through two points, **plus the causal-completeness observation HM06 +never makes**. NAA13 is the corpus's only source stating `𝒪 = 𝒪″`, and states it +as an observation, not as a membership condition — contrast (D19). + +**(D11) [NAA13] §2.4** — tier (a) — the lattice index set, with `∅` a member + +> Let $\Gamma$ be as above. We will write $\gdef\mc#1{\mathcal{#1}}\mc{P}(\Gamma)$ for the set of all subsets of $\Gamma$. Similarly, $\gdef\mc#1{\mathcal{#1}}\mc{P}_f(\Gamma)$ is the subset of all \emph{finite} subsets of $\Gamma$. + +> For convenience we will set $\gdef\alg#1{\mathfrak{#1}}\alg{A}(\emptyset) = \mathbb{C} I$, since multiples of the identity are contained in $\gdef\alg#1{\mathfrak{#1}}\alg{A}(\Lambda)$ for all $\gdef\mc#1{\mathcal{#1}}\Lambda \in \mc{P}_f(\Gamma)$. + +Finite subsets of a countable Γ, ordered by inclusion, ⊥ = plain disjointness. +**Directed**, with a **least element ∅**, a distributive lattice, and its +complement operation `Λ^c` leaves 𝒦. GLRV99 licenses exactly this replacement of +causal disjointness by "its Euclidean counterpart, disjointness". Because +`∅ ∈ 𝒦`, **⊥ is reflexive at ∅** and axiom c) holds for free — see (C11), (H1). + +**(D12) [BGL93] §1, Prop. 1.3** — tier (a) — two index sets in one paper + +> In the following we shall consider the family $\gdef\K{{\cal K}}\tilde\K$ of the subregions of $\tilde M$ which are images of double cones in $M$ under conformal transformations in $\gdef\C{{\cal C}}\tilde\C$. + +> All elements of $\gdef\K{{\cal K}}\tilde\K$ are open contractible precompact submanifolds of $\tilde M$. They are a fundamental set of neighborhoods for $\tilde M$. + +> The space-like complement $\gdef\O{{\cal O}}\O'$ of a region $\gdef\O{{\cal O}}\gdef\K{{\cal K}}\O\in\tilde\K$ belong to $\gdef\K{{\cal K}}\tilde\K$. + +> The family $\gdef\K{{\cal K}}\tilde\K$ is not a net, in fact the union of a region and of its causal complement is not contained in any region of $\gdef\K{{\cal K}}\tilde\K$. + +and, for the same paper's other index set, + +> Since the family $\gdef\K{{\cal K}}\K$ is a direct set, the map $\gdef\O{{\cal O}}\gdef\A{{\cal A}}\O\to\A(\O)$ is indeed a net and the quasilocal $C^*$-algebra $\gdef\A{{\cal A}}\A_0$ is defined as the direct limit of the local algebras. + +**`𝒦̃` is the corpus's only index set literally closed under the causal +complement**, so on it `𝒪 ↦ 𝒪′` is an *operation*; on every other index set it +is only a relation. One paper, two index sets, opposite on directedness and +opposite on closure under `′`. + +**(D13) [BGL93] §2** — tier (a): `where $I'$ is the interior of the complement of $I$.` +`differs from (D14) by:` presentation only — interior-of-complement versus +complement-of-closure. The two sources write different formulas for the same set +and neither remarks on it. + +**(D14) [KOE03] §2** — tier (a) — proper intervals of the circle + +> The localisation regions are open, non-dense intervals contained in the circle, called the {\em proper intervals}. A connected, open subset $I$ of $\gdef\Seins{\mathsf{S}^1}\Seins$ is a proper interval, denoted by $\gdef\Seins{\mathsf{S}^1}I\Subset\Seins$, if its {\em causal complement} $\gdef\Seins{\mathsf{S}^1}I':= \Seins\setminus\overline{I}$ is not the empty set. + +Membership requires connected, open, non-dense, and `I′ ≠ ∅` — as in (D2) and +unlike (D6)/(D10). Locality is stated as `{\em Locality\label{ax:loc}:} For $I_1\subset I_2'$,` +so **the primitive is the operation `I ↦ I′`** and ⊥ is derived, the reverse of +(D1). Interchangeable only because 𝒦 is closed under `′` here. The index set is +not directed and the source draws the terminological consequence: + +> The set of proper intervals in $\gdef\Seins{\mathsf{S}^1}\Seins$ is not directed with respect to the partial order defined by inclusion and thus is not a net in the proper sense of the word. + +**(D15) [KOE03] §1.3** — tier (a) — the chiral split separation + +> a chiral net $\gdef\lok#1{{\mathcal #1}}\lok{B}$ has the split property, if for any pair $I_{1,2}$ of proper intervals satisfying $\overline{I_1}\subset I_2$ there is a type $I$ factor $\gdef\lok#1{{\mathcal #1}}\lok{M}$ interpolating between $\gdef\lok#1{{\mathcal #1}}\lok{B}(I_1)$ and $\gdef\lok#1{{\mathcal #1}}\lok{B}(I_2)$ + +`differs from (D9) by:` **nothing mathematical** — two traditions, one relation, +and neither names it or gives it a symbol. KOE03 also takes the one step in the +corpus connecting the topological and order-theoretic collar families: + +> we conclude that there is $\gdef\Seins{\mathsf{S}^1}I_3\Subset\Seins$ satisfying $I_1\cup I_2' \subset I_3$ + +i.e. `Ī₁ ⊂ I₂` yields a common upper bound for `I₁` and `I₂′`, which with +`I₁ ∩ I₂′ = ∅` says exactly `I₁ ⊥̂ I₂′`. + +**(D16) [BFV01] §2.4, first document body** — tier (a) literal / (b) as intended + +> We denote by $\gdef\calK{{\mathcal K}}\gdef\gb{\boldsymbol{g}}\calK(M,\gb)$ the set of all subsets in $M$ which are relatively compact and contain with each pair of points $x$ and $y$ also all $\gdef\gb{\boldsymbol{g}}\gb$-causal curves in $M$ connecting $x$ and $y$ (cf.\ condition $(ii)$ in the definition of $\gdef\frakM{{\mathfrak{Man}}}\frakM$). + +Two conditions only: **relatively compact** and **causally convex**. Read +literally, `∅`, singletons and spacelike point-pairs are members. The next +sentence silently demands far more — each `(O, ḡ_O)` must lie in `obj(𝔐)`, whose +manifolds are `Hausdorff, paracompact, and connected` — so the intended index set +is open, connected, non-empty and globally hyperbolic. **The two readings give +different index sets**, and the inference to the second is tier (b). + +**(D17) [BFV01] §2.1, first document body** — tier (a) — closure-based ⊥ + +> Two subsets $O_1$ and $O_2$ in $M$ are called causally separated if they cannot be connected by a causal curve, i.e.\ if for all $x \in \overline{O_1}$, $J^{\pm}(x)$ has empty intersection with $\overline{O_2}$. By $O^{\perp}$ we denote the causal complement of $O$, i.e.\ the largest open set in $M$ which is causally separated from $O$. + +**BFV01's plain "causally separated" already excludes touching regions**, unlike +(D3) and (D14). `O^⊥` is given by a maximality characterisation with **no +set-theoretic formula**, unlike (D4) and (D14). + +**(D18) [dB74] Ch. II–III** — tier (b), `mineru-unchecked` except where noted — +three separation shapes in one paper + +- *translation buffer*, Thm 2.2: + > Let $\hat{O}_{1}$ and $\hat{O}_{2}$ be two spacelike separated regions such that $O_{1} + N \subset \hat{O}_{1}$ and $O_{2} + N \subset \hat{O}_{2}$ . +- *closure*, Ch. II item a), **p. 292** (tier (b), + `mineru-cross-checked-against-PDF-text-layer`): + > If the closures of the regions $O_{1}$ and $O_{2}$ are not spacelike separated, then, at least for the free field, it is easy to show that one runs into contradictions if one postulates the existence of normal product states for such regions. + **The corpus's only statement that the closure relation is *necessary*.** +- *positive distance*, Ch. III: + > Both regions shall have smooth boundaries and the distance between $O_{1}$ and $O_{2}$ is supposed to be greater than zero. + +dB74's index set carries openness and `𝒪′ ≠ ∅`, stated as the scope of its +Reeh–Schlieder assumption rather than as a definition. + +`differs from (D7) by:` HM06 translates one region and asks it stay spacelike to +the other; dB74 translates a region and asks it stay **inside a larger region**. +Same buffer idea, two different relations. + +**(D19) [GLRV99] Appendix Ch. 3 and §4.2** — tier (a) — an index set that depends on the net + +> We choose $\gdef\cL{{\cal L}}\cL$ to be the set of non-empty causally closed subsets $\gdef\cS{{\cal S}}\cS$ of $M$ with non-empty causal complements such that for the given net $\gdef\cA{{\cal A}}\cA$ $\tilde\perp$--duality holds either for $\gdef\cS{{\cal S}}\cS$ or for $\gdef\cS{{\cal S}}\cS^\perp$. + +> This choice has the disadvantage of depending on the theory under consideration but it allows a smooth treatment of endomorphisms. + +`differs from every other (D#) by:` **the index set is a function of the net.** +Causal closedness `𝒮 = 𝒮^⊥⊥` is here a *membership condition*, where (D10) +records it as an observation. + +**(D20) [GLRV99] Ch. 5** — tier (a) — diamonds together with wedges + +> Now we consider a net $\gdef\cO{{\cal O}}\gdef\cA{{\cal A}}\cO \mapsto\cA(\cO)$ of von~Neumann algebras indexed by elements $\gdef\cO{{\cal O}}\gdef\cK{{\cal K}}\gdef\cW{{\cal W}}\cO \in \cK \cup \cW$ where $\gdef\cK{{\cal K}}\cK$ is the set of regular diamonds and $\gdef\cW{{\cal W}}\cW$ is a set of wedges with the properties discussed in the previous section + +The corpus's only index set containing **unbounded** elements as first-class +members, and the only one that is not a single geometric family. A form that +builds boundedness into 𝒦 cannot state GLRV99's Ch. 5 hypotheses. + +### Adopted general form + +Fix a partially ordered set `𝒦`, whose elements are called *regions*, and a +binary relation `⊥` on `𝒦`, read *causally disjoint*. The pair `(𝒦, ⊥)` is a +**causal index set** when + +1. `O₁ ⊥ O₂` implies `O₂ ⊥ O₁`; +2. `O₁ ≤ O₂` and `O₂ ⊥ O₃` imply `O₁ ⊥ O₃` — equivalently, `O^⊥ := {O₁ ∈ 𝒦 : O₁ ⊥ O}` is downward closed in `𝒦` for every `O`; +3. for every `O₁ ∈ 𝒦` there exists `O₂ ∈ 𝒦` with `O₁ ⊥ O₂`. + +There are no further hypotheses: the order is **not** assumed directed, `⊥` is +**not** assumed irreflexive, `𝒦` carries **no topology** and no +causal-complement operation into itself, no element is assumed causally complete, +no region is assumed open, bounded or non-empty, and `𝒦` may be empty. Standing +hypotheses of the sources that this form deliberately does not carry are (A1) +directedness, (A7) openness and path-connectedness, (A10) causal completeness and +(A11) global hyperbolicity of an ambient spacetime, each of which is a row in +`## Hypotheses` rather than part of the definition. + +$$ +\text{(a) } O_1 \perp O_2 \Rightarrow O_2 \perp O_1,\qquad +\text{(b) } O_1 \le O_2,\ O_2 \perp O_3 \Rightarrow O_1 \perp O_3,\qquad +\text{(c) } \forall O_1\, \exists O_2,\ O_1 \perp O_2 . +$$ + +This is (D1), verbatim [GLRV99] §3.1 up to notation. It is the corpus's only +abstract axiomatisation, it is stated twice and with a declared motive, and every +concrete index set in the corpus is an instance of it — double cones, regular +diamonds, proper intervals of `S¹`, `𝒦̃`, `𝓛`, `𝒦 ∪ 𝒲`, wedges and `𝒫_f(Γ)` were +each checked against a), b), c). (X18) rejects the alternative of taking the +causal complement as an **operation into 𝒦**: the separating object is a double +cone in Minkowski, whose ⊥-partners have no maximum, while `𝒦̃` and the proper +intervals do support the operation; the sieve-valued operation +`O ↦ O^⊥ ⊆ 𝒦` is `equivalent` and is GLRV99's own notation. (X19) rejects adding +causal completeness as an axiom: a three-element antichain satisfies a), b), c) +with `B^⊥⊥ ⊋ ↓B`. (X25)–(X27) reject the quantifier and heredity variants. + +**Three costs of this choice, recorded rather than left to be rediscovered.** + +- **(H1)** Axiom c) does not deliver what the corpus uses it for. Adjoin a least + element `0` to *any* poset and set `⊥ := {(0,O),(O,0)}`; a), b), c) all hold. + So every poset underlies a causal index set and the form has **no + order-theoretic content**. In particular a form omitting irreflexivity may not + be credited with (X3)'s consequence that comparable regions are never ⊥, nor + with (X6)'s that `𝒦` is not a chain — both need irreflexivity, which is + independent of a), b), c). GLRV99's own text warns that the list is a floor. +- **(H2)** [BFV01]'s primary object is a **category** of spacetimes with + isometric embeddings, whose causality condition quantifies over pairs of + morphisms into a common spacetime. This form covers BFV01's *derived* net over + `𝒦(M,ḡ)` and not BFV01's theory. +- **(T) is not expressible.** The hypothesis under which the two candidate + separation relations for the split property are ordered — see below — is + topological, and a bare poset with ⊥ cannot state it. This is the same + obstruction as (D8)'s. + +**The separation relation for the split property is not adopted as a single +relation.** The corpus carries at least six phrasings — (D5)'s ⊥̃ and ⊥̂, (D5′), +(D7), (D8), (D9)/(D15), and (D18)'s three — and states exactly one implication +between any two of them ([HM06]'s `fact`, (R25), asserted there and proved in +this note). What can be said is: + +**(T) — the causal complement abuts the region.** `Ō ∩ cl(O^⊥) ≠ ∅` for every +`O ∈ 𝒦`; equivalently `dist(Ō, O^⊥) = 0`; equivalently, there is no buffer +between a region and its causal complement. *No source states (T); it is this +note's, extracted from the case analysis below.* + +**Ordering theorem** (this note's, not any source's). Let `𝒦` be a base of +**non-empty open** subsets of a topological space `M`, let ⊥ be induced by an +operation `O ↦ O^⊥` into the open sets by `O₃ ⊥ O₁ ⟺ O₃ ⊆ O₁^⊥`, and let +`O ∩ O^⊥ = ∅`. If **(T)** holds then `Ō₁ ⊆ O₂` implies the collar clause +`∃O₃ ∈ 𝒦 : O₃ ≤ O₂, O₃ ⊥ O₁, O₃ ⊄ O₁`. + +*Proof.* By (T) pick `x ∈ Ō₁ ∩ cl(O₁^⊥)`. Since `Ō₁ ⊆ O₂` with `O₂` open, `O₂` +is a neighbourhood of `x`, so meets `O₁^⊥`; hence `O₂ ∩ O₁^⊥` is non-empty open +and, `𝒦` being a base, contains some `O₃ ∈ 𝒦`. Then `O₃ ≤ O₂`; `O₃ ⊆ O₁^⊥` +gives `O₃ ⊥ O₁`; and `O₃ ⊄ O₁` because `O₃ ⊆ O₁^⊥`, `O₁ ∩ O₁^⊥ = ∅` and +`O₃ ≠ ∅`. ∎ + +Compactness of `Ō₁` is not used; openness of `O₂` is. The implication is +**strict**: (X15) direction 2 gives internally tangent double cones satisfying +the collar clause and failing `Ō₁ ⊆ O₂`. (T) holds for double cones (the +intersection is the waist sphere), for regular diamonds (secured by GLRV99's +two-sidedness clause), for the proper intervals of `S¹` (the two endpoints) and +for `𝒦̃` (tier (b), by transporting the Minkowski computation along a conformal +map); it **fails** for `𝒫_f(Γ)`, where discreteness puts a buffer of width 1 +between a set and its complement, and for any index set containing a region with +empty causal complement. So the two forms are `ordered-under-(T)`, neither +`incomparable` nor `ordered`. + +## Notation and conventions + +| (C#) | Axis | This note | Per source | Translation | +|---|---|---|---|---| +| (C1) | is "spacelike separated" defined? | ⊥ is primitive, axiomatised | [GLRV99] (D3) and [BFV01] (D17) define it; **[HM06] and [NAA13] leave it undefined** and write the complement `O'` with no formula. tier (a)/(b) | none available: the two definitions that exist are inequivalent | +| (C2) | directedness of 𝒦 | not assumed | [HM06] and [GLRV99] both remark it may fail and say what is lost; [BGL93] and [KOE03] make it a terminological matter. tier (a) | (R1), (R19), (R16), (R40) | +| (C3) | causal vs plain disjointness | ⊥ abstract | [GLRV99] licenses replacing causal disjointness by "its Euclidean counterpart, disjointness" on the circle; [NAA13] (D11) does it on a lattice. tier (a) | on a discrete Γ the two coincide by fiat | +| (C4) | what `O^⊥` denotes | a subset of 𝒦 | [GLRV99] uses `𝒪^⊥` for **both** the sieve `{𝒪₁ ∈ 𝒦 : 𝒪₁ ⊥ 𝒪}` (D1) and the point set `M ∖ J̄(𝒪)` (D4), in one paper; [BFV01], [KOE03], [BGL93] use the point-set reading only. tier (a) | (R13): `μ`, `ν` are mutually inverse on causally closed sets and sieves, which is the precise sense in which the two readings agree | +| (C5) | openness of regions | not assumed | open in [GLRV99], [BGL93], [KOE03], [dB74]; unstated but automatic in [HM06], [NAA13]; **absent from [BFV01]'s literal definition** and forced by its next sentence. tier (a)/(b) | — | +| (C6) | irreflexivity of ⊥ | not assumed | **no source states it as an axiom**; forced for non-empty regions by (D3)'s pointwise definition; permitted by a), b), c); holds at `∅` in (D11). tier (a)/(b) | (H1); (X3) | +| (C7) | **[KOE03]'s `⋐` is a membership predicate, not containment** | — | `I ⋐ S¹` abbreviates "`I` is a proper interval of `S¹`"; the separation is written `\overline{I_1}\subset I_2`. tier (a) | **a reader who takes `I₁ ⋐ I₂` to mean `Ī₁ ⊆ I₂` reads the wrong relation into every axiom of that source**; the two symbols appear side by side in (D15) | +| (C8) | the word "net" | the map `O ↦ 𝒜(O)` | = **directed family** in [BGL93] and [KOE03], who therefore say `𝒦̃` and the proper intervals "are not a net"; = the map in [HM06], [NAA13], [GLRV99], [BFV01]. tier (a) | under the first reading, [BGL93] Prop. 1.3(v) is a statement about the *index set*, not the algebras | +| (C9) | extending the net outside 𝒦 | not adopted | three devices for `𝒜(𝒮)`: norm closure of the algebras inside `𝒮` ([NAA13], explicit); smallest C\*-algebra generated by them ([BFV01], for `S^⊥⊥`); additivity plus enlarging the index set ([GLRV99]); **undefined** ([HM06] writes `\al A(O')` with no definition). tier (a) | a *choice*, not a theorem; nothing in the corpus shows two devices agree | +| (C10) | boundedness | not assumed | compact base ([GLRV99]), precompact ([BGL93]), relatively compact ([BFV01]), finite ([NAA13]), **non-dense** ([KOE03]), automatic ([HM06]), **dropped** ([GLRV99] (D20)). tier (a) | non-dense on `S¹` is **not** a compactness condition — every subset of `S¹` is relatively compact — but is what makes the complement non-empty | +| (C11) | is `∅ ∈ 𝒦`? | not excluded | **in**: [NAA13]'s `𝒫_f(Γ)` with `𝒜(∅) = ℂI` (tier (a)); [BFV01] read literally (tier (b)). **out**: [GLRV99], [BGL93], [KOE03], [HM06], [dB74] — in every case as a by-product of the construction, never as a stated condition. tier (a)/(b) | `∅` is ⊥ to everything **including itself**, so admitting it makes ⊥ reflexive and satisfies c) for free — this is (H1)'s concrete form | + +## Results and dependencies + +### (R1) Directedness ⇒ the quasi-local algebra exists; without it the construction is unavailable + +- Source: [GLRV99] §1.1 · tier (a) · **asserted** +- Verbatim: + > If $\gdef\cK{{\cal K}}\cK$ is directed, then one can form the ``quasilocal algebra'', i.e.\ the smallest $C^*$-algebra containing all the local algebras $\gdef\cA{{\cal A}}\gdef\cO{{\cal O}}\cA(\cO)$. It is the norm closure of the union of the local algebras, $\gdef\cO{{\cal O}}\gdef\cA{{\cal A}}\overline{\bigcup_{\cO}\cA(\cO)}$. In the generic case where $\gdef\cK{{\cal K}}\cK$ is not directed, this possibility is denied to us. +- Depends on: (A1); [ext: the C\*-inductive-limit construction — the norm closure of an upward-directed union of C\*-subalgebras of a common ambient algebra is a C\*-algebra and is the smallest one containing them all. tier (d), not retrieved] +- This is why GLRV99's superselection apparatus is cohomological rather than algebraic. + +### (R2) Essential duality lies strictly between locality and Haag duality + +For a representation family, the dual net is `𝒜_π^d(𝒪) := ⋂_{𝒪₁ ⊥ 𝒪} π_{𝒪₁}(𝒜(𝒪₁))′`; +*essential duality* asks that it be local, *Haag duality* that it equal +`π_𝒪(𝒜(𝒪))″`. + +- Source: [GLRV99] §1.1 · tier (a) · **asserted** +- Verbatim: + > This property is stronger than locality but not as strong as Haag duality which demands that $\gdef\p{\pi}\gdef\cO{{\cal O}}\gdef\cA{{\cal A}}\p_{\cO}(\cA(\cO))'' = \cA_{\p}^d(\cO)$ for all $\gdef\cO{{\cal O}}\gdef\cK{{\cal K}}\cO \in \cK$. +- Depends on: (R7); the availability of `𝒪^⊥` as an index set, i.e. (D1) + +### (R3) [GLRV99] Lemma 2.1 — the regular diamonds absorb any point of the causal complement + +Let `𝒪` be a regular diamond and `p ∈ 𝒪^⊥`. Then some regular diamond `𝒪₁` +satisfies `𝒪 ∪ {p} ⊂ 𝒪₁`. + +- Source: [GLRV99] Lemma 2.1 · tier (a) · **sketched in source**, full proof cited elsewhere +- Depends on: (D2)'s regularity conditions; (A11); [ext: Brown — a compact, locally flatly embedded, two-sided topological submanifold boundary admits a collar neighbourhood, so the base of a regular diamond has an open neighbourhood in the same Cauchy surface with the same regularity. tier (d), not retrieved]; [ext: Verch — the detailed proof, and the existence of an acausal Cauchy surface through `p` meeting `J(Ḡ)` inside a regularity-preserving neighbourhood. tier (d), not retrieved] +- Proof route: (1) fix a smooth foliation into acausal Cauchy surfaces [global hyperbolicity, (A11)]; (2) enlarge `Ḡ` to a neighbourhood that is again a regular base [ext: Brown]; (3) find an acausal Cauchy surface through `p` with `J(Ḡ)` inside the transported neighbourhood [ext: Verch]; (4) attach a tubular neighbourhood of a curve to `p` [the two-sidedness clause of (D2)]. +- Step 3 is load-bearing and rests entirely on the unfetched reference. + +### (R4) [GLRV99] Lemma 2.2 — the causally disjoint pairs of points are pathwise connected, with one exception + +- Source: [GLRV99] Lemma 2.2 · tier (a) · **proved in source** +- Verbatim: + > ${\cal X}_{M,g}$ is pathwise connected except when its Cauchy surfaces are noncompact and 1--dimensional in which case there are precisely two path--components corresponding to $x$ being causally to the left or to the right of $y$. +- Depends on: (A11); (A2a); (D3) — the *neighbourhood-buffered* form of ⊥, consumed in step 2 and not replaceable by the naive reading; [ext: elementary homotopy theory — a strong deformation retract induces a bijection of path components. tier (d), not retrieved] +- Proof route: (1) build a homotopy retracting onto pairs in one Cauchy surface [the foliation, (A11)]; (2) check the image stays in `𝒳_{M,g}` — **this step consumes exactly (D3)'s open-neighbourhood buffer and fails for the naive reading of ⊥**; (3) conclude a bijection on path components [ext: homotopy theory]; (4) split by the topology of the Cauchy surface [(A2a)]. +- GLRV99 records `It may be known to experts, but as we have not found it in the literature, we put it on record here.`, so the corpus is this row's primary source. + +### (R5) [GLRV99] Lemma 3A.1 / Cor. 3A.2 — connectedness transported from the space to the poset + +- Source: [GLRV99] Lemma 3A.1, Cor. 3A.2 · tier (a) · **proved in source** (3A.1); **asserted** (3A.2) +- Verbatim: + > Let $\cal{P}$ be a base for the topology of a space $M$ and ordered under inclusion and suppose the elements of $\cal{P}$ are open, (non-empty) and path--connected. Then an open subset $X$ of $M$ is path--connected if and only if $\gdef\cP{{\cal P}}\cP_X$:=$\gdef\cO{{\cal O}}\gdef\cP{{\cal P}}\{\cO\in\cP:\cO\subset X\}$ is connected. +- Depends on: (A6); (A7) +- The bridge lemma of the whole apparatus: every connectedness claim GLRV99 makes about the *poset* is obtained by applying this to a topological one. + +### (R6) The poset of regular diamonds is connected, each `𝒪^⊥` is connected, and the graph of ⊥ is connected — except in two dimensions + +- Source: [GLRV99] §3.1 · tier (a) · **proved in source** as a two-step derivation +- Verbatim: + > By virtue of Lemma 3A.1, we know that $\cal{K}$ is connected and, see Lemma 2.2, that $\gdef\cO{{\cal O}}\cO^\perp$ is connected except when $M$ is two dimensional with a non--compact Cauchy surface. +- Depends on: (R4); (R5); (A6); (A2a) + +### (R7) The dual-net operation is an antitone Galois-type map + +`𝒜 ⊂ 𝒜^dd`; `𝒜 ⊂ ℬ` implies `ℬ^d ⊂ 𝒜^d`; hence `𝒜^d = 𝒜^ddd`; and `𝒜` local +implies `𝒜^dd` local. + +- Source: [GLRV99] §3.2 · tier (a) · **proved in source** +- Verbatim: + > is the largest net local relative to $\gdef\cA{{\cal A}}\cA^d$, $\gdef\cA{{\cal A}}\cA\subset\cA^{dd}$. However $\gdef\cA{{\cal A}}\gdef\cB{{\cal B}}\cA\subset\cB$ implies $\gdef\cB{{\cal B}}\gdef\cA{{\cal A}}\cB^d\subset\cA^d$, so that $\gdef\cA{{\cal A}}\cA^d=\cA^{ddd}$. A net $\gdef\cA{{\cal A}}\cA$ is said to be {\it local} if $\gdef\cA{{\cal A}}\cA\subset\cA^d$ and then $\gdef\cA{{\cal A}}\cA^{dd}\subset\cA^d=\cA^{ddd}$ so that $\gdef\cA{{\cal A}}\cA^{dd}$ is local, too. +- Depends on: (D1) a), b), c) — GLRV99 notes relative locality satisfies their analogues +- Proof route: (1) `𝒜^d` is the largest net relatively local to `𝒜` [the formula]; (2) `𝒜 ⊂ 𝒜^dd` [step 1]; (3) `d` is order reversing, so `𝒜^d = 𝒜^ddd` [steps 1–2]; (4) locality propagates [step 3]. +- The algebra-side shadow of the ⊥-Galois connection on 𝒦; the index-set version is (R9). + +### (R8) The Extension Theorem — connectedness of each `𝒪^⊥` lets essential duality replace Haag duality + +- Source: [GLRV99] Thm 3A.7 · tier (a) · **proved in source** +- Verbatim: + > If each $\cal{O}^\perp$ is connected, every object $\pi$ of {\rm Rep}$\gdef\cA{{\cal A}}^\perp\cA$ admits a unique extension to an object of {\rm Rep}$\gdef\cA{{\cal A}}^\perp\cA^{dd}$. Furthermore there is a canonical isomorphism of $W^*$--categories {\rm Rep}$\gdef\cA{{\cal A}}^\perp\cA$ and {\rm Rep}$\gdef\cA{{\cal A}}^\perp\cA^{dd}$. +- Depends on: (A3); (R7); [GLRV99] Lemma 3A.5; [GLRV99] Thm 3A.6 +- The printed proof cites "Lemma 3.A.4" where Lemma 3A.5 is needed — see `## Not investigated`. + +### (R9) ⊥̃ and ⊥̂ inherit a) and b) but not c); both operations are idempotent; all three relations coincide when 𝒦 is directed + +- Source: [GLRV99] Appendix Ch. 3 · tier (a) · **asserted in source; proved in this note** +- Verbatim: the quote under (D5). +- Depends on: (D1) a), b), c); (A1) for the coincidence clause +- Proof (coincidence): `⊥ ⊆ ⊥̂` — directedness supplies a common upper bound, and uses **no axiom**. `⊥ ⊆ ⊥̃` — given `O₁ ⊥ O₂`, directedness supplies `O₄ ≥ O₁, O₂`, c) supplies `O₃` with `O₄ ⊥ O₃`, and b) applied twice gives `O₁ ⊥ O₃` and `O₂ ⊥ O₃`; this uses **b), c) and directedness, but not a)**. +- Proof (idempotence): `⊥̂` is idempotent with **no axiom** — its defining clause is already carried by `⊥̂`. `⊥̃` is idempotent **using a)**: if `O₃` witnesses `O₁ ⊥̃ O₂`, then `O₂` witnesses `O₁ ⊥̃ O₃`, which needs `O₃ ⊥ O₂` obtained from `O₂ ⊥ O₃` by symmetry. +- So **a) is load-bearing for idempotence of ⊥̃ and for nothing else in this cluster, while b) and c) are load-bearing for the coincidence.** No source locates this. + +### (R10) For regular diamonds the gap between ⊥ and ⊥̃ is a boundary effect + +- Source: [GLRV99] Appendix Ch. 3 · tier (a) · **proved in source** for the implication, **asserted** for the exhaustion +- Verbatim: + > the difference between the relations $\perp$ and $\tilde\perp$ is, in this sense, a boundary effect. +- Depends on: (D2); (R9) + +### (R11) On the sieves with non-trivial causal complement, ⊥̃ = ⊥̂ + +- Source: [GLRV99] Appendix Ch. 3 · tier (b) read · **proved in source** (one line) +- Depends on: (R9); sieves closed under union +- GLRV99's diagnosis of why the two differ on regular diamonds: `The difference between $\tilde\perp$ and $\hat\perp$ merely reflects the potential difficulty of finding suitably large regular diamonds.` — i.e. exactly a failure of directedness. + +### (R12) [GLRV99] Lemma 3A.3 — components of a poset from components of a covering family of sieves + +- Source: [GLRV99] Lemma 3A.3 · tier (a) · **proved in source** +- Depends on: each member of the covering family is a sieve; `I` connected for the second clause + +### (R13) [GLRV99] Lemma 3.A4 — `μ` and `ν` are mutually inverse on causally closed open sets and sieves + +With `μ(X) := {𝒪 ∈ 𝒦 : 𝒪 ⊂ X}` and `ν(𝒮) := ⋃{𝒪 : 𝒪 ∈ 𝒮}`. + +- Source: [GLRV99] Lemma 3.A4 · tier (a) · **proved in source** +- Verbatim: + > When restricted to causally closed open sets and sieves, the maps $\mu$ and $\nu$ are inverses of one another. +- Depends on: (A6); (A9); (A10) +- **This is the result most directly about the object of this note**: it is the precise sense in which the poset `(𝒦, ⊥)` loses no information relative to the causally closed open sets of `M`, and it is what reconciles (C4)'s two readings of `𝒪^⊥`. + +### (R14) [GLRV99] Lemma 3.6 and its dimensional case analysis — the components of `Σ^⊥₁` determine the braiding + +In dimension `> 2` the relevant poset of 1-simplices is connected; in dimension +two with non-compact Cauchy surfaces it has exactly two components; in dimension +two with compact Cauchy surfaces the graph of ⊥ is connected but the simplex +poset still has two components. + +- Source: [GLRV99] Lemma 3.6 and the discussion following · tier (a) · **proved in source** +- Depends on: (R4); (R6); (R12); (D2) +- **This is where the index set alone decides whether the statistics is symmetric or braided.** + +### (R15) A net tending spacelike to infinity exists when 𝒦 is directed and when the Cauchy surface is non-compact; the compact case is open + +- Source: [GLRV99] §3.1 · tier (a) · **asserted** +- Verbatim: + > Such a net obviously exists whenever $\cal{K}$ is directed but it continues to exist for an arbitrary globally hyperbolic spacetime with a noncompact Cauchy surface. The question of whether one can find a suitable substitute for globally hyperbolic spacetimes with compact Cauchy surfaces is still open, +- Depends on: (A1); (A11) +- The geometric input to the construction of a left inverse, hence to the classification of statistics and charge conjugation. GLRV99's own mitigation is (A19). + +### (R16) The regular diamonds are not directed in general and are **never** directed when the Cauchy surface is compact + +- Source: [GLRV99] §3.1 · tier (a) · **asserted** +- Verbatim: + > may not be directed although it will be in cases of interest. However, when $M$ is globally hyperbolic with a compact Cauchy surface, $\gdef\cK{{\cal K}}\cK$ will never be directed and we shall meet problems akin to those on the circle. +- Depends on: (D2)'s `𝒪^⊥ ≠ ∅` clause — the unstated reason is that a diamond over the whole compact Cauchy surface would have empty causal complement +- This makes (R1)'s negative half bite, and is why (R15)'s compact case is open. + +### (R17) [GLRV99] — duality for ⊥̂ follows from duality for ⊥ plus additivity, via Lemma 2.1 + +- Source: [GLRV99] §5.2 · tier (a) · **proved in source** +- Verbatim: + > Under the above assumptions, the net satisfies duality for the relation $\hat\perp$, namely $$\gdef\cA{{\cal A}}\gdef\cO{{\cal O}} \cA(\cO)=\cap_{\cO_1\hat\perp\cO}\cA(\cO_1)' $$ +- Depends on: (R3); additivity of the net; (A17) +- Proof route: (1) Lemma 2.1 gives, for each point of `𝒪₁`, regions witnessing `𝒪 ⊥̂ 𝒪_x` [(R3)]; (2) additivity and duality collapse the two intersections [additivity, (A17)]. +- **The only place in the corpus where (R3) does real work in the main line**, and the justification for GLRV99's remark that the two notions of duality coincide for additive nets over regular diamonds. + +### (R18) [GLRV99] — closed-form causal complement on a wedge index set + +`W(E,t)^⊥ = W(E′,t)`, with `E′` the interior of the complement of the hemisphere `E`. + +- Source: [GLRV99] §5.1 · tier (a) · **asserted** (`The following proposition immediately follows.`) +- Verbatim: + > {$(i)$} $W(E,t)^{\perp} =\hat{r}_{\partial E,t}W(E,t)=W(E',t)$, where $E'$ denotes the interior of the complement of $E$. +- Depends on: (D20) +- The corpus's only closed-form computation of `^⊥` outside Minkowski and conformal settings; the source also records that each wedge here is a diamond, so the two families are not disjoint. + +### (R19) [HM06]'s account of directedness — and it disagrees with (R1) + +HM06 derives the inductive limit from isotony alone and then notes that +non-directedness can in many cases be circumvented. + +- Source: [HM06] §2.1 · tier (a) · **cited elsewhere** +- Verbatim: + > In some spacetimes, the set of double cones is not directed. In many such cases, it is still possible to define the quasilocal algebra by means of more sophisticated techniques \cite{glob}. +- Depends on: [ext: Fredenhagen, *Global observables in local quantum physics* — a construction of a global observable algebra for a net over a non-directed index set, so that failure of directedness does not by itself obstruct the passage to a single ambient C\*-algebra. tier (d), not retrieved] +- **The corpus's most important unreconciled disagreement.** (R1) says the construction is *denied*; this says it can often be circumvented. The two are not formally contradictory — GLRV99 denies the naive norm closure, which HM06 does not claim — but the reconciliation lives in an unfetched article. + +### (R20) Reeh–Schlieder plus `Ō₁ ⊂ 𝒪₂` gives a standard inclusion + +- Source: [HM06] §2.4 · tier (a) · **asserted** +- Verbatim: + > Then if $O_1,O_2$ are double cones such that the closure $\gdef\ol#1{{\overline #1}}\ol{O}_1$ of $O_1$ is contained in $O_2$, then the pair $\gdef\alg#1{\mathfrak{#1}}(\alg{R}(O_1),\alg{R}(O_2))$ is a standard inclusion of von Neumann algebras. +- Depends on: (A5); (D9) +- **The clearest instance in the corpus of a theorem whose only geometric content is a relation on 𝒦.** The unstated intermediate step — that `Ō₁ ⊂ 𝒪₂` makes `𝒪₁′ ∩ 𝒪₂` contain a region — is itself a claim about 𝒦 that no source proves; it is exactly the collar clause, and the Ordering theorem above is its proof under (T). + +### (R21) The funnel property implies the Hilbert space is separable + +- Source: [HM06] Prop. `separable` · tier (a) · **proved in source** +- Depends on: (A13); (A5); [DL84] Prop. 1.6 as an independent route (tier (b), `mineru-unchecked`) +- HM06 records that this is the only place in its chapter where separability is needed, and is openly sceptical of its physical warrant. + +### (R22) Concrete models exist in which the funnel property fails + +- Source: [HM06] §2.4 · tier (a) *for HM06 making the claim*; the claim itself **cited elsewhere** +- Depends on: [ext: Horuzhy, *Introduction to Algebraic Quantum Field Theory* — exhibits models of a net of local algebras in which no type I factor interpolates between the algebras of a region and of a larger region containing its closure. tier (c), no locator, not retrieved] +- No such model is named in any fetched source; (R39) supplies a named one by a different route. + +### (R23) The funnel property holds for free fields + +- Source: [HM06] §2.4 · tier (a) for the claim · **cited elsewhere** — and the cited source is [dB74], in this corpus +- Depends on: (R57); (R60) + +### (R24) Schlieder from microcausality, weak additivity and the spectrum condition, for strictly spacelike separated pairs + +- Source: [HM06] Prop. `schlieder` · tier (a) · **cited elsewhere** +- Depends on: (D7); [ext: Schlieder — for a pair of commuting von Neumann algebras arising from spacelike separated regions of a net with weak additivity and positive energy, the product of two non-zero elements, one from each, is non-zero. tier (d), not retrieved] + +### (R25) "Strongly spacelike separated" lies strictly between spacelike and strictly spacelike separated + +- Source: [HM06] §3.3 · tier (a) · **asserted in source; proved in this note** +- Verbatim: + > \begin{fact} If $O_1$ and $O_2$ are strictly spacelike separated, then they are strongly spacelike separated. \end{fact} +- Depends on: (D7); (D8); (A: 𝒦 contains, for each region, an enlargement containing its closure) +- Proof: from a neighbourhood `N` of `0` with `O₁ + x` spacelike to `O₂` for all `x ∈ N`, pick `ε` with `B(0,ε) ⊆ N`; translation covariance gives `O₁ + x` spacelike to `O₂ + y` for `|x|,|y| < ε/2`, and the unions over those translates are the required enlargements. **The proof is model-specific**: it needs 𝒦 closed under small enlargements, which (D2) records that double cones in a curved spacetime lack. + +### (R26) Schlieder from microcausality and property B, for strongly spacelike separated pairs + +- Source: [HM06] Prop. `frees` · tier (a) · **proved in source** +- Depends on: (A20); (D8) +- The printed proof opens "Let `O₁` and `O₂` be **strictly** spacelike separated" where the statement says **strongly**; the proof's second sentence is the definition of *strongly* unpacked, so the proposition as stated is proved and the first line is a typo. + +### (R27) The split property fails for a wedge and its causal complement + +- Source: [HM06] §3.3 · tier (a) · **proved in source** +- Depends on: `ℛ(W)` and `ℛ(W′)` being type III₁ factors; HM06's remark that a factor and its commutant have the same type +- HM06's own type III₁ proposition carries the escape clause `Then either $\gdef\al#1{{\mathfrak #1}}\gdef\7#1{{\mathbb #1}}\al R=\7C I$ or $\gdef\al#1{{\mathfrak #1}}\al R$ is a type III$_1$ factor.`, so every argument of the shape "split for all pairs ⇒ type I ⇒ contradiction" silently assumes the local algebras are not the scalars. + +### (R28) The funnel property upgrades to the split property for strictly spacelike separated pairs + +- Source: [HM06] §3.3 · tier (a) · **asserted** +- Depends on: (A13); (D7) + +### (R29) The implication chain among independence notions + +- Source: [HM06] §3.2 · tier (b) read · **cited elsewhere** +- Depends on: [ext: Summers — a survey ordering the independence conditions for a pair of commuting von Neumann algebras by logical strength. tier (c), no locator, not retrieved] + +### (R30) [BGL93] Prop. 1.3 — five index-set properties of `𝒦̃`, including that it is closed under `′` and is not directed + +- Source: [BGL93] Prop. 1.3 · tier (a) · **asserted** (`\proof Immediate.`) +- Verbatim: the three clauses quoted under (D12). +- Depends on: `M̃` the universal covering of compactified Minkowski space and `𝒞̃` its conformal group +- Clause (v) is `proved` on inspection, but the proof needs `𝒪″ = 𝒪`, which BGL93 never states. + +### (R31) For the double cones of Minkowski space, directedness is what makes the quasilocal algebra a direct limit + +- Source: [BGL93] §1 · tier (a) · **asserted** +- Verbatim: the `direct set` quote under (D12). + +### (R32) The conformal net extends uniquely to the covering + +- Source: [BGL93] Lemma 1.9, Prop. 1.10 · tier (a) · **proved in source** +- Depends on: (R30)(ii) — transitivity of the conformal action on `𝒦̃` + +### (R33) Essential duality holds automatically for a conformally covariant pre-cosheaf + +- Source: [BGL93] Thm 2.3 · tier (a) · **proved in source** +- Depends on: (R32); [BGL93] Prop. 2.1 + +### (R34) Duality on `S¹` holds with no assumption beyond positivity of the energy + +- Source: [BGL93] §2 · tier (a) · **asserted** for the models +- Depends on: (R33); [ext: Hislop–Longo — for the free massless scalar field the modular group of the algebra of a double cone acts geometrically, giving duality. tier (c), no locator, not retrieved] +- (R42) identifies by name what this describes: duality on the light-ray is equivalent to **strong additivity**, and fails because the complement of an interval of `ℝ` is disconnected while on `S¹` it is a single proper interval. + +### (R35) [BGL93] Cor. 2.7 — under conformal invariance plus a spectral condition, every local algebra is the hyperfinite type III₁ factor + +- Source: [BGL93] Remark 2.6, Cor. 2.7 · tier (a) for the statement; the result **cited elsewhere** (`\proof Immediate, see [\ref(Long1)].`) · **survives at (c), `unverified`** +- Depends on: (R36); conformal invariance; exponential eigenvalue growth; [ext: Longo — the interpolating type I factor structure forces the hyperfinite type III₁ factor. tier (c), no locator, not retrieved] +- **How much weight this can carry:** it is conditional on Remark 2.6's spectral hypothesis and on an unretrieved citation, so an argument needing "local algebras are not type I" should hang on [HM06]'s type III₁ proposition instead, carrying its `ℂ𝟙` escape clause. + +### (R36) The "distal split property" is an existential over a single pair of regions + +- Source: [BGL93] §3 assumption (b) · tier (a) · it is an **assumption**, not a result +- Verbatim: + > \item{$(b)$} Distal split property holds, i.e there exist two regions $\gdef\O{{\cal O}}\O_1\subset \O'_2$ in $M$ such that $\gdef\R{{\cal R}}\gdef\O{{\cal O}}\R(\O_1)$ and $\gdef\R{{\cal R}}\gdef\O{{\cal O}}\R(\O_2)$ generates a $W^*$-tensor product. +- **It imposes no relation on 𝒦 at all** — one split pair anywhere suffices — and is therefore strictly weaker than the funnel property, not a metric strengthening of the separation relation. It upgrades to the universal form under a **transitively acting symmetry group and `d > 2`**. + +### (R37) The distal split property forces uniqueness of the covariant representation + +- Source: [BGL93] Thm 3.1 · tier (a) · **proved in source** +- Depends on: (R36) + +### (R38) Essential duality for wedges from geometric modular action + +- Source: [BGL93] Thm 3.3 · tier (a) · **proved in source** by reference to earlier proofs +- Depends on: (R33) +- The printed proof cites a "Lemma 3.4" that does not exist; Lemma 3.2 is meant. + +### (R39) A named non-split net: `ℬ(𝒪) = 𝒜(π⁻¹𝒪)` + +Let `𝒜` be a Poincaré covariant net on `(d+1)`-dimensional Minkowski space and +`π` the projection onto `d` dimensions; then `𝒪 ↦ 𝒜(π⁻¹𝒪)` is a net on `d` +dimensions which BGL93 labels non-split. + +- Source: [BGL93] §3, closing paragraph · tier (a) for the construction; the attribution is to a **private remark** of Buchholz, so **no locator can ever exist** · **proved in source** as a short verification +- Depends on: (R38) +- **The corpus's only concrete non-split example.** BGL93 only *labels* it non-split and leaves unwritten both that `ℬ` satisfies its assumption (a) and the contraposition; and the argument needs `d > 2`. (R59)(b) reaches the same conclusion by a route with fewer external edges. + +### (R40) The proper intervals of `S¹` are not directed + +- Source: [KOE03] §1.2 · tier (a) · **asserted** +- Verbatim: the quote under (D14). +- Depends on: `I′ ≠ ∅` as a membership condition +- **The corpus's load-bearing non-directed index set.** + +### (R41) What the failure of directedness costs: DHR must be replaced, and Doplicher–Roberts reconstruction is lost + +- Source: [KOE03] §1.3 · tier (a) · **cited elsewhere** +- Depends on: (R40) + +### (R42) Haag duality on `S¹` follows from the general assumptions but may fail on the light-ray + +- Source: [KOE03] §1.3 · tier (a) · **asserted** (`S¹` half) +- Depends on: (R40) +- The mechanism is the index set's topology: the complement of an interval of `ℝ` is disconnected, of an interval of `S¹` is a single proper interval. + +### (R43) Strong additivity of a subnet removes the isotony problem for local relative commutants + +- Source: [KOE03] §1.2–1.3 · tier (a) · **proved in source** +- Depends on: (R42); locality; the `⋐` relation + +### (R44) The dual net on the light-ray makes any chiral theory strongly additive + +- Source: [KOE03] §1.3 · tier (a) · **cited elsewhere** +- Depends on: (R42) + +### (R45) Inner and outer continuity of the local algebras along the `⋐` relation + +- Source: [KOE03] §1.3 · tier (a) · **cited elsewhere** +- Depends on: scale invariance + +### (R46) The split property for chiral nets is stated over the `Ī₁ ⊂ I₂` relation + +- Source: [KOE03] §1.3 · tier (a) · **definition** plus an **asserted/cited** implication from nuclearity +- Verbatim: the quote under (D15). +- Depends on: [ext: Buchholz–Wichmann — a nuclearity condition on the energy-level density implies the split property for sufficiently separated regions. tier (d), no locator, not retrieved] + +### (R47) Double cones are causally complete + +- Source: [NAA13] §1 · tier (a) · **asserted** +- Verbatim: the quote under (D10). + +### (R48) The lattice index set is directed, ⊥ is disjointness, and the quasi-local algebra is the inductive limit + +- Source: [NAA13] §2.4 · tier (a) · **proved in source** at the level of the construction +- Depends on: Γ countable; (A1), used silently + +### (R49) C\*-level duality `𝒜(Λ)^c = 𝒜(Λ^c)` for finite Λ + +- Source: [NAA13] §2.4 · tier (a) · **proved in source** for the finite case +- Depends on: (R48); [ext: NAA13's cited text — the relative commutant of a finite-region algebra inside the quasi-local algebra of a spin system is the algebra of the complementary region. tier (c), no locator, not retrieved] + +### (R50) Haag duality for cones: one inclusion is free from locality, the other is an assumption + +- Source: [NAA13] §3 · tier (a) · the easy inclusion **proved in source** +- Depends on: (R48) + +### (R51) The superselection criterion quantifies over the cone index set + +- Source: [NAA13] §3 · tier (a) · **sketched** +- Depends on: (R50) +- Cones are named as an index set here and by [GLRV99] and are **defined by neither**; the definition lives on the unfetchable list. + +### (R52) [BFV01] — the index set of a locally covariant theory + +- Source: [BFV01] §2.4, first document body · tier (a) · **definition** +- Verbatim: the quote under (D16). +- Depends on: `(M,ḡ)` an object of `𝔐` + +### (R53) [BFV01] — the causal complement as a largest open set + +- Source: [BFV01] §2.1, first document body · tier (a) · **definition** +- Verbatim: the quote under (D17). +- The existence of a *largest* such open set is not argued. + +### (R54) [BFV01] Prop. `localnet` — a locally covariant theory yields a Haag–Kastler net over `𝒦(M,ḡ)` + +- Source: [BFV01] Prop. `localnet`, first document body · tier (a) · **proved in source**, all four clauses +- Depends on: (R52); (R53); the functor axioms +- **The corpus's only derivation of the Haag–Kastler index-set structure from a categorical datum**, and the reason (H2) is a scope limit rather than a defect. + +### (R55) [BFV01] — the fattening lemma: causally separated regions can be enlarged while staying causally separated + +- Source: [BFV01], inside the proof of Prop. `localnet`, first document body · tier (a) · **asserted** +- Depends on: (R52); (R53); (A11) +- BFV01 asserts in one sentence what [GLRV99] (R3) sketches over a page. + +### (R56) [BFV01] — the inductive limit attributed to isotony alone + +- Source: [BFV01] §2.4 footnote, first document body · tier (a) · **asserted** +- Depends on: isotony; and, unstated, that the bounded open subsets of Minkowski space are directed + +### (R57) [dB74] — the `𝒩`-thickened separation relation and the "almost factors" standing hypothesis + +- Source: [dB74] §II · tier (b), `mineru-unchecked` · **cited elsewhere** +- Verbatim: the shape-1 and standing-hypothesis quotes under (D18). +- Depends on: (A5) in dB74's form — Ω cyclic and separating for open regions with non-empty spacelike complement + +### (R58) [dB74] Cor. 2.4 — a normal product state yields interpolating type I factors, and conversely + +- Source: [dB74] Cor. 2.4 · tier (b), `mineru-unchecked` · **proved in source**; the converse **asserted** +- Depends on: (R57) + +### (R59) [dB74] — two classes of configurations admitting **no** normal product state + +(a) regions whose closures are not spacelike separated (asserted, free field); +(b) any two spacelike separated regions each mapped into itself by a common +translation. + +- Source: [dB74] §II, items a) and b), **p. 292** · tier (b), `mineru-cross-checked-against-PDF-text-layer` · (a) **asserted**, (b) **proved in source** +- Depends on: isotony; clustering; (A5) +- **(b) is the corpus's most economical non-example**: it needs no type classification, and it covers both (R27)'s wedge pair (**in `d ≥ 3` only** — in 1+1 no translation fixes both `W` and `W′`, so the two routes are incomparable rather than nested) and every region of (R39)'s net. Neither source cites dB74 for it. The footnote on item b) reads *This example is due to Araki*. + +### (R60) [dB74] — the positive result for the free neutral massive scalar field + +- Source: [dB74] §III · tier (b), `mineru-unchecked` · **proved in source** (the proof runs past the converted pages, so no route) +- Depends on: (D18) shape 3 — smooth boundaries and positive distance + +### (R61) [DL84] — the split property expresses independence of regions separated by non-zero distance + +- Source: [DL84] §0 · tier (b), `mineru-unchecked` · **asserted** +- Verbatim: + > expresses the statistical independence of any bounded region in space-time from any other region space-like separated by non-zero distance +- Depends on: (D18) shape 3, which DL84 echoes from dB74 — and dB74's cached scope (R60) is narrower than either DL84's or HM06's paraphrase. + +### (R62) [DL84] — the split property is stated with **no separation relation at all** + +- Source: [DL84] §1 · tier (b), `mineru-unchecked` · **definition** +- **The cleanest evidence in the corpus that the separation relation belongs to the index set and not to the algebras**: at the two-algebra level it simply is not present. + +### (R63) [DL84] announces field theories that do not satisfy the split property + +- Source: [DL84] §§9–10, **not converted** · tier (b) for the announcement +- Would supply further concrete non-split examples; the highest-value extension available to a later run. + +## Hypotheses + +| (A#) | Statement | Class | Evidence | Witness | Scope | Tier | Used by | +|---|---|---|---|---|---|---|---| +| (A1) | 𝒦 is directed under inclusion | model-dependent | [GLRV99] §3.1; [KOE03] §1.2; [HM06] §2.1 | fails: the proper intervals of `S¹` ([KOE03], stated outright); also the regular diamonds of any globally hyperbolic spacetime with compact Cauchy surface ([GLRV99], class fact, no spacetime named). holds: the open double cones of Minkowski | local | a | (R1), (R9), (R15), (R16), (R19), (R31), (R40), (R48) | +| (A2) | 𝒦 is connected as a poset | provable | [GLRV99] Lemma 3A.1 applied to (A2a) | — | local | a | (R6), (R14) | +| (A2a) | `M` is path-connected | model-dependent | implicit in [GLRV99] §2.1, explicit in [BFV01] §2 (`connected`) | fails: the disjoint union of two copies of Minkowski space (tier (b) — the ingredients are quoted, the assembly is mine). holds: 4-dimensional Minkowski | standing, **implicit** in [GLRV99] | a/b | (R2), (R4), (R6) | +| (A3) | each `𝒪^⊥` is connected | model-dependent | [GLRV99] §3.1, §3.2 | fails: regular diamonds in 2-dimensional Minkowski space, where the complement splits into a left and a right part. holds: 4-dimensional Minkowski | local | a | (R6), (R8), (R14) | +| (A4) | the graph of ⊥ is connected | model-dependent | [GLRV99] Lemma 2.2, proof read | fails: 2-dimensional Minkowski (Cauchy surface `ℝ`). holds: dimension `> 2` | local | a | (R14) | +| (A5) | the vacuum is cyclic and separating for local algebras | provable / assumed | [HM06] §2.3; [dB74] §II as a standing assumption | — | standing in [dB74] | a/b | (R20), (R21), (R57), (R59) | +| (A6) | 𝒦 is a base for the topology of `M` | provable | [GLRV99] Lemma 3A.1 and `Both sets have in common that they form a base for the topology of $M$` | — | local | a | (R5), (R6), (R13), and the Ordering theorem | +| (A7) | members of 𝒦 are open, non-empty, path-connected | provable | [GLRV99] §2.1 regularity conditions | — | local | a | (R5), (R6) | +| (A8) | condition c) for ⊥, ⊥̃, ⊥̂ | definitional for ⊥; provable for ⊥̃, ⊥̂ in the applications | [GLRV99] §3.1 and Appendix Ch. 3 (`c) remains to be checked`) | — | standing / local | a | (R7), (R9) | +| (A9) | ⊥ on `Open(M)` is *local*: `X ⊆ ⋃𝒪ᵢ` with every `𝒪ᵢ ⊥ 𝒪` gives `X ⊥ 𝒪` | provable | [GLRV99] Appendix Ch. 3, asserted as obvious | — | local | a | (R13) | +| (A10) | members of 𝒦 are causally complete, `𝒪 = 𝒪^⊥⊥` | provable for the index sets used | [NAA13] §1 and [GLRV99] §2.1 both assert it without proof; **independent of a),b),c)** by (X19) | — | mixed: membership condition in (D19), observation in (D10) | a | (R13), (R30) | +| (A11) | `(M,g)` is globally hyperbolic | model-dependent | [GLRV99] §2.1; [BFV01] §2, both standing | no failing spacetime is named in the corpus; the classical examples rest on recall, so this row is at best (d) on the failing side | standing | a/d | (R3), (R4), (R15), (R55) | +| (A12) | the vacuum Hilbert space is separable | provable relative to (A13) | [HM06] Prop. `separable`; [KOE03] builds it into its axioms; [DL84] Prop. 1.6 independently (tier (b), `mineru-unchecked`) | — | standing in [HM06], [KOE03] | a/b | (R21) | +| (A13) | the funnel / split property | model-dependent | [HM06] §2.4; [BGL93] §3; [KOE03] §1.3 | fails: [BGL93]'s dimensional-reduction net (R39), and every configuration of (R59)(b). holds: the free neutral massive scalar field (R60); conformal nets under (R35) | local | a | (R21), (R23), (R28), (R35), (R37) | +| (A14) | how much separation the independence results need | model-dependent | [HM06] §3.3; [dB74] §II–III; [DL84] §0; [BGL93] §3 | fails: two double cones in 4-dimensional Minkowski whose closures touch (R59)(a). holds: the same pair pushed apart | local | a/b | (R20), (R24), (R25), (R26), (R28), (R59), (R61) | +| (A15) | every region has non-empty causal complement | model-dependent | [dB74] §II standing; membership condition in (D2), (D14), (D19) | fails: a time-slab around a compact Cauchy surface in [BFV01]'s `𝒦(M,ḡ)`. holds: every open double cone in Minkowski | standing in [dB74] | a/b | (R16), and axiom c) | +| (A16) | members of [BFV01]'s `𝒦(M,ḡ)` are open and connected | model-dependent | [BFV01] §2.4 — **not in the definition, forced by the next sentence** | fails on the literal reading: `∅`, `{x}`, and `{x,y}` for `x`, `y` spacelike. holds: any relatively compact causally convex open connected set | standing, **implicit** | a/b | (R52), (R54), and (X15) direction 1 | +| (A17) | Haag duality, or essential duality, for a given index set | model-dependent — **and dependent on which index set** | [BGL93] Thm 2.3; [KOE03] §1.3; [GLRV99] §1.1; [HM06]; [NAA13] §3 | fails: [BGL93]'s net `𝒜ₙ` on the light-ray, i.e. a non-strongly-additive chiral theory. holds: conformally covariant pre-cosheaves (R33); the free massless scalar (R34) | standing / local | a | (R2), (R8), (R17), (R42), (R50) | +| (A18) | surjectivity of the projection from the graph of ⊥ onto 𝒦 | provable | [GLRV99] §3.1, asserted | — | local | a | (R14) | +| (A19) | local intertwiners equal global intertwiners | provable for Minkowski; open in general | [GLRV99] Ch. 5, postulated | — | local | a | (R15)'s mitigation | +| (A20) | property B | provable from microcausality, the spectrum condition and weak additivity | [HM06] Prop. `prop-B`; [GLRV99] §3.2 | — | standing in [HM06] | a | (R26) | +| (A21) | the time-slice axiom | provable for a named model | [BFV01] §4 — the CCR/Weyl algebra of the Klein–Gordon equation on any globally hyperbolic spacetime | — | local | a | — | +| (A22) | the wedges separate spacelike points, i.e. regular diamonds are intersections of wedges | open | [GLRV99] Ch. 5, assumed | neither a proof nor a failing spacetime could be produced | local | a | (R18), (R20)'s wedge analogue | + +## Degeneracies and boundary cases + +| Case | Effect on the adopted form | Tier | +|---|---|---| +| intended case is nonvacuous | named instances abound and were checked one by one against a), b), c): open double cones in Minkowski ([HM06], [NAA13]); regular diamonds in a globally hyperbolic spacetime ([GLRV99]); proper intervals of `S¹` ([KOE03]); `𝒦̃` ([BGL93], c) via Prop. 1.3(iv)); `𝓛` and `𝒦 ∪ 𝒲` ([GLRV99]); wedges in Minkowski (c) via the opposite wedge); `𝒫_f(Γ)` ([NAA13]) | a | +| zero object / scalars | `𝒦 = ∅`: a), b), c) hold vacuously; no effect. `𝒦` a single element: c) fails unless that element is self-orthogonal | — | +| finite-dimensional | not a meaningful axis for an index set; the discrete analogue is `𝒫_f(Γ)` with `Γ` finite, which still satisfies c) via `∅` | — | +| commutative | not a meaningful axis; the order-theoretic analogue is `𝒦` a chain, which is **not** excluded by the adopted form (it is excluded only under irreflexivity — (X6), (H1)) | — | +| non-separable / non-σ-finite | no effect: these are conditions on the Hilbert space, and (A12) makes separability a *consequence* of (A13) rather than an input | b | +| type III | the intended case: the local algebras of an interesting net are type III₁, and (X32) turns that into the reason the collar clause cannot be dropped — subject to [HM06]'s `ℂ𝟙` escape clause | a/c | +| non-unital / degenerate representation | no effect at index-set level; [NAA13] fixes `𝒜(∅) = ℂI` explicitly rather than allowing a degenerate algebra | a | +| universally orthogonal index element | **admitted**: `∅ ∈ 𝒦` is ⊥ to everything including itself in (D11), and a bottom element ⊥ to everything is exactly (H1)'s construction. It satisfies c) while making c)'s intended guarantee empty | a | +| everything ⊥ everything | a), b), c) all hold; the *net* over such an index set collapses to a commutative one by locality, but the form itself is not contradicted | — | +| quantifier swap in c): `∀O₁ ∃O₂` ↦ `∃O₂ ∀O₁` | meaning changes: taking `O₁ := O₂` forces `O₂ ⊥ O₂`. False on double cones, true on `𝒫_f(Γ)` via `∅` — (X25) | — | +| quantifier swap: "∃ one region ⊥ to all *others*" | genuinely different from both, separated by the 2-element antichain — (X26) | — | +| quantifier swap inside the collar clause | `∃ one collar ∀ nested pairs` is false for double cones (the collar must fit inside arbitrarily small outer regions) and, under the strict collar clause, false for `𝒫_f(Γ)` too — (X28) | — | +| hypothesis dropped: a) symmetry | independent — witness: a 3-cycle on a 3-element antichain satisfies b), c) and fails a). Consequence: idempotence of ⊥̃ fails, by (R9) | — | +| hypothesis dropped: b) heredity | independent — witness: `{A ≤ B, C, D}` with `⊥ = {(B,C),(A,D)}` symmetrised. Consequence: `𝒪^⊥` is no longer a sieve, so (R13) and (R7) fail | — | +| hypothesis dropped: c) | independent — witness: a single element with `⊥ = ∅`. Consequence: `⊥ ⊆ ⊥̃` fails even under directedness, by (R9) | — | +| hypothesis reversed: b) anti-heredity | false in every geometric model; with a top element it produces a *top* orthogonal to everything including itself, **not** the everything-⊥-everything model — (X27) | — | +| hypothesis added: irreflexivity | strictly stronger — not derivable from a), b), c) (witness: (H1)'s bottom-element model). It is what would buy "comparable regions are never ⊥" and "𝒦 is not a chain" | — | +| hypothesis added: causal completeness | strictly stronger — 3-element antichain witness, (X19) | — | +| collar clause dropped from the separation relation | the split property degenerates to `𝒜(O) ⊆ 𝒩 ⊆ 𝒜(O)`, forcing every local algebra to be type I — (X32) | a/c | + +## Rejected formulations and refuted claims + +**Append-only.** + +| id | Candidate or claim | Disposition | Discriminator | Tier | Date | +|---|---|---|---|---|---| +| (X15) | the collar clause and the closure form `Ō₁ ⊆ O₂` as *the* separation relation for the split property | rejected (both, as the single adopted relation) | **(X1) separating object**, both directions — collar without closure: internally tangent double cones `O₂ = {|x₀|+\|x⃗\| < 1}`, `O₁ = {|x₀|+\|x⃗−c\| < 1/2}`, `c = (1/2,0,0)`, with collar `O₃ = {|x₀|+\|x⃗+c\| < 1/8}`, using the verified criterion that `D(B(a,r))` and `D(B(b,s))` are spacelike iff `\|a−b\| ≥ r+s`; closure without collar: `Λ₁ = Λ₂ = {0}` in `𝒫_f(ℤ)`, and (non-degenerately) `O = (−ε,ε)×Σ ⊂ O₂ = (−2ε,2ε)×Σ` over a compact Cauchy surface Σ. **(X5) conditional equivalence** — under (T) the closure form is strictly stronger; see the Ordering theorem | a/b | 2026-08-15 | +| (X18) | the causal complement as an **operation into 𝒦**, `O₁ ⊥ O₂ ⟺ O₂ ≤ O₁^⊥` | rejected | **(X1) separating object** — a double cone in Minkowski: the double cones spacelike to it have no maximum (translate away, then dilate inside `O′`). `𝒦̃` and the proper intervals of `S¹` do support the operation | a | 2026-08-15 | +| (X18′) | the complement as a **sieve-valued** operation `O ↦ {O₁ : O₁ ⊥ O}` | equivalent | — ([GLRV99] writes exactly this: `We write $\gdef\cO{{\cal O}}\gdef\cK{{\cal K}}\cO^\perp:=\{\cO_1\in\cK:\cO_1\perp\cO\}$.`) | a | 2026-08-15 | +| (X19) | causal completeness `O = O″` as an axiom | rejected | **(X1) separating object** — the 3-element antichain `{A,B,C}` with `⊥ = {(A,B),(B,A),(A,C),(C,A)}` satisfies a), b), c) while `B^⊥⊥ = {B,C} ⊋ ↓B`. Geometric realisation: a spacelike disc and its domain of dependence share a causal complement | — | 2026-08-15 | +| (X24) | [BGL93]'s *distal split property* as a phrasing of the separation relation | rejected | **(X5) conditional equivalence** — as written it is an existential over one pair of regions and constrains 𝒦 not at all; it upgrades to the universal form only under a transitively acting symmetry group **and** `d > 2` | a | 2026-08-15 | +| (X25) | c) with the quantifiers swapped | rejected | **(X2) degeneracy** — forces a self-orthogonal element; false on double cones, true on `𝒫_f(Γ)` via `∅` | — | 2026-08-15 | +| (X26) | "∃ one region ⊥ to all *others*" | rejected | **(X1) separating object** — the 2-element antichain `{A,B}` with `A ⊥ B`: the `≠`-form holds, the `∀`-including-self form fails | — | 2026-08-15 | +| (X27) | b) with the inclusion reversed (anti-heredity) | rejected | **(X1) separating object** — false in every geometric model. *The parenthetical claim that with a top element it collapses to everything-⊥-everything is itself refuted*: witness `{A, B ≤ T}` with `⊥ = {(A,B),(B,A)} ∪ {(T,X),(X,T)} ∪ {(T,T)}`, where `A ⊥̸ A` | — | 2026-08-15 | +| (X28) | one collar for all nested pairs | rejected | **(X1) separating object** — double cones: the collar must lie inside arbitrarily small outer regions | — | 2026-08-15 | +| (X32) | dropping the collar clause, leaving `O₁ ≤ O₂` | rejected | **(X2) degeneracy** — the split property becomes `𝒜(O) ⊆ 𝒩 ⊆ 𝒜(O)`, forcing every local algebra type I, contradicting the type III₁ structure — with [HM06]'s escape clause `Then either $\gdef\al#1{{\mathfrak #1}}\gdef\7#1{{\mathbb #1}}\al R=\7C I$ or $\gdef\al#1{{\mathfrak #1}}\al R$ is a type III$_1$ factor.` attached | a/c | 2026-08-15 | +| (X9) | *claim*: [BFV01]'s `𝒦(M,ḡ)` is not an instance of the adopted form, witnessed by `∅`, `{x}`, `{x,y}` and a time-slab | refuted as stated | read literally `∅ ∈ 𝒦(M,ḡ)`, and BFV01's ⊥ is vacuously true against `∅`, so **c) holds everywhere via `∅`** and three of the four witnesses show only that BFV01's regions need not be open or connected. What survives: under the reading forced by BFV01's own `obj(𝔐)` sentence, c) fails at a time-slab around a compact Cauchy surface — **one witness, conditional on a reading BFV01 never states** | a | 2026-08-15 | +| (X7) | *claim*: the lattice index set is excluded by c) | refuted as stated — re-scoped | **[NAA13]'s own `𝒫_f(Γ)` contains `∅`** and fixes `𝒜(∅) = ℂI`, so c) holds even for finite Γ. The claim is about a variant with `∅` removed, not about the corpus's lattice index set | a | 2026-08-15 | +| (X6) | *claim*: the adopted form excludes chains | refuted as applied | the derivation uses **irreflexivity**, which is independent of a), b), c) — witness (H1)'s bottom-element model. The form is not credited with it | — | 2026-08-15 | +| (H1) | *claim about the adopted form*: axiom c) delivers "every region has somewhere spacelike to it" | refuted | adjoin a least element `0` to any poset and set `⊥ = {(0,O),(O,0)}`; a), b), c) hold, so **every poset underlies a causal index set**. Concretely, with `𝒜(0) = ℂ𝟙` the dual net is `B(ℋ)` everywhere and Haag duality forces `𝒜(O) = B(ℋ)` | — | 2026-08-15 | +| (H2) | *scope*: the adopted form expresses [BFV01]'s theory | refuted | BFV01's primary object is a category whose causality condition quantifies over **pairs of morphisms into a common spacetime**; `𝒦(M,ḡ)` is a derived poset obtained by fixing one object. The form covers the derived net, not the theory | a | 2026-08-15 | +| (X-R25) | *claim* [HM06] `fact`: strictly spacelike separated ⇒ strongly spacelike separated, asserted with no proof and no citation | promoted to proved | proof written out at (R25); **not trivial** — it needs 𝒦 closed under small enlargements, which [GLRV99] records that double cones in a curved spacetime lack | a | 2026-08-15 | +| (X-R9) | *claim* [GLRV99]: passing to ⊥̃ or ⊥̂ is idempotent, and all three coincide when 𝒦 is directed, asserted with no proof | promoted to proved | proofs written out at (R9), locating which axiom does which work | a | 2026-08-15 | +| (X-frees) | *claim*: [HM06] Prop. `frees` is proved for a different hypothesis than it states | refuted | the proof's second sentence is the definition of *strongly* unpacked; `strictly` in its first line is a typo and the proposition as stated is proved | a | 2026-08-15 | +| (X-Cor2.7) | *claim*: [BGL93] Cor. 2.7 establishes type III₁ for local algebras | survives at (c), `unverified` | discharged by `\proof Immediate, see [\ref(Long1)].` and conditional on Remark 2.6's spectral hypothesis; the cited work is not retrieved. **The locator is into BGL93, which was retrieved at tier (a), so it is a permitted locator; the `c` in the tier column is the verification status of the *result*, whose proof lies in an unretrieved work** | c | 2026-08-15 | +| (X-1.3v) | *claim*: [BGL93] Prop. 1.3(v) (that `𝒦̃` is not directed) is immediate | promoted to proved | the verification is immediate **given `𝒪″ = 𝒪`**, which BGL93 never states | a | 2026-08-15 | + +## Prior art + +| System | Found | Relation to variants | How searched | Measured at | +|---|---|---|---|---| +| this repository | `CausalOrthogonality` (`QuantumSystem/Algebra/LocalNet/Net.lean`) — a class over a `Preorder` with fields `Orthogonal`, `orthogonal_symm`, `orthogonal_mono_left` | **still weaker than (D1) alone**: a) and b) present, **c) absent from this class specifically** — c) has since been split off into the separate mixin `CausalIndexSet` (see below), so `CausalOrthogonality` by itself remains a strict sub-axiomatisation, exactly as before this revision. The docstring now names the split explicitly and gives three ways the corpus secures c) (membership condition, admitting `∅`, or not at all — citing (D2)/(D14), (D11), (D16)). Irreflexivity still absent, deliberately; no causal-complement operation, no causal completeness, no directedness in the class | `Grep` for `Causal\|complement\|Haag\|duality\|DHR\|superselection` over `QuantumSystem/`; full read of `Net.lean`; `lean_local_search "CausalIndexSet"`; `lean_hover_info` on both classes | working tree, 2026-08-15 | +| this repository | `CausalIndexSet` (`QuantumSystem/Algebra/LocalNet/Net.lean`) — a new `Prop`-valued mixin class over `[Preorder K] [CausalOrthogonality K]` with the single field `exists_orthogonal : ∀ O₁, ∃ O₂, O₁ ⟂ O₂` | **the combination `CausalOrthogonality K` + `CausalIndexSet K` realizes (D1) exactly** — a), b), c) together, with no further hypotheses: no irreflexivity, no directedness, no causal-complement operation into `K`, no causal completeness. The docstring states this identification in so many words (`An ordered K with [CausalOrthogonality K] and this mixin is exactly the adopted general form of the extraction note`). Previously (X18) and (X19) were rejections of *additional* axioms this class still correctly does not carry — a causal-complement operation into `K` and causal completeness respectively — and that remains true: `CausalIndexSet` adds only c), nothing from (X18)/(X19)'s rejected strengthenings | `lean_hover_info` and `lean_declaration_file` on `CausalIndexSet`; `lean_references` (no consumers found); full read of `Net.lean` | working tree, 2026-08-15 | +| this repository | `instance : CausalOrthogonality (Finset α)` with `Orthogonal := Disjoint`, and `instance : CausalIndexSet (Finset α)` proved by `⟨fun Λ => ⟨∅, Finset.disjoint_empty_right Λ⟩⟩` | **same as (D11)**, and the `CausalIndexSet (Finset α)` instance is the Lean realization of exactly the route (X7)/(D11) already document for the concrete lattice case: `∅` is disjoint from every finite set, so `∅` is the witness for `exists_orthogonal`, matching NAA13's `𝒫_f(Γ) ∋ ∅` device that (X7) identifies as how axiom c) is secured for the lattice index set (the docstring of the instance states this explicitly: "the empty region is disjoint from every region, so causal complements exist for free"). The other (D-concrete) branches — double cones, regular diamonds, `S¹` intervals — have no instance anywhere | greps for `Minkowski`, `Lorentz`, `double cone`, `diamond`, `spacetime`, `wedge` return only docstring prose; full read of the `CausalIndexSet (Finset α)` instance and its docstring | working tree, 2026-08-15 | +| this repository | `ProperContainment` (`QuantumSystem/Algebra/LocalNet/SplitProperty.lean`) with the collar field `∃ O₃, O₃ ≤ O₂ ∧ O₁ ⟂ O₃ ∧ ¬ O₃ ≤ O₁` | **the collar branch of the separation family** — closest to (D5′) among the literature forms, though (D5′) quantifies over all regions rather than relativising to a given outer region. Weaker than (D8)/(D9)/(D15): no closure, no distance. The repository proves the exact strength of its own clause (`exists_orthogonal_iff_not_subset`), namely that on `Finset α` it is equivalent to strict inclusion — which is (X15) direction 1 rediscovered | same | working tree, 2026-08-15 | +| this repository | `ProperContainment.ofThicken` and the `Finset ℤ` 1-neighbourhood instance | **stronger than the class** — the literature's separation is recovered by a buffer layer, at model level | same | working tree, 2026-08-15 | +| this repository | `LocalNet`, `VonNeumannNet`, `LocalNet.Covariance` | consuming structures; `Covariance` is an automorphism group of a fixed index set, **unrelated to (D-categorical)** — no functor from a category of spacetimes exists here | same | working tree, 2026-08-15 | +| this repository | could not find: a causal-complement operation, causal completeness, Haag duality, additivity, spacelike cones, or any DHR material at index-set level | — | same | working tree, 2026-08-15 | +| Mathlib | order-theoretic substrate only: `Disjoint`, `SetRel`, `SimpleGraph`, `Metric.AreSeparated`, `Metric.thickening`, `closure`/`interior` | **substrate**; nothing expresses a causal-disjointness relation on a poset | `lean_local_search`, `Grep` over `.lake/packages/mathlib`, `lean_leansearch`, `lean_loogle`, `lean_leanfinder` | mathlib rev `5450b53e5ddc75d46418fabb605edbf36bd0beb6` | +| Mathlib | could not find: an abstract orthogonality relation on an order with the heredity axiom, causal structure, Lorentzian geometry, or anything AQFT-shaped | — | same queries | mathlib rev `5450b53e5ddc75d46418fabb605edbf36bd0beb6` | +| Lean, outside Mathlib | `physlib`: `Lorentz.Vector.causallyUnrelated`, `causalDiamond`, `CausalCharacter` | **substrate at event level** for (D-concrete); no regions, no index set, no AQFT net | web and repository search | 2026-08-15 | +| Lean, outside Mathlib | an orthomodular-lattice development was reported but not retrieved | (D-complement)-shaped, tier (d) | web search | 2026-08-15 | +| Isabelle AFP | Schutz's Minkowski axioms; GNS; no-faster-than-light entries | **event-level substrate**; the index set is absent | AFP topic index and entry pages | 2026-08-15 | +| Coq/Rocq | could not find any causal index set or AQFT net, having searched the mathcomp and CoqQ indices and general web queries | — | web searches | 2026-08-15 | +| any system | could not find a formalization of Haag–Kastler nets over a causal index set, nor of causal-set structure carrying a *disjointness* rather than a causal *order* | — | web searches, Lean Zulip site-scoped queries | 2026-08-15 | + +## Open questions + +- (R19) versus (R1): does the failure of directedness deny the quasi-local algebra, or can it be circumvented? The reconciliation lives in an unfetched Fredenhagen article. **This is the corpus's most consequential unresolved disagreement.** +- (A5)/(R15): whether a substitute for a net tending spacelike to infinity exists over a compact Cauchy surface — [GLRV99]'s own stated open problem. +- (A22): whether the wedges of a spherically symmetric spacetime separate spacelike points; neither a proof nor a failing spacetime could be produced. +- Whether (D3)'s neighbourhood-buffered ⊥ and (D17)'s closure-based ⊥ are equivalent; no source in the corpus compares them, and (R4)'s proof consumes the buffer. +- Whether the ordering theorem's hypothesis (T) is the weakest such condition, and whether it has a purely order-theoretic surrogate — the adopted form cannot state (T). +- (R63): which field theories [DL84] §§9–10 exhibits as non-split; those pages were not converted. +- (R22): a funnel-property counterexample at a checkable tier; the corpus's only pointer is unfetchable, and (R39)'s alternative rests on a private communication. + +## Sources + +**Append-only.** + +| Key | Work | Status | Cache | Version | Tier reached | Retrieved | +|---|---|---|---|---|---|---| +| HM06 | H. Halvorson, M. Müger, *Algebraic Quantum Field Theory*, in *Handbook of the Philosophy of Physics*, arXiv math-ph/0602036 | retrieved (arXiv LaTeX, verbatim) | `references/arxiv-math-ph-0602036/` | arXiv | a | 2026-08-15 | +| GLRV99 | D. Guido, R. Longo, J. E. Roberts, R. Verch, *Charged sectors, spin and statistics in quantum field theory on curved spacetimes*, arXiv math-ph/9906019 | retrieved (arXiv LaTeX, verbatim) | `references/arxiv-math-ph-9906019/` | arXiv | a | 2026-08-15 | +| NAA13 | P. Naaijkens, *Quantum spin systems on infinite lattices*, arXiv 1311.2717 | retrieved (arXiv LaTeX, verbatim) | `references/arxiv-1311.2717/` | arXiv | a | 2026-08-15 | +| BGL93 | R. Brunetti, D. Guido, R. Longo, *Modular structure and duality in conformal quantum field theory*, arXiv funct-an/9302008 | retrieved (arXiv LaTeX, verbatim) | `references/arxiv-funct-an-9302008/` | arXiv | a | 2026-08-15 | +| BFV01 | R. Brunetti, K. Fredenhagen, R. Verch, *The generally covariant locality principle*, arXiv math-ph/0112041 | retrieved (arXiv LaTeX, verbatim) — **the cache holds two complete document bodies; all locators here are from the first** | `references/arxiv-math-ph-0112041/` | arXiv | a | 2026-08-15 | +| KOE03 | S. Köster, *Structure of Coset Models*, dissertation, arXiv math-ph/0308031 | retrieved (arXiv LaTeX, verbatim) | `references/arxiv-math-ph-0308031/` | arXiv | a | 2026-08-14 | +| dB74 | D. Buchholz, *Product states for local algebras*, Comm. Math. Phys. 36 (1974) 287–304 | retrieved **partial** — pp. 1–8 of 18 via MinerU; p. 292 additionally cross-checked against the PDF's own text layer with `pypdf`, independently of MinerU | `references/buchholz-1974-product-states/` | published | b | 2026-08-14 | +| DL84 | S. Doplicher, R. Longo, *Standard and split inclusions of von Neumann algebras*, Invent. Math. 75 (1984) 493–536 | retrieved **partial** — pp. 1–14 of 44 (§0–§4) via MinerU | `references/doplicher-longo-1984-standard-split/` | published | b | 2026-08-14 | +| HAAG | R. Haag, *Local Quantum Physics* | not retrieved — not attempted this run | — | — | d | 2026-08-15 | +| ROB76 | J. E. Roberts, *Local cohomology and superselection structure*, Comm. Math. Phys. 51 (1976) | not retrieved — not attempted this run | — | — | d | 2026-08-15 | +| BW-NETS | H. Baumgärtel, M. Wollenberg, *Causal Nets of Operator Algebras* — [GLRV99]'s cited treatment of nets over posets with a causal-disjointness relation | not retrieved — not attempted this run | — | — | d | 2026-08-15 | +| BF82 | D. Buchholz, K. Fredenhagen, *Locality and the structure of particle states*, Comm. Math. Phys. 84 (1982) — the source of the spacelike-cone index set | not retrieved — not attempted this run | — | — | d | 2026-08-15 | +| HOR | S. S. Horuzhy, *Introduction to Algebraic Quantum Field Theory* — [HM06]'s pointer to funnel-property counterexamples | not retrieved — not attempted this run | — | — | c | 2026-08-15 | +| FRED93 | K. Fredenhagen, *Global observables in local quantum physics* — the reconciliation of (R1) with (R19) | not retrieved — not attempted this run | — | — | d | 2026-08-15 | +| DHR | S. Doplicher, R. Haag, J. E. Roberts, *Local observables and particle statistics* I, II | not retrieved — not attempted this run | — | — | d | 2026-08-15 | + +Further works cited by the corpus and not retrieved (Schlieder, Brown, Verch, Keyl, Borchers, Hislop–Longo, Dimock, Longo, Buchholz–Schulz-Mirbach, Buchholz–Wichmann, Buchholz–D'Antoni, Yngvason, Guido–Longo–Wiesbrock, Fredenhagen–Gabbiani, Fredenhagen–Jörß, Summers, Araki, Sakai, Kay–Wald): every claim through them carries a substitution sentence and no locator. One of them — [BGL93]'s attribution of (R39) to Buchholz — is a *private communication*, so no locator can ever exist for it. + +## Not investigated + +- **The `[ext]` gap.** Lane 5 swept only the formalization landscape; **no lane checked any external mathematical result**. The refutation pass fetched none. Every one of the external edges above — Fredenhagen's global-observables construction (which decides the (R1)/(R19) disagreement), Horuzhy's funnel counterexamples, Schlieder, Brown, Verch, Keyl, Hislop–Longo, Longo, Buchholz–Wichmann, Summers, and the DHR papers — remains unexamined, so (R19), (R22), (R24), (R29), (R34), (R35), (R46), (R49) and the external legs of (R3) rest entirely on attestations. +- **Unconverted pages.** dB74 pp. 9–18, including the proof underlying (R60); DL84 pp. 15–44, including the §§9–10 non-split field theories of (R63). +- **No page-image comparison was possible** — `pdftoppm`, `pdftotext`, `mutool` and `gs` are all absent from this container — so **no MinerU quote reaches tier (a)**. dB74's p. 292 was cross-checked against the PDF's own text layer, which removes the MinerU-inference risk for that passage only; DL84 was not cross-checked at all. +- **Spacelike cones** are named as an index set by [GLRV99] and [NAA13] and **defined by neither**; [HM06] defers to an unfetchable source. No (D#) row exists for them and none can be written from this corpus, so whether the cone index set is an instance of the adopted form is **unverified**, not covered. +- **[GLRV99] Ch. 5's labelled geometric assumptions** (a), (b), (c), (c′) were read only as far as (A19) and (A22) required — the single largest gap in the hypothesis lane. [KOE03]'s coset and subnet machinery and [NAA13]'s lattice chapters were swept only for index-set statements. +- **Degeneracy probes skipped**: whether (T) is preserved under passing to `𝒦̃`-style conformal orbits in general; whether ⊥̃ and ⊥̂ can be separated on a *corpus* index set (they coincide on `S¹` and fail together on the sieve poset — the only separating object found was constructed for the purpose); closure-separation versus positive distance for unbounded regions. +- **Errata found but not chased**: [GLRV99]'s Extension-Theorem proof cites Lemma 3.A.4 where 3A.5 is needed, and prints three spellings of one label; its Lemma 3.6 says `dimension $\geq 2$` and then treats dimension two as the remaining case; [BGL93] Thm 3.3's proof cites a Lemma 3.4 that does not exist; [BFV01]'s `cf.\ condition $(ii)$` points at orientation-preservation where causal convexity is condition (i), **in both document bodies, so authorial rather than a conversion artefact**. +- **Not opened**: HS17 (`references/arxiv-1702.04924/`), cached from a previous run and outside this corpus. diff --git a/docs/math/separable-faithful-representation.md b/docs/math/separable-faithful-representation.md new file mode 100644 index 0000000..e118641 --- /dev/null +++ b/docs/math/separable-faithful-representation.md @@ -0,0 +1,538 @@ +--- +object: Faithful representation of a separable C*-algebra on a separable Hilbert space +slug: separable-faithful-representation +status: draft +worst-tier: c +mathlib-rev: 5450b53e5ddc75d46418fabb605edbf36bd0beb6 +implemented-as: CStarRep.exists_isometric_separable +revisions: + - 2026-08-16 · 2e21b4b · initial extraction · sources: BF26, LAN98, SHI12, VER25 +--- + +<!-- +Macros the quotes below need, copied from each source's own preamble: + \cB, \cH BF26, references/arxiv-2602.15812/raw/measpreambleu.tex:63, :79 + \cstar BF26, references/arxiv-2602.15812/raw/measpreambleu.tex:292 + \sfS BF26, references/arxiv-2602.15812/raw/25-Cstar-choice.tex:76 + \ca LAN98, references/arxiv-math-ph-9807030/raw/*.tex:53 + \rep LAN98, :56 + \Hs LAN98, :58 + \om LAN98, :233 + \A LAN98, :238 + \H LAN98, :269 (the source itself uses \renewcommand — KaTeX defines \H) + \cs, \ss SHI12, references/arxiv-1211.3404/raw/main.tex:65, :66 (\def, not \newcommand) +--> + +$$ +\newcommand{\cB}{{\mathcal B}} +\newcommand{\cH}{{\mathcal H}} +\newcommand{\cstar}{$\mathrm{C}^*$} +\newcommand{\sfS}{\mathsf S} +\newcommand{\ca}{$C^*$-algebra} +\newcommand{\rep}{representation} +\newcommand{\Hs}{Hilbert space} +\newcommand{\om}{\omega} +\newcommand{\A}{{\frak A}} +\renewcommand{\H}{{\cal H}} +\def\cs{{$C^{\ast}$}} +\def\ss{{$\ast$}} +$$ + +# Faithful representation of a separable C\*-algebra on a separable Hilbert space + +## What this object is for + +The Gelfand–Naimark theorem places every C\*-algebra inside the bounded operators +on *some* Hilbert space, but the Hilbert space its standard proof produces is +indexed by the whole state space and is enormous. This object is the refinement +that bounds the size of the Hilbert space by the size of the algebra: a +countable dense subset of the algebra buys a countable dense subset of the +Hilbert space. The generality that matters is that nothing beyond separability +is assumed — not unitality, not commutativity, not nuclearity — and that the +bound is one-directional: it is a sufficient condition on the algebra, never a +characterisation. + +## Definition + +### Variants as the sources write them + +| (D#) | Source | What is asserted | "faithful" reads as | "separable H" reads as | unitality | ambient theory | Tier | +|---|---|---|---|---|---|---|---| +| (D1) | [BF26] `SepRepThm` | the object | undefined in the source | undefined in the source | none assumed | ZF | a | +| (D2) | [BF26] `RepCAlg` | *representable*: no separability of H at all | isomorphism onto a **closed** \*-algebra of operators | — (H unrestricted) | none assumed | ZF | a | +| (D3) | [LAN98] `injmor` context | ingredient definition | **injective**; isometry derived | — | — | ZFC | a | +| (D4) | [VER25] Operator Algebra Basics | ingredient definition | **injective** (π(A)=0 ⟹ A=0); faithful *state*: ω(A\*A)=0 ⟹ A=0 | — | unital throughout | ZFC | a | +| (D5) | [SHI12] Approximate units | ingredient: separable **algebra** | — | — | — | ZFC | a | +| (D6) | [SHI12] Hilbert spaces | ingredient: separable **Hilbert space** | — | **countable orthonormal basis** | — | ZFC | a | +| (D7) | [SHI12], [LAN98] `GNSconstruction` | ingredient: H_φ as an explicit completion | — | — | LAN98 unital only | ZFC | a | + +**(D1) [BF26] Theorem `SepRepThm`** — tier (a) + +> `Every separable C*-algebra has a faithful representation as a concrete C*-algebra of operators on a separable Hilbert space.` + +BF26 nowhere defines "faithful representation": the word occurs six times in the +whole paper and never in a definition environment. The meaning has to be +imported, which is what (D3) and (D4) supply. + +**(D2) [BF26] Definition `RepCAlg`** — tier (a) + +> `A \cstar-algebra $A$ is {\em representable} if it is isomorphic to a closed *-algebra of bounded operators on a Hilbert space.` + +`differs from (D1) by:` (D2) demands an isomorphism onto a norm-**closed** +\*-subalgebra and says nothing about the Hilbert space, where (D1) demands a +faithful map and a **separable** Hilbert space. +`sources claim equivalence:` not addressed — BF26 uses both in one subsection +without remarking on the difference. + +**(D3) [LAN98] Lemma `injmor` and its use** — tier (a) + +> `An injective morphism between \ca s is isometric. In particular, its range is closed.` + +used in the sentence "‖π(A)‖ = ‖A‖ + +> `when $\pi$ is faithful by Lemma \ref{injmor}` + +`differs from (D1) by:` fixes faithful = injective and derives the other two +readings. +`sources claim equivalence:` yes — this lemma, together with [SHI12] +`cor:firstiso`, *is* the equivalence; see (R5). + +**(D4) [VER25] §Operator Algebra Basics** — tier (a) + +> `A state on a unital $*$-algebra $\mathbfcal{A}$ is called \textbf{faithful} if, for any $\Att \in \mathbfcal{A}$,` + +followed by the displayed ω(A\*A) = 0 ⟹ A = 0; faithfulness of a representation +is defined in the same passage by π(A) = 0 ⟹ A = 0. + +`differs from (D3) by:` nothing for representations; (D4) additionally defines +the *faithful state*, which (D3) does not and which (D1)'s justification +consumes. +`sources claim equivalence:` not addressed. + +**(D5) [SHI12] §Approximate units** — tier (a) + +> `A \cs-algebra $A$ is called {\bf separable} if it possesses a countable and dense subset.` + +BF26 uses the same notion without a definition environment, speaking throughout +of a countable dense subset and, in `L.states`, of "a countable dense sequence". + +**(D6) [SHI12] §Hilbert spaces** — tier (a) + +> `A Hilbert space is called {\bf separable} if it has a countable orthonormal basis.` + +immediately followed by + +> `\begin{exercise} Let $H$ be a Hilbert space. Show that $H$ is a separable Hilbert space if and only if $H$ is a separable topological space. \end{exercise}` + +`differs from (D5) by:` (D6) is basis-theoretic where (D5) is topological, and +the source relegates their equivalence to an exercise rather than proving it. +`sources claim equivalence:` yes, as an exercise — but see (X3): the two are +**provably inequivalent** in ZF, which is the ambient theory of the source that +states (D1). + +**(D7) [LAN98] Construction `GNSconstruction`, [SHI12] §Hilbert spaces** — tier (a) + +> `The \Hs\ $\H_{\om}$ is the closure of $\A/{\cal N}_{\om}$ in this inner product.` + +SHI12 builds the same object as `The completion of $A/N_\ff$ with respect to the norm defined by $\inner_\ff$`, and VER25 instead postulates the triple abstractly +with a cyclic vector. Only the completion form makes separability of H_ω +visible, which is why the corpus's failure to prove (R7) is a failure to compose +two of its own definitions. + +### Adopted general form + +Let `A` be a C\*-algebra over ℂ — an involutive Banach algebra with +‖x\*x‖ = ‖x‖² — **not** assumed unital and **not** assumed commutative, and +suppose `A` is separable as a metric space, that is, `A` has a countable dense +subset in the norm topology (D5). Then there exist a complex Hilbert space `H` +and a map `π` such that + +1. `H` has a countable dense subset in the norm topology; +2. `π : A → B(H)` is a \*-homomorphism — linear, multiplicative, and + star-preserving — into the bounded linear operators on `H`; +3. `π` is injective. + +Nothing further is assumed of `A`, and nothing further is asserted of `π`: in +particular the adopted form does **not** claim that `π` is nondegenerate, does +**not** claim `π(1) = 1` when `A` is unital, and does **not** claim that `H` may +be taken to be ℓ²(ℕ). The hypotheses carried are (A1) alone. Three consequences +follow from 1–3 with no extra input and are therefore not extra content (R5): +`π` is isometric, its range is norm-closed, and `π` corestricts to a +\*-isomorphism of `A` onto a C\*-subalgebra of `B(H)` — which is what makes the +phrase "concrete C\*-algebra of operators" in (D1) legitimate. + +*Justification.* This is (D1), the only formulation in the corpus that states +the object, with its two undefined phrases fixed. "Faithful" is read as +injective, following (D3) and (D4), the only sources in the corpus that define +the word; (X5) and (X6) record that injective, isometric, and "isomorphic onto a +norm-closed \*-subalgebra" coincide for C\*-algebras, so this reading costs no +generality and (D2) is not a competing variant. "Separable Hilbert space" is +read topologically rather than basis-theoretically, following (X3)'s +**(X5) conditional equivalence**: the two readings agree under the axiom of +countable choice and come apart without it, and BF26 — the source that states +(D1) — works in ZF and says so explicitly, so the topological reading is the +only one available to it. Under ZFC, the ambient theory of every other source in +the corpus, the choice is inert. + +## Notation and conventions + +| (C#) | Axis | This note | Per source | Translation | +|---|---|---|---|---| +| (C1) | what "faithful" means | injective | [LAN98] injective, isometry derived; [VER25] injective; [BF26] undefined, glossed parenthetically as isometric; [SHI12] no notion of faithful representation | the three readings coincide, by (R5) | +| (C2) | unitality | not assumed | every source does its state/GNS work in the unital case and defers the rest to unitisation; [BF26] `RepThm` says "separable (unital)" with the parenthesis, `SepRepThm` says nothing; none of the three writes the unitisation argument | (A2) | +| (C3) | what "separable Hilbert space" means | countable dense subset | [SHI12] countable orthonormal basis, equivalence set as an exercise; [BF26] topological, forced by its ZF setting | equivalent under countable choice only — (X3) | +| (C4) | ℓ²(ℕ) as the canonical space | not used in the statement | [SHI12] is careful — `When $H$ is an infinite dimensional separable Hilbert space, or equivalently $H\simeq \ell^2$`; the ℓ²(ℕ) phrasing appears in BF26 only in commented-out source | a formulation naming ℓ²(ℕ) silently excludes the finite-dimensional case unless ℓ² is read as allowing finite dimension | +| (C5) | conjugate-linearity of the inner product | first argument | [LAN98] `(A,B)_0:=\om(A^* B)`, first argument; [VER25] first argument; [SHI12] not determined | — | +| (C6) | nondegeneracy | not asserted | [LAN98] defines it and uses it elsewhere; 0 occurrences in [BF26] and [VER25]; no source attaches it to this object | (A9) | +| (C7) | ambient set theory | ZFC assumed, ZF tracked | [BF26] ZF throughout; [LAN98] [SHI12] [VER25] ZFC | the ZF/ZFC split is what makes (C3) a live axis and (R5) an edge rather than a triviality | + +## Results and dependencies + +### (R1) The object + +Every separable C\*-algebra has a faithful representation as a concrete +C\*-algebra of operators on a separable Hilbert space. + +- Source: [BF26] Theorem `SepRepThm` · tier (a) · **asserted** +- Depends on: (R2), (R6), (R7), (R5) +- Conventions: (C1), (C3), (C7) +- Verbatim: + > `Every separable C*-algebra has a faithful representation as a concrete C*-algebra of operators on a separable Hilbert space.` + +**No `Proof route:` field, because there is no proof.** There is no proof +environment after the theorem in BF26's source; the entire justification is the +single sentence preceding it: + +> `In fact, using \ref{FaithfulStateThm} we can do better, since the GNS representation from a faithful state is faithful:` + +That sentence supplies (R2) and asserts (R6). It says nothing about separability +of the GNS Hilbert space — the adjective that distinguishes this theorem from +the unrefined Gelfand–Naimark theorem (R8) — and nothing about the image being +norm-closed. Of the four things (R1) needs: one is proved in the stating source, +one is asserted twice and proved nowhere in the corpus, one is absent from the +corpus entirely, and one is proved only in ZFC sources while the stating source +works in ZF. + +### (R2) A separable C\*-algebra has a faithful state + +- Source: [BF26] Proposition `FaithfulStateThm` · tier (a) · **proved in source** +- Depends on: (R3), (R4), (A2), [ext: a countable convex combination of states is a state — used in step 3 and stated nowhere in the corpus] +- Conventions: (C2) +- Proof route: + 1. Reduce to the unital case — asserted, not argued, consuming (A2); + 2. `S(A)` is weak\*-separable — consumes (R3); + 3. take a weak\*-dense sequence (φₙ) in `S(A)` and set φ = Σ 2⁻ⁿ φₙ, a state + because a countable convex combination of states is a state; + 4. for positive `a` of norm 1 there is a state ψ with ψ(a) = 1 — consumes (R4); + 5. approximate ψ by some φₙ, giving φₙ(a) > ½, hence φ(a) ≥ 2⁻ⁿφₙ(a) > 0. +- Verbatim: + > `A separable \cstar-algebra has a faithful state.` + +Note which faithfulness is produced: step 5 yields φ(a) > 0 for positive `a`, +the order-theoretic form, where (D4) defines faithfulness of a state by +ω(A\*A) = 0 ⟹ A = 0. These are interchangeable, but BF26 neither says so nor +defines the term. + +### (R3) The state space of a separable unital C\*-algebra is separable, compact and metrizable in the weak\* topology + +- Source: [BF26] Lemma `L.S(A)` · tier (a) · **proved in source** +- Depends on: (R4), [ext: BF26's own separable Hahn–Banach / Banach–Alaoglu package], [ext: BF26's separable Krein–Milman], [ext: Blackadar, *Real Analysis* — the pure state space of a separable unital C\*-algebra is a G_δ subset of the state space] +- Conventions: (C2), (C7) +- Verbatim: + > `Suppose that $A$ is a separable unital \cstar-algebra. Then the state space $\sfS(A)$ with respect to the weak*-topology is a separable compact metrizable space.` + +Only the weak\*-separability clause is consumed by (R2). The Krein–Milman step +inside this proof supports the pure-state clause, which nothing on the route to +(R1) cites — see (A7). + +### (R4) Every separable C\*-algebra has sufficiently many states + +- Source: [BF26] Lemma `L.states` · tier (a) · **proved in source** +- Depends on: (A2), [ext: BF26's own positivity proposition — `x*x` is a positive element], [ext: BF26's separable Hahn–Banach extension], [ext: BF26's continuous functional calculus], [ext: Blackadar, *Operator Algebras* — a linear functional on a unital C\*-algebra is a state iff it has norm 1 and value 1 at the unit] +- Conventions: (C2), (C7) +- Proof route: + 1. reduce to `A` unital — asserted, consuming (A2); + 2. `x*x` is positive; + 3. ‖λ1 + x\*x‖ = λ + ‖x\*x‖ for λ ≥ 0, by functional calculus — consumes the + functional-calculus edge; + 4. on the two-dimensional span of 1 and `x*x`, the functional sending 1 ↦ 1 + and `x*x` ↦ ‖x‖² has norm 1; + 5. extend by Hahn–Banach for separable Banach spaces, choice-free — consumes + the Hahn–Banach edge; + 6. a norm-1 functional taking value 1 at the unit is a state — consumes the + Blackadar edge. +- Verbatim: + > `Every separable \cstar-algebra, and more generally every \cstar-algebra with a well-ordered dense subset, has sufficiently many states.` + +BF26 defines "sufficiently many states" as: for every `x` there is a state φ +with ‖π_φ(x)‖ = ‖x‖. The proof produces φ(x\*x) = ‖x‖²; the one-line bridge +between the two is **not written down in BF26**. + +### (R5) An injective \*-homomorphism of C\*-algebras is isometric, and its range is norm-closed + +- Source: [LAN98] Lemma `injmor`; [SHI12] Corollary `cor:csinjection` together with Corollary `cor:firstiso` · tier (a) · **proved in source** (SHI12), **sketched** as read by this extraction (LAN98) +- Depends on: [LAN98] its ideal theory +- Conventions: (C1), (C7) — both sources are ZFC +- Verbatim: + > `An injective morphism between \ca s is isometric. In particular, its range is closed.` + + > `Every injective \ss-homomorphism between two \cs-algebras is an isometry.` + +This is one result with two locators, kept as one row. It is what makes (C1) +inert and (D2) not a competing variant. It is also the edge that BF26 uses +parenthetically — `a direct sum of GNS representations is faithful (isometric)` — +without citing any lemma, which in a ZF paper is not free; see `## Open questions`. + +### (R6) The GNS representation of a faithful state is faithful + +- Source: [VER25] §Operator Algebra Basics · tier (a) · **asserted** +- Depends on: [VER25]'s GNS theorem, for the existence of the triple +- Conventions: (C2) — stated for unital algebras +- Verbatim: + > `For a unital $C^*$-algebra, if a state $\omega$ is faithful, then $(\pi_\omega,\mathcal{H}_\omega)$ is faithful.` + +This is exactly the step (R1)'s one-sentence justification consumes. The corpus +states it twice — here and inside BF26's sentence — and proves it zero times. +[LAN98] has the only *proved* proposition in this direction, and it is about the +compact operators specifically, not a general C\*-algebra. + +### (R7) The GNS Hilbert space of a state on a separable C\*-algebra is separable + +**Absent from the corpus.** No source states, proves or cites it. + +- Source: none · tier — · **not in corpus** +- Depends on: (D7) plus the general facts that a continuous image of a separable + space is separable and that the closure of a separable subset is separable +- Conventions: (C3) + +Both ingredients are present, in different sources: [VER25] gives cyclicity of +Ω_ω, and [LAN98]/[SHI12] give H_ω as the closure of a quotient of `A` (D7). +Either yields H_ω as the closure of a continuous image of `A`, hence separable +when `A` is, with no choice used because the dense sequence in H_ω is the +*image* of a fixed dense sequence in `A`. **No source performs the +composition.** This is the entire content of the refinement over (R8), and it is +the one step the literature in this corpus leaves unwritten. + +### (R8) The unrefined Gelfand–Naimark representation theorem + +- Source: [LAN98] Theorem `GNT` · tier (a) · **proved in source**; announced without proof by [SHI12] +- Depends on: [LAN98] `defunivrep`, `gnscor`, `lots`, the C\*-identity, (R5) +- Conventions: (C1) +- Proof route: + 1. take `H` to be the universal representation's space ⊕_{ω∈S(A)} H_ω — + consumes `defunivrep`; + 2. π_u(A) = 0 forces π_ω(A) = 0 for every state — by definition of direct sum; + 3. hence ω(A\*A) = 0 for every state — consumes `gnscor`; + 4. hence ‖A\*A‖ = 0 — consumes `lots`, "there are lots of states"; + 5. hence A = 0 by the C\*-identity; + 6. injective ⟹ isometric — consumes (R5). +- Verbatim: + > `A \ca\ is isomorphic to a subalgebra of $\B(\H)$, for some Hilbert space $\H$.` + +### (R9) The universal Hilbert space is too large, and the pure-state reduction does not fix it + +- Source: [LAN98], the paragraph after the proof of `GNT` · tier (a) · **remark (asserted)**, with a **proved** reduction to pure states +- Depends on: (R8), [LAN98] `lotsp`, [ext: Krein–Milman, which LAN98 states without proof] +- Verbatim: + > `While the universal \rep\ leads to a nice proof of \ref{GNT}, the \Hs\ $\H_{\mbox{\tiny u}}$ is absurdly large; in practical examples a better way of obtaining a faithful \rep\ always exists.` + +This is the corpus's own statement that (R8) does not deliver a separable +Hilbert space. LAN98's remedy is to sum over pure states, and then over one pure +state per unitary-equivalence class — a reduction that for a separable algebra +still leaves an uncountable index set. See (X1) and (X2). + +### (R10) `B(H)` is norm-separable if and only if `H` is finite-dimensional + +- Source: [BF26] Theorem `BHNormSepThm` · tier (a) · **proved in source** (read as a sketch by this extraction) +- Depends on: [BF26] `OrthProjSeqProp`, and its choice-free replacement for the orthonormal-sequence argument +- Verbatim: + > `Let $\cH$ be a Hilbert space. Then $\cB(\cH)$ is norm-separable if and only if $\cH$ is finite-dimensional.` + +No step of (R1) consumes this, so it is not an edge. It is the boundary object: +it shows that "faithful representation on a separable Hilbert space" must not be +confused with "faithful representation into a separable `B(H)`", which would +force finite dimension and trivialise the object. [LAN98] makes the same +observation from the other side: `Another argument against $\BH$ is that it is non-separable in the nom-topology even when $\H$ is separable.` (the typo `nom-` is +in the original). + +### (R11) Every closed subspace of a separable Banach space is separable + +- Source: [BF26] Proposition `ClosedSubspaceProp` · tier (a) · **cited elsewhere** +- Depends on: [ext: Blackadar, *Real Analysis* — every closed subset of a separable metric space is separable] + +Not an edge of (R1). With (R1) it gives that the image π(A) is itself a +separable concrete C\*-algebra. + +### (R12) Two Hilbert spaces are unitarily equivalent iff their orthonormal bases are equinumerous + +- Source: [SHI12] Corollary `cor:Hilbertcardinality` · tier (a) · **proved in source** +- Depends on: [SHI12]'s theorem that any two orthonormal bases are equinumerous +- Conventions: (C3), (C4), (C7) + +This is what would licence replacing "separable Hilbert space" by ℓ²(ℕ) in the +infinite-dimensional case. It is unusable in BF26's ZF setting (C3), and SHI12's +equinumerosity proof makes an unflagged simultaneous choice. + +### (R13) Every separable commutative C\*-algebra is C₀(X) for a locally compact metrizable X + +- Source: [BF26], the separable commutative Gelfand theorem · tier (a) · **proved in source** +- Depends on: (R4), (A7) Krein–Milman, Stone–Weierstrass +- Conventions: (C7) + +Recorded because it gives a second, independent route to (R1) in the commutative +case, and because that route is **strictly more expensive** in ZF than the +general one: it consumes Krein–Milman, which the general route does not (A7). + +## Hypotheses + +| (A#) | Statement | Class | Evidence | Witness | Scope | Tier | Used by | +|---|---|---|---|---|---|---|---| +| (A1) | `A` is norm-separable | antecedent — the hypothesis of the theorem, not a side condition | [BF26] `SepRepThm`; [SHI12] definition | — | standing | a | (R1)–(R4) | +| (A1b) | separability of `A` is **not necessary** for the conclusion | — (a finding: the hypothesis cannot be made an iff) | [BF26] `BHNormSepThm`; [LAN98] | **B(ℓ²)**, non-separable yet faithfully represented on ℓ² by its identity representation; likewise ℓ^∞(ℕ) | — | a | — | +| (A2) | `A` is unital | provable — removable by unitisation | [BF26] `L.states`, `FaithfulStateThm` both open "We may assume `A` is unital", unargued; [LAN98] defers to `extstate` | — (non-unital instance C₀(ℝ) is covered) | local | a | (R2), (R4) | +| (A3) | `A` is σ-unital | provable from (A1) | [SHI12] "Every separable C\*-algebra is σ-unital"; [LAN98] `exau` | — | local | a | — (consumed by no step of the adopted route) | +| (A4) | `A` has sufficiently many states / is representable | provable under (A1); **model-dependent** without it | [BF26] `L.states`, `P.Representable`, `P.ExistenceOfStates` | **ℓ^∞(ℕ)/c₀(ℕ)** and the **Calkin algebra**, which have no states at all in ZF models where every set of reals has the Property of Baire | local | a | (R2) | +| (A5) | `A` has a faithful state | provable from (A1) | [BF26] `FaithfulStateThm` | — | local | a | (R1) | +| (A6) | `S(A)` is weak\*-separable | provable from (A1)+(A2); model-dependent without (A1) | [BF26] `L.S(A)`, `P.Russel.example` | the **Russell-set algebra** — concretely representable, unital, commutative, AF, non-separable, with a state space that is not compact and has no extreme points | local | a | (R2) | +| (A7) | Krein–Milman / nonemptiness of the pure state space | **not a hypothesis of this theorem** | [BF26]: the route `SepRepThm ← FaithfulStateThm ← L.S(A)` cites only weak\*-separability, itself from Banach–Alaoglu plus metrizability, both proved outright in ZF | — | — | a | (R13) only | +| (A8) | `H_φ` is separable when `A` is | provable from (A1) — **and no source in the corpus proves it** | the argument is written out at (R7); the corpus supplies only the ingredients | — | local | a | (R1) | +| (A9) | the representation is nondegenerate | provable for `A ≠ 0`; fails at `A = 0` | [LAN98] defines nondegeneracy; the GNS representation of a state on a unital algebra is cyclic, and cyclic ⟹ nondegenerate | `A = 0` | local | a | — (not asserted by the adopted form) | + +Hypotheses searched for and found attached nowhere in this corpus: nuclearity, +exactness, simplicity, stable rank, existence of a tracial state, σ-finiteness, +type classification, amenability. The theorem rests on separability alone. + +## Degeneracies and boundary cases + +| Case | Effect on the adopted form | Tier | +|---|---|---| +| intended case is nonvacuous | **C([0,1])** — separable by Stone–Weierstrass over ℚ+iℚ, infinite-dimensional, and faithfully represented by multiplication operators on the separable space L²([0,1], m); second instance, the compacts 𝒦 = K(ℓ²) with their defining representation | a | +| zero object / scalars | `A = 0` is separable and the unique map into any `B(H)` is injective, so the adopted form holds trivially. It breaks two natural strengthenings: "π is nondegenerate on a nonzero H" and "π is unital" — see (X7) | a | +| finite-dimensional | no effect; conclusion automatic, since a finite-dimensional C\*-algebra is a direct sum of matrix algebras and sits in `B(ℂⁿ)`. By (R10) this is the **only** case where the ambient `B(H)` is itself norm-separable | a | +| commutative | no effect on truth; a second route exists via (R13) and multiplication operators on L²(X,μ), and that route is strictly more expensive in ZF because it consumes Krein–Milman (A7) | a | +| non-separable / non-σ-finite | **conclusion false.** Witness **c₀(ω₁)**: its ℵ₁ many singleton characteristic functions are nonzero mutually orthogonal projections, whose images under a faithful π are nonzero mutually orthogonal projections; the least-index argument BF26 uses for ε-discrete sets then injects ω₁ into ℕ. Non-σ-unital cannot arise at all, by (A3) | a | +| type III | outside the scope, provably: no infinite-dimensional von Neumann algebra is norm-separable, by BF26's 2^ℵ⁰-sized 1-discrete family of projections. Witnesses: the hyperfinite II₁ factor, L^∞([0,1]) — both act on separable Hilbert spaces and neither is a separable C\*-algebra | a | +| non-unital / degenerate representation | no effect; (A2) discharges unitality by unitisation and the adopted form asserts no nondegeneracy. Named non-unital separable instance: C₀(ℝ) | a | +| universally orthogonal index element | no effect on the adopted form. In the direct-sum formulations the index set is the state space; its degenerate extreme `A = ℂ` gives a one-dimensional sum, harmless, and its opposite extreme — uncountable state space — is what destroys those formulations (X1) | a | +| quantifier swap: ∀…∃… ↦ ∃…∀… | swapping to "for every `a ≠ 0` there is a representation on a separable H not killing `a`" gives BF26's weaker `RepThm`. Under (A1) the two are **equivalent**, by a route no source states: index by a dense sequence (aₙ) of the algebra, take the state norming each aₙ from (R4), and form the countable direct sum ⊕ₙ π_{ψₙ}. Each summand is separable by (A8), so the countable sum is separable in ZF, and the representation is isometric on a dense set | a | +| quantifier swap: ∃ separable H ↦ ∀ separable H | **false.** `A = M₂(ℂ)`, `H = ℂ`: `B(ℂ) = ℂ` admits no injective \*-homomorphism from M₂(ℂ) | a | +| hypothesis dropped: (A1) | nothing survives — false, witness c₀(ω₁) above | a | +| hypothesis dropped: (A2) | everything survives — discharged by unitisation | a | +| hypothesis dropped: (A5) | everything survives, via the quantifier-swap route above, which avoids the faithful state entirely | a | +| hypothesis dropped: (A7) | everything survives for this theorem; the *commutative* Gelfand theorem (R13) falls | a | +| hypothesis dropped: (A9) | everything survives, except at `A = 0` | a | + +## Rejected formulations and refuted claims + +**Append-only.** + +| id | Candidate or claim | Disposition | Discriminator | Tier | Date | +|---|---|---|---|---|---| +| (X1) | take the direct sum of the GNS representations of **all** states — the universal representation | rejected | **(X1) separating object** — **ℂ²**: the cyclic vectors Ω_ω lie in mutually orthogonal summands and form an orthonormal family of cardinality \|S(A)\|, uncountable for every algebra of dimension ≥ 2, while ℂ² is separable. [LAN98] flags the defect (`absurdly large`) but adopts the construction, so this is not an (X4) | a | 2026-08-16 | +| (X2) | take one pure state from each unitary-equivalence class, and sum over those — [LAN98]'s own refinement | rejected | **(X1) separating object** — **C([0,1])**: its pure states are exactly the point evaluations (`The pure state space of the commutative \ca\ $C_0(X)$ (equipped with the relative $w^*$-topology) is homeomorphic to $X$.`), each with a one-dimensional GNS space and a distinct kernel, so the classes are still continuum many. C₀(ℝ) works identically | a | 2026-08-16 | +| (X3) | read "separable Hilbert space" as "has a countable orthonormal basis" | **(X5) conditional equivalence** | assumption named: the axiom of countable choice. [BF26], in ZF: `an infinite-dimensional Hilbert space need not contain an orthonormal sequence, or even an infinite orthonormal set`, and in the other direction there is a nonseparable Hilbert space carrying a sequence of mutually orthogonal rank-one projections. [SHI12] *adopts* the basis reading, so this is a definitional variant (D6), not a rejection | a | 2026-08-16 | +| (X4) | state the conclusion as "on ℓ²(ℕ)" | equivalent | for `A ≠ 0`, by amplification and (R12); only the *nondegenerate*-on-ℓ² variant dies at `A = 0`. Caveat (C4): the phrasing silently excludes finite dimension unless ℓ² is read loosely | a | 2026-08-16 | +| (X5) | demand "faithful **and** isometric" as two separate conditions | equivalent | (R5) — injective ⟹ isometric for C\*-algebras, so the second demand is empty | a | 2026-08-16 | +| (X6) | state it as "`A` is \*-isomorphic to a norm-closed \*-subalgebra of `B(H)`" rather than as an injective homomorphism | equivalent | (R5) — the image of an injective \*-homomorphism is automatically norm-closed. This is [BF26]'s own (D2) phrasing | a | 2026-08-16 | +| (X7) | strengthen the conclusion by demanding the representation be nondegenerate, or unital when `A` is | rejected | **(X2) degeneracy** — `A = 0`: no nondegenerate representation on a nonzero Hilbert space exists, and no unital \*-homomorphism `0 → B(H)` exists for `H ≠ 0` | a | 2026-08-16 | +| (X8) | strengthen "separable" to a characterisation — "`A` is separable **iff** it has a faithful representation on a separable Hilbert space" | refuted | **B(ℓ²)** is not norm-separable (R10) yet its identity representation on the separable space ℓ² is faithful. The hypothesis is sufficient and never necessary | a | 2026-08-16 | +| (X9) | strengthen the conclusion to "on ℓ² with π(A) containing no nonzero compact operator", or any similar structural normalisation of the image | open — could not separate; searched all three sources for `compact`, `essential`, `Calkin` and read [LAN98]'s compact-operator section | — | c | 2026-08-16 | +| (X10) | weaken "separable" to "has a well-ordered dense subset" in the conclusion as well as the hypothesis | open — could not separate. [BF26] pairs the two hypotheses systematically elsewhere (`Every separable \cstar-algebra, and more generally every \cstar-algebra with a well-ordered dense subset, has sufficiently many states.`) but stops short of the analogue for the representation theorem | — | c | 2026-08-16 | +| (X11) | *claim*: [BF26] proves `SepRepThm` | refuted | there is **no proof environment after the theorem** in BF26's LaTeX source: `\begin{Theorem}\label{SepRepThm}` is followed by the statement, `\end{Theorem}`, a commented-out paragraph, and then the commutative Gelfand theorem. The whole justification is the one preceding sentence, which never mentions separability of the Hilbert space | a | 2026-08-16 | +| (X12) | *claim*: the separable refinement is obtained by trimming the pure-state direct sum to a countable subfamily of pure states | refuted | (X2)'s separating object. The trimming that does work indexes by a dense sequence **of the algebra**, not by states — see the quantifier-swap row in `## Degeneracies` | a | 2026-08-16 | + +## Prior art + +| System | Found | Relation to variants | How searched | Measured at | +|---|---|---|---|---| +| Mathlib | `PositiveLinearMap.PreGNS`, `.GNS` (a `UniformSpace.Completion`), `.gnsNonUnitalStarAlgHom`, `.gnsStarAlgHom` in `Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean` | partial — supplies the GNS construction of (D7) for a positive linear functional, with **no** cyclic vector (its own TODO asks for one), no faithfulness, no isometry, no nondegeneracy, no separability | `find` for `*GelfandNaimark*`; full read of the file; `lean_leanfinder "faithful representation of a C*-algebra on a Hilbert space; injective star algebra homomorphism into bounded operators"` | `5450b53e5ddc75d46418fabb605edbf36bd0beb6` | +| Mathlib | `NonUnitalStarAlgHom.norm_map`, `.isometry` in `Mathlib/Analysis/CStarAlgebra/Hom.lean` | same as (R5) — an injective non-unital \*-homomorphism of complex C\*-algebras is isometric, already a theorem | `lean_leansearch "C*-algebra embeds isometrically into bounded operators on a Hilbert space"`; read of the file | `5450b53e…` | +| Mathlib | `gelfandTransform_isometry`, `gelfandTransform_bijective`, `gelfandStarTransform` in `GelfandDuality.lean` | unrelated — the **commutative** Gelfand–Naimark theorem, a different statement under a stronger hypothesis; it shares only the name | file read | `5450b53e…` | +| Mathlib | could not find a **noncommutative** Gelfand–Naimark theorem in any form | — | `grep -rni "gelfand.naimark" Mathlib/ --include=*.lean -l` (one file, the GNS one); `lean_leansearch "C*-algebra embeds isometrically into bounded operators on a Hilbert space"`; GitHub PR search `repo:leanprover-community/mathlib4 Gelfand Naimark is:pr` (one PR, #33116, the GNS construction) | `5450b53e…` | +| Mathlib | could not find a notion of a **state** on a C\*-algebra, nor of a **faithful** state or representation | — the GNS input is `PositiveLinearMap`, with no normalisation and no faithfulness predicate | `grep -rn "faithful" Mathlib/Analysis/CStarAlgebra/ Mathlib/Analysis/InnerProductSpace/ -i`; `grep -rni "\bstate\b" Mathlib/Analysis/CStarAlgebra/*.lean`; `grep -rln "QuasiState\|StateSpace\|IsState\b" Mathlib/` | `5450b53e…` | +| Mathlib | `TopologicalSpace.SeparableSpace` (the `@[mk_iff]` class), `DenseRange.separableSpace`, `SeparableSpace.of_denseRange`, `IsSeparable.image`, `isSeparable_range`, `IsSeparable.span`, countable products, `UniformSpace.separableSpace_completion` | supplies (C3)'s topological reading and, composed, supplies the corpus's missing (R7): the GNS space is a completion of a type synonym of the algebra, so dense-range transfer followed by separability of completions covers it | `grep`/read of `Topology/Bases.lean`, `UniformSpace/{Cauchy,Completion}.lean`, `Topology/Algebra/Module/Basic.lean` | `5450b53e…` | +| Mathlib | `IsHilbertSum`, `IsHilbertSum.linearIsometryEquiv`, `HilbertBasis`, `exists_hilbertBasis` in `InnerProductSpace/l2Space.lean` | partial — "every Hilbert space is unitarily an ℓ²" in unindexed Zorn generality, i.e. (R12) without the cardinality bookkeeping | file read | `5450b53e…` | +| Mathlib | could not find separability of `lp` over a countable index, nor "separable iff countable Hilbert basis", nor "a separable Hilbert space is unitarily ℓ²(ℕ)" | — this is where (C4)/(R12) have no counterpart | `grep -rni "separab" Mathlib/Analysis/InnerProductSpace/ Mathlib/Analysis/Normed/Lp/`; `lean_leansearch "separable Hilbert space has a countable orthonormal basis"`; `lean_leansearch "lp space is separable when the index type is countable"`; `lean_loogle "TopologicalSpace.SeparableSpace (lp _ _)"` (empty) | `5450b53e…` | +| Mathlib | `WeakDual.isCompact_closedBall`, `WeakDual.isCompact_polar`, `WeakDual.exists_countable_separating`, `WeakDual.metrizable_of_isCompact`, sequential Banach–Alaoglu | supplies both halves of (R3)'s analytic input — Banach–Alaoglu and metrizability of weak\*-compact subsets for a separable normed space. Could not find the composed statement that the dual ball is weak\*-separable | `grep -rni "alaoglu"`; `grep -rni "metrizab" Mathlib/Analysis/Normed/Module/WeakDual.lean`; `lean_leansearch "separable normed space dual unit ball weak-star separable"` | `5450b53e…` | +| Mathlib | could not find "`B(H)` is norm-separable iff `H` is finite-dimensional" (R10) | — | `grep -rni "separab" Mathlib/Analysis/Normed/Operator/*.lean Mathlib/Analysis/Normed/Module/Dual.lean`; `grep -rn "SeparableSpace (.*→L\[" Mathlib/ --include=*.lean` | `5450b53e…` | +| Isabelle AFP | `Complex_Bounded_Operators` — complex normed/Banach/Hilbert spaces, `cblinfun`, unitaries, projectors, BLT, adjoints, Loewner order | unrelated — supplies the ambient `B(H)`, not the object. The abstract names no C\*-algebras, no Gelfand–Naimark, no GNS, no separability; only the abstract was read | WebSearch for the AFP entry and for `arXiv:2512.05878`; the AFP search page itself is client-side rendered and returned nothing — an instrument failure, not an absence | 2026-08-16 | +| Coq/Rocq | could not find any formalization of Gelfand–Naimark in either form, or of C\*-algebras as a structure | — | one WebSearch, `Coq mathcomp-analysis Gelfand-Naimark C*-algebra formalization`; a thin measurement | 2026-08-16 | +| Lean Zulip | could not find a thread on the noncommutative theorem or on faithful representations | — the archive indexes poorly, so this is a weak negative. One community artefact found: a blog post on the merged GNS work, with no future-work items | WebSearch restricted to `leanprover.zulipchat.com` | 2026-08-16 | + +## Open questions + +- Whether the equivalence "injective ⟹ isometric ⟹ closed range" (R5), which + [BF26] uses parenthetically, survives in [BF26]'s ambient ZF. Both corpus + proofs of it are ZFC and go through the continuous functional calculus; [BF26] + cites no lemma at that point. This is the fourth of (R1)'s four dependencies + and the only one whose *scope* rather than existence is in doubt. +- (X9) — whether the image can be structurally normalised. +- (X10) — whether the well-ordered-dense-subset generalisation has a matching + conclusion about the Hilbert space. +- Whether the unitisation reduction (A2) preserves faithfulness of the state as + well as of the representation: every source in the corpus asserts the + reduction and none writes it out. +- Whether [SHI12] takes the inner product conjugate-linear in the first or the + second argument (C5) — not determined. + +## Sources + +**Append-only.** + +| Key | Work | Status | Cache | Version | Tier reached | Retrieved | +|---|---|---|---|---|---|---| +| BF26 | B. Blackadar, I. Farah, *Separable C\*-algebras Without the Countable Axiom of Choice*, arXiv:2602.15812 | retrieved | `references/arxiv-2602.15812/` | arXiv | a | 2026-08-16 | +| LAN98 | N. P. Landsman, *Lecture Notes on C\*-algebras, Hilbert C\*-modules and Quantum Mechanics*, arXiv math-ph/9807030 | retrieved | `references/arxiv-math-ph-9807030/` | arXiv | a | 2026-08-16 | +| SHI12 | V. Shirbisheh, *Lectures on C\*-algebras*, arXiv:1211.3404 | retrieved | `references/arxiv-1211.3404/` | arXiv | a | 2026-08-16 | +| VER25 | R. Verch, *Lecture Notes on Operator Algebras and Quantum Field Theory*, arXiv:2507.00900 | retrieved | `references/arxiv-2507.00900/` | arXiv | a | 2026-08-16 | +| MUR90 | G. J. Murphy, *C\*-algebras and Operator Theory*, Academic Press 1990 — reported to state the Gelfand–Naimark theorem together with the refinement that a separable algebra may be represented on a separable Hilbert space; no locator, the work was not opened | not retrieved — no legitimate free copy found; only a web search was attempted | — | — | d | 2026-08-16 | +| PED79 | G. K. Pedersen, *C\*-algebras and their Automorphism Groups*, Academic Press 1979 | not retrieved — same | — | — | d | 2026-08-16 | +| DIX77 | J. Dixmier, *C\*-algebras*, North-Holland 1977 | not retrieved — same | — | — | d | 2026-08-16 | +| BLA06 | B. Blackadar, *Operator Algebras: Theory of C\*-Algebras and von Neumann Algebras*, Springer 2006 — cited by BF26 for the definition of a C\*-algebra and for "norm 1 and φ(1)=1 ⟹ state" | not retrieved | — | — | c (through BF26) | 2026-08-16 | +| BLA-R | B. Blackadar, *Real Analysis* — cited by BF26 for "closed subsets of separable metric spaces are separable" and for the pure state space being G_δ | not retrieved | — | — | c (through BF26) | 2026-08-16 | +| BLA-H | B. Blackadar, *Hilbert spaces* (2023) — cited by BF26 for the ZF pathologies: a Hilbert space with no orthonormal basis, Russell and Cohen-finite sets | not retrieved | — | — | c (through BF26) | 2026-08-16 | +| TAK79 | M. Takesaki, *Theory of Operator Algebras I*, Springer 1979 | not retrieved — same | — | — | d | 2026-08-16 | + +## Not investigated + +- **The `[ext]` gap.** Lane 2 marked five external edges; lane 5 reached none of + them, because all five point into books nobody obtained. They are: Blackadar, + *Operator Algebras* — a linear functional on a unital C\*-algebra is a state + iff it has norm 1 and value 1 at the unit (consumed by (R4)); Blackadar, + *Real Analysis* — closed subsets of separable metric spaces are separable + (consumed by (R11)) and the pure state space of a separable unital + C\*-algebra is G_δ (consumed by the clause of (R3) that nothing on the route + to (R1) uses); Blackadar, *Hilbert spaces* — the ZF pathologies underwriting + (X3); and Krein–Milman as [LAN98] states it, without proof (consumed by (R9), + not by (R1)). **(R4) is on the route to (R1), so the load-bearing chain does + pass through a tier (c) edge** — this is what sets `worst-tier: c`. +- **BF26's own internal edges** were read as statements only, not chased: its + separable Hahn–Banach / Banach–Alaoglu package, its separable Krein–Milman, + its continuous functional calculus, and its positivity proposition. +- **(R7) is absent from the corpus and the argument in this note is the note's + own**, not any source's. Anyone relying on (R1) is relying on a step no source + in this corpus writes down. +- **(R5)'s scope in ZF** is unresolved — see `## Open questions`. Every claim + that BF26's `SepRepThm` delivers a *concrete* C\*-algebra of operators, rather + than merely an injective homomorphism, rests on it. +- **The five textbooks were never opened**, so the attributions that motivated + this extraction — that Murphy, Pedersen, Dixmier, Blackadar and Takesaki each + state the refinement alongside the Gelfand–Naimark theorem — are unverified. + No row in this note carries a locator into any of them. Whether the refinement + sits in the body of a numbered result or in a following remark is therefore + still unknown, and that was one of the questions the extraction set out to + answer. +- **Proof reading depth.** (R5) as LAN98 states it, (R10), and [LAN98]'s + compact-operator proposition were skimmed rather than read through, and are + recorded as sketched. [LAN98]'s `spc`/`spc5` and its ideal theory were not + opened. +- **[VER25] was mis-scoped by the orchestrator** and only partly recovered: it + contains no occurrence of "separable" or "Gelfand", but it is the corpus's + only source for (D4) and (R6). Lanes 3 and 4 accepted the mis-scoping and did + not open it, so its degeneracy and hypothesis content is unmeasured. +- **Not searched**: HOL Light, Metamath, Mizar, and Lean's `Mathlib/Archive`. + The Isabelle AFP entry was measured from its abstract only, and the Coq/Rocq + and Zulip negatives rest on one search each. +- **The unexamined base.** Everything above stands on the tier (c) rows in + `## Sources` — the three Blackadar works reached only through BF26's + citations — and on the two tier (c) `open` rows (X9) and (X10). diff --git a/docs/math/split-inclusion.md b/docs/math/split-inclusion.md new file mode 100644 index 0000000..beb1cdb --- /dev/null +++ b/docs/math/split-inclusion.md @@ -0,0 +1,590 @@ +--- +object: Split inclusion of von Neumann algebras +slug: split-inclusion +status: draft +worst-tier: c +mathlib-rev: 5450b53e5ddc75d46418fabb605edbf36bd0beb6 +implemented-as: VonNeumannAlgebra.IsSplitInclusion +revisions: + - 2026-08-14 · 4d8a21b · initial extraction · sources: KOE03, HS17, dB74, DL84 + - 2026-08-15 · 4ec09ec · format migration: variant grid, nonvacuity row, adopted-form display · no new claims + - 2026-08-15 · 4ec09ec (working tree) · prior-art locator refresh after rename/refactor (`exists_tensor_decomposition`, `VonNeumannNet`) · no new claims + - 2026-08-15 · <working tree, uncommitted> · prior-art refresh after the diagonal-algebra non-split witness · no new claims +--- + +<!-- +No document-level macro preamble: measured through +@vscode/markdown-it-katex (the plugin VS Code's own Markdown preview uses), +no macro definition form -- \newcommand, \gdef, \global\def -- survives +from one math span to the next, so a preamble here would leave every quote +using it broken (see check_render.py / render_check.js). Instead, every +verbatim-quote math span that needs a source's own macro carries a local, +self-contained \gdef of exactly that macro, e.g. +`$\gdef\lok#1{{\mathcal #1}}\lok{B}$` -- defined and used inside the same +$...$ pair, so it renders correctly without any state surviving to the next +span. The \gdef prefix is presentation, not content: strip it before +comparing a quote's tex against source.flat.txt for the quote check, and +audit it against the source's own definition below. + +Source macro catalogue (name[arity] = body, source, file:line): + \A = {\mathfrak{A}} HS17, references/arxiv-1702.04924/raw/main.tex:151 + \H = {\mathcal{H}} HS17, main.tex:196 — \renewcommand, not \newcommand: \H is the Hungarian-umlaut accent in KaTeX's own macro table, which is why the source redefines it too + \bC = {{\mathbb C}} HS17, main.tex:97 + \lok[1] = {{\mathcal #1}} KOE03, references/arxiv-math-ph-0308031/raw/mathphkoediss.tex:144 +--> + +# Split inclusion of von Neumann algebras + +## What this object is for + +A split inclusion upgrades mere commutation of two von Neumann algebras to +*statistical independence*: an intermediate type I factor lets states be +prescribed independently on the two sides, as if the pair acted on a tensor +product. In local quantum physics the split property of a net (split inclusions +for all suitably separated region pairs) is the standing regularity hypothesis +behind the type III₁ structure of local algebras, the quantum Noether theorem, +and the finiteness/positivity theory of entanglement measures. The general +two-algebra form matters because the literature proves its structure theory +(canonical interpolating factor, product states, tensor decompositions) at that +level and only then specialises to nets. + +## Definition + +### Variants as the sources write them + +| (D#) | Source | Level | Interpolating 𝔑 squeezed against | Extra data | Tier | +|---|---|---|---|---|---| +| (D1) | [KOE03] | net (chiral) | larger local algebra 𝔅(I₂) | — | a | +| (D2) | [HS17] | pair | — (abstract isomorphism 𝔄_A ∨ 𝔄_B ≅ 𝔄_A ⊗ 𝔄_B, no interpolating factor) | — | a | +| (D3) | [HS17] | pair | commutant 𝔄_B′ | vector \|Ψ⟩ cyclic for each, separating for the join | a | +| (D4) | [dB74] | net (QFT regions) | commutant ℛ(O₂)′ | normal product state | b | +| (D5) | [HS17] | pair | undefined ("intermediate" never pinned) | — | a | +| (D6) | [DL84] | pair | arbitrary second algebra B | — | b | +| (D7) | [DL84] | pair | — (split after tensoring with an auxiliary type I factor M) | auxiliary type I factor M | b | + +**(D1) [KOE03] §2.2 ("Split property for chiral subnets"), eq. `eq:splitprop`, source.txt 1697–1707** — tier (a) — net-level, nested form + +> a chiral net $\gdef\lok#1{{\mathcal #1}}\lok{B}$ has the split property, if for any pair $I_{1,2}$ of proper intervals satisfying $\overline{I_1}\subset I_2$ there is a type $I$ factor $\gdef\lok#1{{\mathcal #1}}\lok{M}$ interpolating between $\gdef\lok#1{{\mathcal #1}}\lok{B}(I_1)$ and $\gdef\lok#1{{\mathcal #1}}\lok{B}(I_2)$ + +with the display Λ: 𝔅(I₁) ⊂ 𝔐 ⊂ 𝔅(I₂), Ī₁ ⊂ I₂ ⋐ S¹. A net-level property, +quantified over all pairs of proper intervals with closure containment, +demanding a type I **factor** squeezed against the **local algebra of the +larger region**. KOE03 presents it as "the usual definition (adapted to our +context; cf FG93 definition 2.11)" — the wording is KOE03's adaptation, and the +FG93 locator is KOE03's citation (tier (c) toward FG93, not retrieved). KOE03 +also asserts, without listing them: "There are equivalent formulations of this +property." + +**(D2) [HS17] §2, displayed definition, source.txt 985–993** — tier (a) — statistical independence + +> The algebras $\gdef\A{\mathfrak{A}}\A_A$ and $\gdef\A{\mathfrak{A}}\A_B$ are said to be statistically independent iff there is an isomorphism of the v. Neumann algebras $\gdef\A{\mathfrak{A}}\A_A\vee\A_B \simeq \A_A\otimes\A_B$. + +For two commuting von Neumann algebras on a common ℋ, with ∨ and ⊗ defined at +the definition site and the notion pinned as "$W^*$-independence in the product +sense" (citation florig, not retrieved). + +`differs from (D1) by:` single-pair, no net, abstract isomorphism instead of an interpolating factor. +`sources claim equivalence:` no — HS17 says only that statistical independence "is closely related" to the split property. (X4) shows it strictly weaker in general; DL84 identifies it with **quasi-split** (D7), equivalent to split under a semi-standard state. + +**(D3) [HS17] §2, "Split property" paragraph, source.txt 995–1005** — tier (a) — the split, commutant form + +Given statistical independence and a vector |Ψ⟩ cyclic for 𝔄_A and 𝔄_B and +separating for 𝔄_A ∨ 𝔄_B (written inside the definitional paragraph as "there +is typically a vector…"), there is a unitary W: ℋ → ℋ⊗ℋ with WaW\* = π_A(a)⊗1, +WbW\* = 1⊗π_B(b); setting 𝔑 = W\*(𝔅(ℋ_A)⊗1)W gives 𝔄_A ⊂ 𝔑 ⊂ 𝔄_B′, + +> which is also called the ``split''. The split and the unitary $W$ are unique (for given $\gdef\H{\mathcal{H}}|\Psi \rangle \in\H$) if we require that + +W\*(|Ψ⟩⊗|Ψ⟩) lies in the natural cone of |Ψ⟩ for 𝔄_A ⊗ 𝔄_B. 𝔑 depends on the +chosen |Ψ⟩. + +`differs from (D1) by:` the type I factor is squeezed against the **commutant** 𝔄_B′, not the larger local algebra; no regions. In the chiral dictionary the two agree iff Haag duality 𝔅(I₂) = 𝔅(I₂′)′ holds — neither source invokes duality at its definition site. +`sources claim equivalence:` HS17 presents (D2) + |Ψ⟩ ⇒ (D3) as an entailment ("entails"), no proof, citing DL84 and BW86. The machinery is proved in [DL84] Thm 2.1 (tier (b), mineru-unchecked; see (R7)). + +**(D4) product-state formulation [dB74], attested through [KOE03] and now read in [dB74] pp. 1–8** — tier (b), mineru-unchecked + +[dB74] Cor. 2.4: in its QFT standing structure (local rings with Reeh–Schlieder +vacuum, "almost factor" property, regions with slack), a normal product state +for the pair yields interpolating type I factors realizing Borchers' display +ℛ(O₁) ⊂ 𝔐₁ ⊂ 𝔐₂′ ⊂ ℛ(O₂)′. The converse is asserted: + +> It is obvious, but still worth mentioning, that the existence of factors $M_{1}$ and $M_{2}$ with the properties specified above implies the existence of normal product states. + +**Caution (refutation finding):** "split ⇔ faithful normal product state" is +**false for abstract commuting pairs** — the (X4) witness carries a faithful +normal product state yet is not split. The abstract relation is [DL84] §1: +normal product state / product isomorphism ⇔ **quasi-split** (D7), and + +> Under very general circumstances, for example if there exists a semi-standard state, the split and quasi-split properties are equivalent + +dB74's equivalence is honest because its QFT standing structure supplies the +standardness; KOE03's use of the criterion in (R1) is inside that structure and +is sound. + +`sources claim equivalence:` KOE03 attests both directions of (D1) ⇔ (D4) to dB74 (proof of `prop:subsplit`, source.txt 1765–1769, and source.txt 9753–9758) — for local algebras in the QFT setting, now grounded at (b) by the dB74 fetch, with the standardness proviso above. + +**(D5) [HS17] "intermediate type I subfactors", source.txt 2293–2300** — tier (a) usage row; notion undefined in the corpus + +Thm `Thm:dominance` quantifies over "intermediate type I subfactors" +𝔑_A ⊂ 𝔄_A, 𝔑_B ⊂ 𝔄_B; "intermediate" (between which algebras) and "exhausting +𝔄" in the following remark are never defined in HS17. Recorded as a gap, not +filled. Partially grounded by [DL84]'s dyadic interpolation chain (see (R9)). + +**(D6) [DL84] Definition 1.4 (p. 497 of Invent. Math. 75)** — tier (b), mineru-unchecked — the bare two-algebra form + +> 1.4. Definition. A $W^*$ -inclusion $(A, B)$ is split if there exists a type I factor $N$ such that $A \subset N \subset B$ . + +where a W*-inclusion is a pair A ⊆ B of von Neumann algebras, + +> (of course $A$ and $B$ have the same identity) + +No vector, no separability, no factoriality of the endpoints. Standardness +(a vector cyclic and separating for A, B and A′∧B — Def. 1.1) is a separate +refinement layer; for a non-trivial **standard** split inclusion, separability +of the GNS space and proper infiniteness of A, B, A′∧B are *theorems* +([DL84] Prop. 1.6, tier (b), mineru-unchecked). + +`differs from (D1)/(D3) by:` no net, no commutant: the second algebra is arbitrary. Both (D1) and (D3) are instances (take B := 𝔅(I₂), resp. B := 𝔄_B′). + +**(D7) [DL84] §1, quasi-split** — tier (b), mineru-unchecked + +> We shall also say that $(A,B)$ is quasi-split if there exists a type I factor $M$ such that $(A\otimes \mathbb{1},B\otimes M)$ is split. + +DL84 (citing its ref. [12]) states: (A,B) is quasi-split iff there is an +isomorphism A∨B′ → A⊗B′ with ab′ ↦ a⊗b′ — i.e. exactly the (D2) shape for the +pair (A, B′). Split ⇒ quasi-split; equivalence under a semi-standard state +(quote under (D4)). The (X4) witness is quasi-split but not split. + +### Adopted general form + +Fix a complex Hilbert space ℋ, of arbitrary dimension. All von Neumann algebras +act on ℋ and contain 1_ℋ; that the interpolating factor shares the unit is +*derived* from Λ(1) ⊆ 𝔑, not assumed (degeneracy table, "non-unital" row). +A **W*-inclusion** is a pair (Λ(1), Λ(2)) of von Neumann algebras on ℋ with +Λ(1) ⊆ Λ(2). The inclusion is **split** iff there exists a type I factor 𝔑 on +ℋ with Λ(1) ⊆ 𝔑 ⊆ Λ(2). There are no further hypotheses: no factoriality or +type restriction on the endpoints, no separability of ℋ, no distinguished +vector, and no non-triviality clause (the inclusion is trivially split whenever +either endpoint is a type I factor). + +$$ +\Lambda(1) \;\subseteq\; \mathfrak{N} \;\subseteq\; \Lambda(2), +\qquad \mathfrak{N} \text{ a type I factor on } \mathcal{H}. +$$ + +At net level: a local net O ↦ 𝓡(O) has the **split property** iff for every +suitably separated pair of regions — Ī₁ ⊂ I₂ in the chiral/interval case, +a positive-distance corridor in the Minkowski case — the inclusion +𝓡(O₁) ⊆ 𝓡(O₂) is split (the *nested* form). The *commutant* form — for each +admissible disjoint pair, 𝓡(O_A) ⊆ 𝔑 ⊆ 𝓡(O_B)′ is split — is the historical +original (Borchers' conjecture as displayed in [dB74]); the nested form implies +it via locality (𝓡(O₂) ⊆ 𝓡(O₂′)′), and the converse holds under Haag duality, +which no fetched source assumes at its definition site. This note adopts the +nested form at net level and treats the commutant form as a conditionally +equivalent variant, so that every net-level row below says which of the two its +source states. + +Justification: the single-inclusion core is (D6) — up to notation verbatim +[DL84] Definition 1.4 — and subsumes (D1) and (D3) as instances. (X1) and (X2) +discriminate both clauses of "type I factor": an interpolating factor of +arbitrary type is strictly weaker ((X1), hyperfinite III₁ identity inclusion), +and an interpolating type I *algebra* is strictly weaker ((X2), ℂ⊕ℂ ⊆ ℂ⊕ℂ). +(X4) shows the statistical-independence/quasi-split candidate (D2)/(D7) +strictly weaker in general (a 5-dimensional separating object), with +equivalence restored exactly under a semi-standard state ([DL84] §1). The +net-level choice resolves the refutation's hole H1 by picking one quantified +property and recording the conditional equivalence instead of an ambiguous +disjunction. + +## Notation and conventions + +| (C#) | Axis | This note | Per source | Translation | +|---|---|---|---|---| +| (C1) | standing setting, chiral | — | [KOE03] def:chcotheo, source.txt 953–982: chiral net on **explicitly separable** ℋ; proper interval I ⋐ S¹ open, connected, non-dense; I′ := S¹∖Ī. tier (a) | — | +| (C2) | separation of the region pair | nested closure containment | [KOE03] Ī₁ ⊂ I₂ (inside the definition); [HS17] dist(A,B) > 0 ("finite safety-corridor"). tier (a) both | on S¹ they agree (Ī₁ ⊂ I₂ ⇔ disjoint closures with two open arcs between); on a non-compact Cauchy surface dist > 0 is strictly stronger than disjoint closures; the corpus never confronts the two | +| (C3) | HS17 standing conventions | — | "Hilbert spaces are always assumed (or manifestly) separable."; inner products anti-linear in the **first** entry; ∨, ⊗ defined at the (D2) site. tier (a) | — | +| (C4) | which "statistical independence" | W*-independence in the product sense | [HS17] pins by citation (florig); [KOE03] footnote flags the family of inequivalent notions (sS90). Both citations unfetched | none available in corpus | +| (C5) | factor vs type I subalgebra | factor (definitional) | definitions in all sources demand type I **factors**; [HS17]'s proofs use type I subalgebras merely "chosen to be" factors (source.txt 4360). tier (a) | (X2): the algebra form is strictly weaker | +| (C6) | net-level property vs single inclusion | both; single inclusion is primary | [KOE03] net-level (D1); [HS17] per-pair object (D3); [DL84] abstract pair (D6). tier (a)/(b) | net-level = ∀ admissible pairs, single-inclusion form | +| (C7) | meaning of ≅ in (D2) | abstract *-isomorphism | [HS17] displayed definition: isomorphism of von Neumann algebras; its intro glosses ≅ as "up to unitary equivalence" (spatial). Internal wobble, unremarked in the source. tier (a) | under the spatial reading the candidate becomes the (X7) tensor form, which *is* equivalent to split | + +## Results and dependencies + +### (R1) Heredity: chiral subnets inherit the split property and nuclearity + +Let 𝔄 ⊂ 𝔅 be a chiral subnet. If 𝔅 has the split property (is nuclear), then 𝔄 +has the split property (is nuclear). + +- Source: [KOE03] `prop:subsplit`, source.txt 1748–1782 · tier (a) · **proved in source** +- Verbatim: + > Let $\gdef\lok#1{{\mathcal #1}}\lok{A}\subset\lok{B}$ be a chiral subnet. If $\gdef\lok#1{{\mathcal #1}}\lok{B}$ has the split property (is nuclear), then $\gdef\lok#1{{\mathcal #1}}\lok{A}$ has the split property (is nuclear). +- Proof route: restrict 𝔄 to its vacuum subrepresentation; by the dB74 + criterion it suffices to produce a faithful normal product state on + 𝔄(I₁)e_𝔄 ∨ 𝔄(I₂′)e_𝔄; modular covariance (A15) makes A ↦ Ae_𝔄 an + isomorphism on 𝔄(I₃), I₃ ⊇ I₁ ∪ I₂′; pull back a product state from 𝔅. +- Depends on: (D4)/[dB74] criterion (tier (b), mineru-unchecked, with the + standardness proviso under (D4)); (A15) modular covariance; hypothesis (A13). +- Note: KOE03 demands a *faithful* normal product state where dB74 Cor. 2.4 + needs only a normal one (faithfulness is manufactured in dB74 Thm 2.2) — + a harmlessly stronger input. + +### (R2) Nuclearity ⇒ split + +BW-nuclearity (a5): Θ_{β,r}: a ↦ e^{−βH}π₀(a)|0⟩ nuclear with +‖Θ_{β,r}‖₁ ≤ e^{(c/β)ⁿ}) implies the split property for pairs with +dist(A,B) > 0; chiral version with L₀ in place of H. + +- Source: [HS17] source.txt 1199 (cites BDF87); [KOE03] source.txt 1709–1716 + and 9174–9278 (nuclearity formulated by BW86; chiral implication via FG93, + its lemma 2.12 — locator is KOE03's citation) · tier (a) attestations, result + **(c)** · cited elsewhere +- Conventions: one result under two conventions (H-form and L₀-form). +- Attribution note (refutation-verified): KOE03 attaches no citation to the + implication itself — BW86 is credited with the nuclearity condition and the + free-field case, FG93 with the chiral implication; HS17 credits BDF87. Not a + contradiction; the unfetched originals would adjudicate. +- Depends on: [ext: Buchholz–Wichmann (BW86) — formulates energy nuclearity and + establishes it for the free scalar field, deriving causal/statistical + independence of sufficiently separated local algebras. tier (c), no locator, + not retrieved]; [ext: Buchholz–D'Antoni–Fredenhagen (BDF87) — HS17's citation + target for nuclearity ⇒ split. tier (c), no locator, not retrieved]; + [ext: Gabbiani–Fröhlich (FG93) — translates the implication to chiral nets on + S¹. tier (c), not retrieved]. + +### (R3) Modular nuclearity a5′) ⇒ statistical independence; a5′) ⇒ a5) + +- Source: [HS17] source.txt 2337–2349 · tier (a) attestation, results (c) · + asserted / cited elsewhere +- Depends on: [ext: Buchholz–D'Antoni–Longo, *Nuclear maps and modular + structures I* — modular nuclearity implies energy nuclearity in Minkowski + space without the quantitative bounds. tier (c), no locator, not retrieved]; + edge to (R2). + +### (R4) Split ⇒ local algebras ≅ centre ⊗ hyperfinite type III₁ factor + +- Source: attested in [KOE03] source.txt 1718–1731 · tier (c) · cited elsewhere +- Depends on: [ext: BDF87 — under the split property (plus its standing + assumptions, for nets in 3+1-dimensional Minkowski space) local von Neumann + algebras factorise as centre ⊗ hyperfinite type III₁ factor. tier (c), no + locator, not retrieved]; [ext: Haagerup — uniqueness of the injective type + III₁ factor. tier (c), not retrieved]; [ext: FG93 — chiral translation. + tier (c), not retrieved]. +- Flag (refutation): KOE03 and HS17 attribute *different hypothesis-sets* to + BDF87 (split alone, here, vs nuclearity + scale invariance in (R5)); read + this row as KOE03's compression, not as a theorem shape. + +### (R5) Nuclearity + asymptotic scale invariance ⇒ direct sums of type III₁ factors + +- Source: attested in [HS17] source.txt 1201 · tier (c) · cited elsewhere + ([ext: BDF87, as in (R4)]) +- Depends on: a5) (A8) and (A14). Kept distinct from (R4): different + hypotheses, different conclusion. + +### (R6) No purity / failure of statistical independence for touching regions (HS17 `thm_split`) + +(a) The restriction of a locally normal state to a local algebra is never pure. +(b) A pure normal state on a local algebra extends to no larger local algebra +normally. (c) For disjoint A, B with Ā ∩ B̄ ≠ ∅ there is no normal separable +state on 𝔄(O_A) ∨ 𝔄(O_B); in particular the pair is not statistically +independent (hence not split). + +- Source: [HS17] `thm_split`, source.txt 1202–1210 · tier (a) for (a), (c); + (b) inherits (c) · (a), (c) proved in source; (b) cited elsewhere +- Depends on: [ext: Fewster–Verch, *The necessity of the Hadamard condition* — + its corollary 3.3 (locator as HS17 cites it): a pure normal state on a local + algebra cannot be extended to a normal state on any larger local algebra. + tier (c), not retrieved]; setting of (R5); hypotheses (A10), (A14). +- Independent grounding (refutation fetch): [dB74] Ch. II conclusion asserts + the same failure for the free field — + > one runs into contradictions if one postulates the existence of normal product states for such regions + (tier (b), mineru-unchecked). + +### (R7) Canonical split: implementing unitary, canonical 𝔑, natural-cone uniqueness + +For a semi-standard split W*-inclusion, there is a standard-implementation +unitary U_Λ: ℋ_Λ → ℋ₁⊗ℋ₂ with U_Λ ab′U_Λ\* = a_e⊗b′_f and +U_Λ P^♮_Ω(A′∧B) = P^♮_{Ω⊗Ω}; the canonical interpolating type I factor is +N_Λ = U_Λ\*(𝔅(ℋ₁)⊗1)U_Λ, unique per standard vector by unicity of the standard +implementation. When the vector is cyclic for both algebras (HS17's hypotheses) +this is exactly (D3)'s W: ℋ → ℋ⊗ℋ. + +- Source: [DL84] Thm 2.1, Def. 2.2, Cor. 2.4 (pp. 1–14 of the conversion) · + tier (b), mineru-unchecked · proved in source (promoted from (c) by the + refutation fetch); [HS17] source.txt 995–1005 asserts it · tier (a) +- Precisions: DL84 normalises via the natural cone of the **relative + commutant** A′∧B (equivalent to HS17's phrasing under the identification by + W); Cor. 2.4 shows every interpolating type I factor with properly infinite + relative commutants is vN_Λv\* for a unitary v ∈ A′∧B — uniqueness is + strictly *per vector*. +- Depends on: (D6); standardness layer; [ext: DL84's ref. [12] for the + quasi-split equivalences. tier (c), not retrieved]. + +### (R8) Entanglement-measure dominance on intermediate type I subfactors + +E(ω) ≥ E_D(ω|_𝔑) for entanglement measures with (e2), (e4), (e5) and the +normalisation, 𝔑 = 𝔑_A ⊗ 𝔑_B an intermediate type I subfactor pair. + +- Source: [HS17] `Thm:dominance`, source.txt 2292–2307 · tier (a) statement · + cited elsewhere + sketched (finite-dimensional case in [ext: Donald– + Horodecki–Rudolph — entanglement measures with the stated properties dominate + distillable entanglement in finite-dimensional type I algebras. tier (c), not + retrieved]; approximation sketch in source) +- Depends on: (R9); (D5) gap; measure axioms (out of this object's scope). + +### (R9) Split ⇒ many intermediate type I subfactors "exhausting" the pair + +- Source: [HS17] remark after `Thm:dominance` · tier (a) for the assertion · + **asserted, no proof, no citation in HS17** +- Verbatim: + > The existence of many such intermediate type I subfactors exhausting $\gdef\A{\mathfrak{A}}\A$ is guaranteed by the split property. +- Refutation status: half-grounded. [DL84]'s introduction states + > the interpolation by a chain of type I factors $N_{d}$ , $d$ a diadic rational + (Th. 8.3; that section not converted) — "many" in a precise sense, tier (b), + mineru-unchecked. "Exhausting 𝔄" remains undefined in every fetched text; + this row must not be cited as a theorem shape until it is pinned. + +### (R10) Split ⇒ the decoupled state is well-defined; lower bounds on E_I, E_R (HS17 `corI`) + +For dist(A,B) > 0 and ω faithful normal on 𝔄_A ∨ 𝔄_B, the decoupled state +ω′(ab) = ω(a)ω(b) is well-defined by the split property, and E_I, E_R admit the +lower bounds of HS17 corI. + +- Source: [HS17] `corI` and preceding paragraph, source.txt 4648–4676 · + tier (a) · proved in source +- Depends on: split property for the pair ((R2) route); statistical + independence (split ⇒ quasi-split, [DL84] §1, tier (b)); hypotheses (A7), + (A11). Consequence noted in source: E_R diverges as the corridor shrinks. + +### (R11) E_R(ω) > 0 across a corridor (HS17 `cor1`) + +- Source: [HS17] `cor1`, source.txt 4347–4386 · tier (a) · proved in source + (split-chosen type I "Cbit" subalgebras + near-maximal Bell violation + + Fell's theorem + Reeh–Schlieder) +- Depends on: (R10); split property; Reeh–Schlieder (A5, proved in HS17); + [ext: Fell's theorem — normal states of a representation approximate any + state weakly; HS17 points at Haag's book. tier (c)/(d), not retrieved]; + technique attributed to [ext: Narnhofer; Summers–Werner — maximal Bell + violation is generic in QFT. tier (c), not retrieved]. + +### (R12) Quantum Noether theorem: split ⇒ local implementers via universal localisation maps + +- Source: [KOE03] source.txt 9751–9773 (detailed summary), 1724–1726 · + tier (c) · cited elsewhere +- Depends on: (R1); [ext: Buchholz–Doplicher–Longo (BDL86) — under the split + property, spacetime symmetries admit local implementers built from universal + localisation maps: for Ī ⊂ J a norm-one *-homomorphism onto the algebra of J + acting trivially on the algebra of I carries global symmetry unitaries to + locally supported implementers. tier (c), no locator, not retrieved]; + (D4)/[dB74] supplies the product state; local normality of the embedding. + +### (R13) Localisation maps → identity as regions exhaust spacetime + +- Source: attested in [KOE03] source.txt 9812–9818 · tier (c) · cited elsewhere +- Depends on: (R12); [ext: D'Antoni–Doplicher–Fredenhagen–Longo (ADF87) — for + suitable enlarged regions the universal localisation maps converge pointwise + strongly to the identity, given irreducibility of the quasi-local algebra. + tier (c), no locator, not retrieved]; irreducibility hypothesis. + +### (R14) Trace-class estimate on e^{−βL₀} ⇒ nuclearity ⇒ split for the U(1)-current derivative models + +- Source: [KOE03] source.txt 9174–9278, estimate `eq:nuclcond` · tier (a) for + the estimate, proved in source; the final split step inherits (c) (FG93's + own lemma 2.12, locator as KOE03 cites it) +- Depends on: (R2); [ext: FG93 as in (R2)]; [ext: Schoeneberg III.§3 — Dedekind + η transformation law, locator as KOE03 cites it. tier (c), not retrieved]; + trace-class hypothesis (A8 witnesses). + +### (R15) The chiral nets LSU(n)_k are split, hence completely rational + +- Source: attested in [KOE03] source.txt 4175–4180 · tier (c) · cited elsewhere +- Depends on: [ext: FG93 — split property for chiral current algebra models. + tier (c), not retrieved]; [ext: Loke thesis — strong additivity. tier (c), + not retrieved]; [ext: Xu — μ-index computation. tier (c), not retrieved]; + [ext: Kawahigashi–Longo–Müger — complete rationality (split + strong + additivity + finite μ-index) implies finitely many sectors of finite + statistical dimension. tier (c), not retrieved]. Split is a defining + constituent of complete rationality (KOE03 source.txt 7913–7920). + +### (R16) Joint cyclic-separating vectors — corrected statement + +**As quoted by KOE03 (source.txt 6666–6669) the sentence is refuted** — see +(X9) in the rejected/refuted table. Corrected statement, now read in the +fetched DL84: under the hypotheses of [DL84] Prop. 1.2 (properly infinite +algebras *acting standardly* / with properly infinite commutants, separable +predual), each of A, B, A′∧B admits a dense G_δ set of cyclic separating +vectors, hence standard vectors for (A, B) form a dense set. + +- Source: [DL84] Prop. 1.2 · tier (b), mineru-unchecked · proved in source, + via [ext: Dixmier–Maréchal — the cyclic separating vectors of a von Neumann + algebra with separable predual and properly infinite commutant form a dense + G_δ. tier (c), not retrieved] +- KOE03's *application* (type III subfactor inclusions on the vacuum space) is + sound; only its freestanding sentence drops the hypotheses. + +### (R17) Finite-dimensional statistical independence — refuted as stated + +HS17 asserts (source.txt 991, tier (a)): + +> When $\gdef\A{\mathfrak{A}}\A_A$ and $\gdef\A{\mathfrak{A}}\A_B$ are finite dimensional and $\gdef\A{\mathfrak{A}}\gdef\bC{{\mathbb C}}\A_A\cap\A_B=\bC 1$, then the algebras are always statistically independent. + +**Refuted as literally stated** — see (X10) in the rejected/refuted table. It +holds for commuting finite-dimensional *factors* (then the join is naturally +the tensor product); the corpus's uses of the remark are unaffected because +they concern factors. + +### (R18) Diffeomorphism covariance ⇒ split for chiral nets + +- Source: attested in [HS17] footnote, source.txt 1493 · tier (c) · cited + elsewhere ([ext: Morinelli–Tanimoto–Weiner, *Conformal covariance and the + split property* — a chiral net containing the Virasoro subnet automatically + satisfies the split property. tier (c), no locator, not retrieved]) + +### (R19) Model inputs: free fields satisfy BW-nuclearity + +- Source: attested in [HS17] source.txt 2375, 1220 · tier (c) · cited elsewhere +- Depends on: [ext: BW86 — nuclearity for the free scalar field. tier (c), not + retrieved]; [ext: D'Antoni–Hollands — nuclearity and split for free Dirac + fields, also in curved spacetime. tier (c), not retrieved]. These supply the + nonvacuous instances of the split property in the corpus. + +## Hypotheses + +| (A#) | Statement | Class | Evidence | Witness | Scope | Tier | Used by | +|---|---|---|---|---|---|---|---| +| (A1) | ℋ separable | open | explicit in both sources (KOE03 source.txt 956; HS17 conventions); **no fetched result visibly uses it**; for *standard* split inclusions separability is a theorem ([DL84] Prop. 1.6) | — | standing | a/b | none exhibited | +| (A2) | chiral-net axiom bundle (isotony, locality, covariance, spectrum, unique cyclic vacuum) | — (definitional bundle) | KOE03 def:chcotheo; HS17 a1)–a4) | — | standing | a | (A5), (R1)–(R6) | +| (A3) | subnet conformal covariance | model-dependent | KOE03 source.txt 1006–1020, 1484–1496 | fails: light-ray inclusion of the U(1)-current derivatives in their dual net (KOE03 names it, GLW98 leg tier (c)); holds: coset/current subalgebras | standing | b/c | (A15), (R1) | +| (A4) | uniqueness of the vacuum | — (axiom) | KOE03 source.txt 1221–1223; HS17 a4) | — | standing | a/c | type III₁ factoriality | +| (A5) | vacuum cyclic and separating for local algebras (Reeh–Schlieder) | provable | HS17 proves in-source, source.txt 1141–1167; KOE03 attests RS61 et al. | — | standing (derived) | b | (A11), (A12), (R1), (R11) | +| (A6) | weak additivity | provable (chiral) / open (general nets) | KOE03 derives for chiral nets (LRT78/FJ96 legs (c)); HS17 assumes it as Araki's axiom | — | standing | a/c | (A5) | +| (A7) | corridor dist(A,B) > 0 | model-dependent | HS17 source.txt 451, 1199, 2345, 4649 | fails: free Klein–Gordon net with touching bases ((R6)(c); fewster_2 leg (c); split ⇒ statistical independence now (b) via dB74/DL84); holds: same net with corridor | local | a/c | (R2), (R10), (R11) | +| (A8) | BW-nuclearity a5) | open | sufficient for split (attested); no converse claimed; no net satisfying a1)–a4) but failing a5) named in any fetched source | satisfiers named: free KG, free spin-½, U(1)-current derivative and stress-energy models (KOE03 proves the chiral estimate in-source) | local | a/c | (R2), (R5), (R14), (R19) | +| (A9) | modular nuclearity a5′) | open | a5′) ⇒ a5) attested (tier (c)); satisfiers: integrable models with factorizing S-matrix | — | local | a/c | (R3) | +| (A10) | local normality of ω | model-dependent | HS17 source.txt 1201 | fails: pure state on 𝔄(O_A) Hahn–Banach-extended (argument written out in extraction; non-constructive step flagged); holds: vacuum and density-matrix states | local | a/b | (R6) | +| (A11) | ω faithful + normal on 𝔄_A ∨ 𝔄_B | provable for the vacuum (corridor + Reeh–Schlieder argument written out; faithfulness *consumes* the corridor); model-dependent for general normal states | HS17 source.txt 4649 | fails (general case): normal state with support projection p < 1; holds: vacuum | local | a/b | (R10), (R11) | +| (A12) | vector Ψ cyclic for 𝔄_A, 𝔄_B, separating for the join | provable for the vacuum (discharges HS17's "typically"); open beyond it | HS17 source.txt 995 | — | local | a/b | (R7), (D3) | +| (A13) | ambient net 𝔅 has the split property | open | satisfiers: LSU(n)_k, U(1)-current models; **no non-split net named in the fetched pages** — [DL84] announces non-split field theories in its §9–10, not converted | — | local | a | (R1), (R12), complete rationality | +| (A14) | asymptotic scale invariance at small scales | open | undefined in fetched text; grounding lives in BDF87 (unfetched) | — | local | a/c | (R5), (R6) | +| (A15) | modular covariance of subnet inclusions | provable | derived from (A3) + geometric modular action; KOE03 source.txt 1508–1523 (Takesaki/Jones legs (c)) | — | standing (derived) | b/c | (R1) | +| (A16) | faithful normal product state on 𝔄(I₁)e_𝔄 ∨ 𝔄(I₂′)e_𝔄 | provable given (A13) + (A15) | proof written out in KOE03 prop:subsplit; criterion now read in dB74 (tier (b), mineru-unchecked) | — | local | b | (R1), (R12) | + +## Degeneracies and boundary cases + +| Case | Effect on the adopted form | Tier | +|---|---|---| +| intended case is nonvacuous | named instances exist in the corpus, at attestation level only: the (A8) satisfiers (free KG, free spin-½, U(1)-current derivative and stress-energy models) and (R15)'s LSU(n)_k — every leg rests on tier (c) attestations ((R2)'s BW86/FG93 route); the type III row below records where the bare definition is non-vacuous | c | +| zero object / scalars | Λ(1) = ℂ1: always split (ℂ1 is a type I factor). Either endpoint a type I factor (incl. Λ(2) = 𝔅(ℋ)): trivially split — the definition has no non-triviality clause, matching [DL84] (its Cor. 2.3(d) handles N = ℂ1 and N = 𝔅(ℋ) explicitly) | b | +| identity inclusion Λ(1) = Λ(2) = 𝔐 | split iff 𝔐 is a type I factor; fails for the hyperfinite III₁ factor (existence attested via KOE03/BDF87, (c)) | a/c | +| finite-dimensional | **not trivial**: split ⇒ Λ(1) ∩ Λ(2)′ = ℂ1 (one-line proof), but not conversely — 5-dimensional witness (ℋ = ℂ⁵, Λ(2) = M₂⊕M₃, Λ(1) = {p, 1−p}″, p = diag(1,0)⊕diag(1,1,0)): trivial relative intersection, statistically independent, yet no interpolating type I factor (unital subfactor of M₂⊕M₃ forces r ∣ 2 and r ∣ 3 ⇒ r = 1). Minimal witness: ℂ⊕ℂ ⊆ ℂ⊕ℂ, now formalized as `VonNeumannAlgebra.not_isSplitInclusion_diagonalAlgebra` (Prior art) | — (verified computation) | +| commutative | Λ(1) commutative harmless; Λ(2) commutative ⇒ split iff Λ(1) = ℂ1 (only commutative factor is ℂ1). Witnesses: ℂ⊕ℂ ⊆ ℂ⊕ℂ (formalized, see Prior art); L∞[0,1] ⊆ L∞[0,1] | — | +| non-separable / non-σ-finite | no effect on the bare form (ℂ1 ⊆ 𝔅(ℋ) splits on any ℋ); separability enters only with the standardness refinement, where it becomes a **theorem** ([DL84] Prop. 1.6) | b | +| type III | intended case; the definition is non-vacuous exactly when neither endpoint is type I | a | +| non-unital / degenerate representation | dissolves: 1 ∈ Λ(1) ⊆ 𝔑 forces unit-sharing — a derived fact, not a hypothesis (refutation hole H2); no fetched source entertains a non-unital variant | — | +| universally orthogonal index element | net level: touching pairs are excluded by the definition's typography (Ī₁ ⊂ I₂), and the exclusion is essential ((R6)(c)); empty pair-index ⇒ vacuously split, no effect | a | +| quantifier swap: ∀ pairs ∃ 𝔑 ↦ ∃ 𝔑 ∀ pairs | contradictory in any net with factorial local algebras: 𝔐 ⊆ 𝔅(I₂) for all I₂ plus locality forces 𝔐 ⊆ Z(𝔅(I₂)) = ℂ1, then 𝔅(I₁) ⊆ ℂ1 — see (X8) | a | +| hypothesis dropped: "type I" | strictly weaker — (X1) | a/c | +| hypothesis dropped: "factor" | strictly weaker — (X2) | — | +| hypothesis dropped: Λ(1) ⊆ 𝔑 | vacuous — (X3) | — | + +## Rejected formulations and refuted claims + +**Append-only.** + +| id | Candidate or claim | Disposition | Discriminator | Tier | Date | +|---|---|---|---|---|---| +| (X1) | interpolating *factor* of arbitrary type | rejected | **(X1) separating object** — identity inclusion of the hyperfinite type III₁ factor: interpolates itself, split fails (identity-inclusion row) | a/c | 2026-08-14 | +| (X2) | interpolating type I *algebra* instead of factor | rejected | **(X1) separating object** — ℂ⊕ℂ ⊆ ℂ⊕ℂ on ℂ²: the only interpolating algebra is ℂ⊕ℂ, type I but not a factor | — | 2026-08-14 | +| (X3) | "Λ(1)′ ∩ Λ(2) contains a type I factor" | rejected | **(X2) degeneracy/vacuity** — ℂ1 always qualifies; satisfied by every unital inclusion, incl. the non-split (X2) witness | — | 2026-08-14 | +| (X4) | statistical independence of (Λ(1), Λ(2)′) as the definition (≡ quasi-split, (D7)) | rejected | **(X1) separating object** — the 5-dimensional witness (degeneracy table): quasi-split and W*-independent yet not split; it has no semi-standard vector, exactly threading [DL84]'s gap. **(X5) conditional equivalence** — split ⇔ quasi-split under a semi-standard state ([DL84] §1, tier (b)); the standard-position sub-question is thereby settled affirmatively. Under HS17's spatial gloss of ≅ ((C7)) the candidate becomes (X7) and is equivalent | b | 2026-08-14 | +| (X5) | uniqueness of the interpolating 𝔑 required | rejected | **(X1) separating object** — ℂ1 ⊆ M₂(ℂ): both ℂ1 and M₂ interpolate. [DL84] Cor. 2.4 confirms structurally: uniqueness only per standard vector | a/b | 2026-08-14 | +| (X6) | strict intermediacy (𝔑 ∉ {Λ(1), Λ(2)}) | rejected | **(X1) separating object** — ℂ1 ⊆ M₂(ℂ) is split, but the only unital subfactors of M₂ are the endpoints | — | 2026-08-14 | +| (X7) | tensor/unitary form: ∃ W: ℋ → ℋ_A ⊗ ℋ_B with WΛ(1)W* ⊆ 𝔅(ℋ_A)⊗1, WΛ(2)′W* ⊆ 1⊗𝔅(ℋ_B) | equivalent | — ((⇐) is HS17's construction, tier (a); (⇒) via the type I structure theorem, used verbatim in [DL84] Prop. 1.5, tier (b), mineru-unchecked — promoted from (d) by the refutation fetch). HS17's literal ℋ⊗ℋ form is conditionally equivalent (needs the cyclicity hypotheses making both legs ℋ) | b | 2026-08-14 | +| (X8) | net-level ∃ 𝔑 ∀ pairs | rejected | **(X2) degeneracy** — forces 𝔐 = ℂ1 in any net with factorial local algebras (corpus-only argument via locality + factoriality, no irreducibility needed); named case: free scalar field / any III₁ net | a | 2026-08-14 | +| (X9) | *claim* [KOE03 source.txt 6666–6669]: properly infinite von Neumann algebras on a separable ℋ admit joint cyclic-separating vectors | refuted | counterexample 𝔑 = 𝔐 = 𝔅(ℓ²): properly infinite, separable, **no separating vector at all**. Corrected statement = [DL84] Prop. 1.2 (needs standard action / properly infinite commutants); KOE03's application is sound, its freestanding sentence is not | b | 2026-08-14 | +| (X10) | *claim* [HS17 source.txt 991]: finite-dimensional commuting pair with trivial intersection is always statistically independent | refuted | counterexample: 𝔄_A = {1, p}″, 𝔄_B = {1, q}″ with p, q nonzero orthogonal projections, p + q < 1 (e.g. on ℂ³): commuting, 𝔄_A ∩ 𝔄_B = ℂ1, but 𝔄_A ∨ 𝔄_B ≅ ℂ³ ≇ ℂ⁴ ≅ 𝔄_A ⊗ 𝔄_B, and the product state with φ(p) = ψ(q) = 1 admits no extension (ω(1−p−q) = −1). True for commuting finite-dimensional *factors* | — (written-out argument) | 2026-08-14 | + +## Prior art + +| System | Found | Relation to variants | How searched | Measured at | +|---|---|---|---|---| +| this repository | `VonNeumannAlgebra.IsSplitInclusion` (`QuantumSystem/Algebra/VonNeumannAlgebra/SplitInclusion.lean`) — interpolating type I factor, containment derived; monotonicity, sandwich and self constructors; `IsSplitInclusion.exists_tensor_decomposition` (spatial split of the interpolating factor and its commutant) | same as (D6)/adopted form; the tensor decomposition is the (X7)(⇒) direction | find/grep over QuantumSystem/ | working tree, 2026-08-15 | +| this repository | `VonNeumannAlgebra.not_isSplitInclusion_diagonalAlgebra` (`QuantumSystem/Algebra/LocalNet/Examples.lean`) — identity inclusion of `diagonalAlgebra := commutantSet {diagonalProjection}` on `EuclideanSpace ℂ (Fin 2)` (concretely ℂ⊕ℂ, the diagonal operators) is **not** split, via `not_isTypeIFactor_diagonalAlgebra`/`not_isFactor_diagonalAlgebra` and `IsSplitInclusion.isTypeIFactor_of_self`; axiom-checked (`#print axioms`), rests only on `propext`, `Classical.choice`, `Quot.sound` | **negative witness** — realizes the degeneracy table's minimal witness ℂ⊕ℂ ⊆ ℂ⊕ℂ (also the object underlying (X2)'s discriminator); distinct in kind from the positive rows above (`IsSplitInclusion` itself, `exists_tensor_decomposition`, `VonNeumannNet.SplitProperty`), which inhabit the predicate rather than refute it | `lean_local_search`, `lean_declaration_file`, axiom check via `#print axioms` | working tree, 2026-08-15 | +| this repository | `LocalNet.SplitProperty` (`QuantumSystem/Algebra/LocalNet/SplitProperty.lean`) — nested form over `ProperContainment` pairs, stated as `VonNeumannNet.SplitProperty` at the representation instance | same as (D1) at net level (nested form, as adopted) | same | working tree, 2026-08-15 | +| this repository | type classification substrate: `IsFactor`, `IsTypeI`, `IsTypeIFactor`, `IsTypeIInfinite`, Murray–von Neumann equivalence, `IsTypeIFactor.exists_starAlgEquiv`, type I structure theorem, `vnTensorLeft/Right` commutant theorems, double commutant theorem (both halves) | supports the adopted form; (X7)(⇒)'s external is proved here | same | working tree, 2026-08-14 | +| this repository | could not find: statistical independence (D2), split unitary W (D3), product-state form (D4), standard split inclusion, quasi-split (D7), W*-independence, funnel property, hyperfinite III₁, normal states | — | grep for product state / independence / standard split / funnel / hyperfinite / cyclic | working tree, 2026-08-14 | +| Mathlib | `VonNeumannAlgebra`, `WStarAlgebra`, `commutant`, `commutant_commutant`, WOT files — substrate only | can phrase an inclusion and commutants; nothing more | grep over `.lake/packages/mathlib`, `lean_leansearch` | mathlib rev `5450b53e5ddc75d46418fabb605edbf36bd0beb6` | +| Mathlib | could not find: split inclusion, type I factor, factor (vN sense), MvN equivalence, hyperfinite, normal state, statistical independence, vN double commutant theorem (the structure's field is an axiom, not the theorem) | no Mathlib declaration can state any variant | grep sweeps + `lean_leansearch` | mathlib rev `5450b53e5ddc75d46418fabb605edbf36bd0beb6` | +| Lean ecosystem | vN double commutant TFAE is a Lean AI leaderboard benchmark (solved by AI systems, May–July 2026) — corroborates absence from Mathlib | substrate | leaderboard page fetched | 2026-08-14 | +| Isabelle AFP | substrate only (Complex_Bounded_Operators, Hilbert tensor products, Kraus Maps, GNS 2026, Registers); no vN algebra structure, factors, or split found | Hilbert tensor product is substrate (D2) would need | topic index + entry pages; **search.isa-afp.org unreachable (TLS)** | 2026-08-14 | +| Coq/Rocq (CoqQ/mathcomp) | finite-dimensional only; no vN algebra structure | trivialised setting for every variant | web searches, paper summaries | 2026-08-14 | +| Lean Zulip | could not find indexed threads, having searched site-scoped and archive-scoped web queries — a statement about the searches run, not about absence | — | web searches | 2026-08-14 | +| any system, AQFT level | could not find any other formalization of Haag–Kastler nets or the net-level split property | this repository's `LocalNet.SplitProperty` is the only one located | web searches | 2026-08-14 | + +## Open questions + +- A concrete non-split net, named at a checkable tier: [DL84] announces + > In particular we give examples of field theories which do not fulfill the split property. + (§9–10, pages not converted). Converting DL84 pp. 15–44 would likely move (A13) from `open`. +- "Exhausting 𝔄" and "intermediate" in (D5)/(R9): undefined in every fetched text. +- (A14) asymptotic scale invariance: formulation lives in BDF87 (unfetched). +- (R4) vs (R5): which hypothesis-set BDF87 actually uses — needs the paper. +- Whether the free KG field satisfies (A14) (needed to run (A7)'s witness fully inside HS17's own hypotheses). +- KOE03's stated open problem (source.txt 2750–2752): a split-based triviality argument for certain coset representations is "out of reach to date". +- (A1): does any result in this circle actually need separability at the bare-form layer? (For standard split inclusions it is a theorem; no fetched result visibly consumes the standing assumption.) + +## Sources + +**Append-only.** + +| Key | Work | Status | Cache | Version | Tier reached | Retrieved | +|---|---|---|---|---|---|---| +| KOE03 | S. Köster, *Structure of Coset Models*, dissertation, arXiv math-ph/0308031 | retrieved (arXiv LaTeX, verbatim) | `references/arxiv-math-ph-0308031/` | arXiv v1 | a | 2026-08-14 | +| HS17 | S. Hollands, K. Sanders, *Entanglement measures and their properties in quantum field theory*, arXiv 1702.04924 | retrieved (arXiv LaTeX, verbatim) | `references/arxiv-1702.04924/` | arXiv | a | 2026-08-14 | +| dB74 | D. Buchholz, *Product states for local algebras*, Comm. Math. Phys. 36 (1974) | retrieved **partial** — pp. 1–8 of 18, Project Euclid PDF via MinerU hybrid-engine | `references/buchholz-1974-product-states/` | published | b (mineru-unchecked) | 2026-08-14 | +| DL84 | S. Doplicher, R. Longo, *Standard and split inclusions of von Neumann algebras*, Invent. Math. 75 (1984) 493–536 | retrieved **partial** — pp. 1–14 of 44 (§0–§4), GDZ digitization via MinerU hybrid-engine | `references/doplicher-longo-1984-standard-split/` | published | b (mineru-unchecked) | 2026-08-14 | +| FG93 | Gabbiani, Fröhlich, *Operator algebras and conformal field theory* | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| BDF87 | Buchholz, D'Antoni, Fredenhagen, *The universal structure of local algebras*, CMP 111 (1987) | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| BW86 | Buchholz, Wichmann, *Causal independence and the energy-level density of states…* | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| BDL86 | Buchholz, Doplicher, Longo, *On Noether's theorem in quantum field theory* | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| ADF87 | D'Antoni, Doplicher, Fredenhagen, Longo, *Convergence of local charges…* | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| FV13 | Fewster, Verch, *The necessity of the Hadamard condition* (HS17's fewster_2) | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| DHR02 | Donald, Horodecki, Rudolph (HS17's donald_2) | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| MTW | Morinelli, Tanimoto, Weiner, *Conformal covariance and the split property* | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| SS90 | Summers, independence-notions survey (KOE03's sS90) | not retrieved — not attempted this run | — | — | d | 2026-08-14 | +| GLW98 | Guido, Longo, Wiesbrock (KOE03's GLW98) | not retrieved — not attempted this run | — | — | c | 2026-08-14 | +| DM | Dixmier, Maréchal (DL84's ref. [14]) | not retrieved — not attempted this run | — | — | c | 2026-08-14 | + +Further works cited by the corpus and not retrieved (uH87, CC01, KLM01, vL97, +fX00a, florig, narnhofer_1, summers_1/2, haag_2, dantoni, buchholz_3, +buchholz_5, LRT78, FJ96, mT72, vJ83, wD75, RS61, hjB68, bS74): every claim +through them carries a substitution sentence, no locators beyond the attesting +sources' own citations. + +## Not investigated + +- **The [ext] gap.** Lane 5 swept only the formalization landscape; **no lane + checked any of the external mathematical results** lane 2 marked. The + refutation pass fetched exactly two (dB74 pp. 1–8, DL84 pp. 1–14); all other + [ext] edges — BW86, BDF87, FG93, BDL86, ADF87, uH87, fewster_2, donald_2, + MTW/weiner, CC01, KLM01, vL97, fX00a, sS90, florig, GLW98, Dixmier–Maréchal, + narnhofer_1, summers_1/2, haag_2, dantoni, buchholz_3, buchholz_5 — remain + unexamined; (R2), (R4), (R5), (R12), (R13), (R15), (R18), (R19) and the + GLW98/fewster_2 witness legs rest entirely on attestations. +- **Unconverted pages.** dB74 pp. 9–18; DL84 pp. 15–44, including Th. 8.3's + dyadic-chain proof ((R9)), §6 standard-vector structure, and the §9–10 + non-split field theories that would unblock (A13). +- **MinerU output was not compared against page images**: every dB74/DL84 quote + and locator stays (b) with `mineru-unchecked`. +- Degeneracy probes skipped: type II endpoints as separate witnesses; whether + split inclusions of type III algebras force 𝔑 type I_∞; the repaired + candidate "Λ(1)′ ∩ Λ(2) is a type I factor"; σ-finiteness separately from + separability. +- Variants sighted, not pursued: the C*-independence side of the sS90 taxonomy; + HS17's curved-spacetime axiom variants a3′), a4′); the funnel property + (searched only by lane 5 as a keyword). +- AFP full-text search (site unreachable); Lean Zulip content; CoqQ source. +- KOE03's finite-index chapters beyond the grep hits; HS17's Thm_m>0, Thm_KMS, + Thm_ER<EN, Thm_EN<Em and the N-Cbit construction (iterate (R11)'s mechanism). +- **The unexamined base**: the tier (c) rows everything above stands on — (R2), + (R4), (R5) and the external legs of (A3), (A7), (A15), (R6)(b), (R8), (R11) + — plus the mineru-unchecked (b) layer of dB74/DL84. diff --git a/docs/math/umegaki-relative-entropy.md b/docs/math/umegaki-relative-entropy.md new file mode 100644 index 0000000..d07ef91 --- /dev/null +++ b/docs/math/umegaki-relative-entropy.md @@ -0,0 +1,1136 @@ +--- +object: Umegaki relative entropy +slug: umegaki-relative-entropy +status: draft +worst-tier: c +mathlib-rev: 5450b53e5ddc75d46418fabb605edbf36bd0beb6 +implemented-as: none +revisions: + - 2026-08-16 · 2d7f460 · initial extraction · sources: UME62, HIA18, VED02, WIT18, HS17, KW20 +--- + +<!-- +No document-level macro preamble: measured through @vscode/markdown-it-katex +(the plugin VS Code's own Markdown preview uses), no macro definition form -- +\newcommand, \gdef, \global\def -- survives from one math span to the next, so +a preamble here would leave every quote using it broken (see +check_render.py / render_check.js). Instead, every verbatim-quote math span +that needs a source's own macro (\S, \U, \A, \a from WIT18; \A from HS17, +whose own \A = \mathfrak{A}, NOT WIT18's \mathcal{A}; \< \> from HIA18; \mbox, +the LaTeX primitive KaTeX does not implement, reconstructed as \mathrm; \Tr, +\supp from KW20's unavailable Book_KW class, likewise reconstructed) carries a +local, self-contained \gdef of exactly that macro, e.g. +`$\gdef\lok#1{{\mathcal #1}}\lok{B}$` -- defined and used inside the same +$...$ pair, so it renders correctly without any state surviving to the next +span. The \gdef prefix is presentation, not content: strip it before +comparing a quote's tex against source.flat.txt for the quote check, and +audit it against the source's own definition (or, where marked +"reconstructed", against ordinary mathematical usage, since the source's own +macro table is unrecoverable). + +Everything the note says in its own voice is written in plain KaTeX. +--> +# Umegaki relative entropy + +## What this object is for + +The Umegaki relative entropy is a two-argument, non-symmetric, jointly convex, +jointly lower semicontinuous functional on pairs of normal positive functionals +on an operator algebra, whose classical special case is the Kullback–Leibler +divergence and whose operational meaning is the optimal error exponent in +asymmetric quantum hypothesis testing. Its defining structural property is +*monotonicity* under the adjoints of well-behaved positive unital maps — the +data-processing inequality — from which the literature derives non-negativity, +subadditivity, strong subadditivity, sufficiency of a subalgebra and the +entanglement measures of quantum field theory. + +Its generality is forced by a definitional obstruction, not by taste. The naive +formula $\mathrm{Tr}\,\rho(\log\rho-\log\sigma)$ needs a trace and needs $\log\sigma$, and +a type III von Neumann algebra — the case that actually occurs for the local +algebras of a quantum field — has neither. The object therefore has two lives: a +trace-based one (Umegaki 1962) and a relative-modular-operator one (Araki +1976/77), and the point of the modern treatment is that the second restricts to +the first where the first exists. + +## Definition + +### Variants as the sources write them + +The columns are the axes along which this corpus actually splits. `Total?` asks +whether the definition assigns a value to *every* pair, or is a partial function +whose domain is a hypothesis. `Apparatus` is what the formula is built out of. + +| (D#) | Source | Ambient algebra | Trace needed | Apparatus | Arguments | Total? | Tier | +|---|---|---|---|---|---|---|---| +| (D1) | [UME62] | finite class, σ-finite | **yes**, faithful normal finite | trace + Radon–Nikodym densities in $L^1(A)$ | both normalised | **no** — domain is $a\prec b$ | b | +| (D2) | [HIA18] §1 | semifinite | **yes**, semifinite | trace + density operators | both normal states | yes, case split | a / c | +| (D3) | [HIA18] §1 | arbitrary | no | relative modular operator | both normal states | yes, case split | a / c | +| (D4) | [HIA18] Def. 2.1 | arbitrary | no | standard $f$-divergence at $f(t)=t\log t$ | both in $M_*^+$, unnormalised | yes, boundary term | a | +| (D5) | [VED02] | none (matrices) | Tr | bare formula | both states | **no** — silent off the domain | a | +| (D6) | [WIT18] | arbitrary, with a cyclic separating vector | no | relative modular operator, vector form | $\Psi$ cyclic separating; $\Phi$ any | yes, $+\infty$ a consequence | a / c | +| (D7) | [HS17] | standard form, σ-finite | no | relative modular operator + Connes cocycle | both **faithful** normal states | partial, then stipulated | a / c | +| (D8) | [KW20] | $\mathcal B(\mathcal H)$, $\dim\mathcal H<\infty$ | Tr | bare formula, base 2 | first a state, second only PSD | yes, case split | a | +| (D9) | [HIA18] §3 / Kosaki | arbitrary | no | supremum over step-function paths | both in $M_*^+$ | yes | a / c | + +**(D1) [UME62] §4, Definition 1 (journal pp. 68–69)** — tier (b), +`mineru-cross-checked-against-PDF-text-layer` + +The cache is MinerU model output, so no blockquote is taken from it; the +following is a paraphrase, and every clause below was confirmed independently +against the PDF's own OCR text layer (`pdftextlayer.txt`, extracted with `pypdf`) +as well as against the MinerU conversion. + +Standing setting (§1): $A$ is a von Neumann algebra **of finite class and +σ-finite**, carrying a fixed faithful normal (finite) trace $\tau$; $L^p(A)$ are +Segal's measurable-operator spaces; a normal state $\rho$ has a +Radon–Nikodym derivative $d\rho/d\tau\in L^1(A)$ with +$\rho(a)=\tau((d\rho/d\tau)a)$. For self-adjoint $a,b$, Umegaki writes $a\prec b$ +to mean $s(a)\le s(b)$, $s(\cdot)$ the support projection. + +Definition 1: for $a,b\in L^1(A)$ with $a,b\ge 0$, $a\prec b$ and +$\tau(a)=\tau(b)=1$, +$$I(a,b)=\tau(a\log a-a\log b),$$ +called the **information** between $a$ and $b$; and for normal states with +$\sigma\prec\rho$, $I(\sigma,\rho):=I(d\sigma/d\tau,\,d\rho/d\tau)$. The singular +part is cut off inside the definition by $a\log b:=a\,s(b)\log b$. + +Four points that are easy to get wrong: + +1. **The name is *information*, not *relative entropy*.** See (X6): the string + "relative entropy" occurs zero times in the paper. *Entropy* is reserved for + the one-argument $H(a)=-\tau(a\log a)$, and §5 defines a separate symmetric + *divergence* $J(a,b)=I(a,b)+I(b,a)$ on the stricter domain $a\sim b$. +2. **The support condition is a hypothesis of the definition, not a case.** + Outside $a\prec b$, $I(a,b)$ receives no value at all. There is no $+\infty$ + branch anywhere in the paper. +3. **Finite entropy is *not* in Definition 1.** The class + $\mathcal E=\{a\in L^1(A): a\ge0,\ |H(a)|<\infty,\ \tau(a)=1\}$ is defined + separately at the end of §3 and is a hypothesis of the *theorems* (R7)–(R11). + Proposition 4.1 gives two independent sufficient conditions for $I(a,b)$ to be + unambiguous in $(-\infty,+\infty]$: $ab=ba$, or $H(a)$ finite with $b$ bounded. +4. **The setting is *finite*, not semifinite — but Umegaki descopes it himself.** + §1 states that a faithful normal trace exists iff $A$ is of finite class and + σ-finite, then says the assumption "is not necessarily essential" and that for + semifinite $A$ with a semi-trace all theorems and propositions hold "by a + little or simply modified proofs", subject to an unverified side condition on + subalgebras. Footnote 5) goes further and *extends the definition itself* to + the semifinite case for $0\le a\in L^1(A)$ and $0\le b\in A$ with $a\prec b$, + where $b$ need not lie in $L^1(A)$ — so **Umegaki's own semifinite extension + already drops the normalisation of the second argument**, which is (D8)'s + asymmetric regime, in 1962. See (C4) and (X7). + +`differs from (D2):` a finite trace rather than a semifinite one; a partial +function rather than a total one; both arguments normalised; and a different +name. +`sources claim equivalence:` [HIA18] presents (D2) *as* Umegaki's; no source in +the corpus remarks on the finite/semifinite difference. + +**(D2) [HIA18] §1, eq. (F-1.1)** — tier (a) for HIA18's restatement, tier (c) as +a claim about what [UME62] says + +> $$ +> D(\rho\|\sigma):=\begin{cases} +> \tau(d_\rho(\log d_\rho-\log d_\sigma)) & \text{if $s(\rho)\le s(\sigma)$}, \\ +> +\infty & \text{otherwise}, +> \end{cases} +> $$ + +introduced by + +> the relative entropy $D(\rho\|\sigma)$ was first +> introduced in 1962 by Umegaki \cite{Um} for normal states $\rho,\sigma$ on a semifinite von + +and glossed + +> where $\tau$ is a semifinite trace on $M$, $d_\rho$ is the density operator of $\rho$ with +> respect to $\tau$ and $s(\rho)$ is the support projection of $\rho$. + +`differs from (D1):` semifinite versus finite trace; total versus partial; and +the **letters are swapped** — see (C1), under which +$D_{\mathrm{HIA}}(\rho\|\sigma)=I_{\mathrm{UME}}(\sigma,\rho)$. +`sources claim equivalence:` yes, by identification. See (X7) for the +adjudication: this is a defensible generalisation of a claim [UME62] makes about +itself, not a transcription of its standing hypothesis. + +**(D3) [HIA18] §1, eq. (F-1.2)** — tier (a) for the restatement, tier (c) for the +attribution to Araki, whose papers were not obtained + +> $$ +> \gdef\<{\langle}\gdef\>{\rangle} +> D(\rho\|\sigma):=\begin{cases} +> -\<\xi_\rho,(\log\Delta_{\sigma,\rho})\xi_\rho\> +> =\<\xi_\sigma,(\Delta_{\rho,\sigma}\log\Delta_{\rho,\sigma})\xi_\sigma\> +> & \text{if $s(\rho)\le s(\sigma)$}, \\ +> +\infty & \text{otherwise}, +> \end{cases} +> $$ + +preceded by + +> Later in 1970's Araki +> \cite{Ar5,Ar2} extended Umegaki's relative entropy, by introducing the \emph{relative modular +> operator} $\Delta_{\rho,\sigma}$ for normal states $\rho,\sigma$, to general +> von Neumann algebras as + +Supporting apparatus (HIA18 §2.1, tier (a) for HIA18's statements): every +$\sigma\in M_*^+$ has a unique vector representative $\xi_\sigma$ in the natural +cone with $\sigma(x)=\langle\xi_\sigma,x\xi_\sigma\rangle$; +$S_{\rho,\sigma}(x\xi_\sigma+\eta):=s_M(\sigma)x^*\xi_\rho$ for $x\in M$, +$\eta\in(1-s_{M'}(\sigma))\mathcal H$; +$\Delta_{\rho,\sigma}:=S_{\rho,\sigma}^*\overline{S_{\rho,\sigma}}$; the support +projection of $\Delta_{\rho,\sigma}$ is $s_M(\rho)s_{M'}(\sigma)$. + +Note the **two equal expressions with opposite subscript orders** inside +(F-1.2). That is the notational trap; see (C2). + +`differs from (D1)/(D2):` no trace, no density operators, no semifiniteness — +valid for an arbitrary von Neumann algebra, type III included. +`sources claim equivalence:` [HIA18] says Araki *extended* Umegaki's, by +citation, not by proof. The reduction is *proved* in this corpus only for +$\mathcal B(\mathcal H)$ ((R3)) and for finite-dimensional bipartite systems ((R13)). See +`## Open questions`. + +**(D4) [HIA18] Definition 2.1, eqs. (F-2.4)–(F-2.6)** — tier (a) + +For $\rho,\sigma\in M_*^+$ (arbitrary normal **positive functionals**, not +necessarily states) and $f$ convex on $(0,\infty)$: + +> We then introduce the {\it standard $f$-divergence} $S_f(\rho\|\sigma)$ of $\rho,\sigma$ by + +> $$ +> \gdef\<{\langle}\gdef\>{\rangle} +> S_f(\rho\|\sigma):=\<\xi_\sigma,f(\Delta_{\rho,\sigma})\xi_\sigma\> +> +f(0^+)\sigma(1-s_M(\rho))+f'(+\infty)\rho(1-s_M(\sigma)). +> $$ + +with $f(\Delta_{\rho,\sigma}):=\int_{(0,+\infty)}f(t)\,dE_{\rho,\sigma}(t)$ — the +spectral integral over the **open** interval, the endpoints carried by the two +boundary terms explicitly — and the scalar conventions $bf(0/b):=f(0^+)b$, +$0f(a/0):=f'(+\infty)a$, $(+\infty)\cdot0:=0$, $(+\infty)c:=+\infty$ for $c>0$. +The relative entropy is the case $f(t)=t\log t$, for which $f(0^+)=0$ and +$f'(+\infty)=+\infty$. + +`differs from (D3):` **not in value** — (D4) *is* (D3) at $f=t\log t$, extended +off states to all of $M_*^+$. It differs in *mechanism*: the $+\infty$ on +non-dominated supports is not a case split but the boundary term +$f'(+\infty)\rho(1-s_M(\sigma))$, which is what makes (D4) automatically +homogeneous, additive over direct sums and jointly lower semicontinuous. +`sources claim equivalence:` yes — HIA18 Remark 2.7 and Example 3.12, proved in +source for $\mathcal B(\mathcal H)$ with $\dim\mathcal H<\infty$. + +A near-miss recorded here rather than as its own variant: HIA18 Remark 2.7 also +displays Petz's *quasi-entropy* at $k=1$, which for $f=t\log t$ gives +$\mathrm{Tr}\, D_\rho(\log D_\rho-\log^+ D_\sigma)$ with $\log^+0:=0$ — finite for every +pair — and calls it improper. That is a formulation the literature exhibits and +discards; it is (X1). + +**(D5) [VED02], the Definition following the von Neumann entropy, eq. (def8)** — +tier (a) + +> {\bf Definition}. {\em The von Neumann relative entropy} between +> the two states $\sigma$ and $\rho$ is defined as + +> $$ +> \gdef\mbox#1{\mathrm{#1}} +> S_N(\sigma ||\rho) = \mbox{Tr} \sigma (\ln \sigma - \ln \rho) \;\; . +> $$ + +Explicit natural logarithm. **No support condition and no $+\infty$ branch at +all**; the singular case is not discussed anywhere in the paper. The attribution +is in the same paragraph: + +> this quantity was first considered by Umegaki (1962), but for +> consistency reasons I name it after von Neumann; I will also refer +> to it as the quantum relative entropy). + +`differs from (D8):` no support condition, hence a partial function whose domain +the source never states; natural log; second argument a state. +`sources claim equivalence:` with Umegaki's, asserted. VED02 is internally +consistent in keeping $\sigma$ first throughout (F1–F3, Theorem 5, the Stein +exponent). + +**(D6) [WIT18] §"Relative Entropy In Quantum Field Theory", eq. (onorf)** — +tier (a) for WIT18's statements, tier (c) as a claim about Araki + +> $$\gdef\S{{\mathcal S}}\gdef\U{{\mathcal U}}\gdef\la{\langle}\gdef\ra{\rangle} +> \S_{\Psi|\Phi}(\U)= -\la\Psi|\log \Delta_{\Psi|\Phi}|\Psi\ra.$$ + +with $\Delta_{\Psi|\Phi}:=S^\dagger_{\Psi|\Phi}S_{\Psi|\Phi}$ and +$S_{\Psi|\Phi}\,a|\Psi\rangle=a^\dagger|\Phi\rangle$; $\Psi$ must be cyclic and +separating for $\mathcal A_{\mathcal U}$, while $\Phi$ may be any state. The singular case is a +consequence rather than a stipulation: + +> For example, $\gdef\S{{\mathcal S}}\gdef\U{{\mathcal U}}\S_{\Psi|\Phi}(\U)$ may be $+\infty$ if $\Delta_{\Psi|\Phi}$ has a zero eigenvalue, which will occur if $\Phi$ is not separating for $\gdef\A{{\mathcal A}}\gdef\U{{\mathcal U}}\A_\U$. + +`differs from (D3):` the subscript convention on $S$ and $\Delta$ is reversed +(C2), and the first argument must carry a cyclic separating vector. +`sources claim equivalence:` yes, and **proved in source** for the +finite-dimensional case — see (R13). + +**(D7) [HS17], the Definition of the relative entropy, eq. (drel1)** — tier (a) +for HS17's statements, tier (c) as a claim about Araki + +> $$H(\omega, \omega') = \langle \Omega | \log \Delta_{\omega, \omega'} \ \Omega\rangle$$ + +together with the Connes-cocycle expression +$\lim_{t\to0}\omega([D\omega:D\omega']_t-1)/(it)$ as part of the same +definition — a **third** equivalent expression, present in no other corpus +source. Standing hypotheses: + +> One assumes to be given two faithful, normal states $\omega, \omega'$ on a v. Neumann algebra $\gdef\A{\mathfrak{A}}\A$ in standard form. + +and three stipulations the others do not make: an explicit two-parameter +rescaling rule +$H(\lambda\omega,\lambda'\omega')=\lambda H(\omega,\omega')+\lambda\log(\lambda/\lambda')$; +$H(\omega,\omega')=\infty$ when $\omega'$ is not normal; and the frank + +> When $\omega$ or $\omega'$ are not faithful (such that +> $|\Omega\rangle,|\Omega'\rangle$ are not standard), the definition has to be somewhat modified~\cite{ohya_1}. + +The **plus** sign here is not a disagreement with HIA18's minus: HS17 uses +HIA18's subscript order and inserts the index-reversed $\Delta$. See (C2). + +`differs from (D3):` requires both states faithful; carries the cocycle formula +as part of the definition; and its domain is the positive functionals of a +**C\*-algebra**, not $M_*^+$ — see (C8). +`sources claim equivalence:` the $\mathcal B(\mathcal H)$ reduction is computed in source; the +general equality with Araki's is asserted by citation. + +**(D8) [KW20], Definition (`def-rel_ent`)** — tier (a) + +Stated for *every state* $\rho$ and *positive semi-definite operator* $\sigma$ — +the asymmetry is in the definition's own hypothesis, not a later remark: + +> the \textit{quantum relative entropy of $\rho$ and $\sigma$}, denoted by $D(\rho\Vert\sigma)$, is defined as + +> $$ +> \gdef\Tr{\mathrm{Tr}\,}\gdef\supp{\operatorname{supp}} +> D(\rho\Vert\sigma)=\left\{\begin{array}{l l} \Tr[\rho(\log_2 \rho-\log_2\sigma)] & \text{if }\supp(\rho)\subseteq\supp(\sigma),\\ +\infty & \text{otherwise}. \end{array}\right. +> $$ + +with $0\log_2 0=0$, base 2 uniformly, finite dimensions throughout, and the +asymmetry stated deliberately: + +> More generally, we could define the quantum relative entropy exactly as above, but with both arguments being positive semi-definite operators. For our purposes in this book, however, it suffices to restrict the first argument to be a state. + +`differs from (D2):` base 2; finite dimensions; second argument merely positive +semi-definite. +`sources claim equivalence:` with Umegaki's, by citation; with the regularised +form $\lim_{\varepsilon\to0^+}D(\rho\|\sigma+\varepsilon\mathbb 1)$, **proved in +source** — see (R14). + +**(D9) [HIA18] §3, Theorem 3.5 and Example 3.12; Kosaki 1986** — tier (a) for +HIA18's own expression, tier (c) for Kosaki's, whose paper was not obtained + +HIA18's expression, obtained by specialising its Theorem 3.5 to $f=-\log t$, is a +supremum over $n\in\mathbb N$ and over $M$-valued step functions $x(\cdot)$ of an +explicit expression involving $\sigma(1)\log n$, a correction term +$(\sigma(1)-\rho(1))\tfrac{2}{n+1}$, and +$-\int_{[1/n,n]}\{\sigma((1-x(s))^*(1-x(s)))+s^{-1}\rho(x(s)x(s)^*)\}s^{-1}\,ds$. + +Substitution sentence for Kosaki (tier (c), no locator into Kosaki's paper): +*Kosaki, "Relative entropy of states: a variational expression" (J. Operator +Theory 16 (1986) 335–348, per HIA18's bibliography) expresses the relative +entropy as a supremum over $n$ and over $M$-valued step functions $x(\cdot)$ of +$\sigma(1)\log n-\int_{[1/n,+\infty)}\{\sigma((1-x(s))^*(1-x(s)))+s^{-1}\rho(x(s)x(s)^*)\}s^{-1}\,ds$ +— the integration range $[1/n,+\infty)$ being the only difference HIA18 names — +from which positivity, joint convexity, lower semicontinuity and monotonicity +follow directly. Whether Kosaki states it on $M_*^+$ or on states is not +recoverable from HIA18's reproduction.* + +HIA18 on the relation: + +> This expression is similar to but a bit different from the variational expression + +`differs from (D4):` the same functional, presented as a supremum; the cut-off +interval is two-sided in HIA18 and one-sided in Kosaki, which HIA18 explains as +what makes its expression behave under the transpose +$\widetilde f(t)=tf(t^{-1})$. +`sources claim equivalence:` yes — HIA18 derives its own from Theorem 3.5, +proved in source, and reports Kosaki's as an alternative for the same quantity. + +There is a **second, genuinely different** variational expression, Petz's, at +(R16); it is not a further (D#) because HIA18 states it for the relative entropy +already defined, not as a definition. + +### Adopted general form + +Let $M$ be a von Neumann algebra in standard form $(M,\mathcal H,J,\mathcal P)$, and let +$M_*^+$ be the positive cone of its predual — the normal positive linear +functionals on $M$, **not** required to be normalised, faithful, or nonzero. For +$\rho,\sigma\in M_*^+$ let $\xi_\rho,\xi_\sigma$ be their unique vector +representatives in the natural cone $\mathcal P$, let $s_M(\cdot)$ and +$s_{M'}(\cdot)$ denote the support projections in $M$ and in $M'$, let +$S_{\rho,\sigma}$ be the conjugate-linear operator determined on the dense +subspace $M\xi_\sigma+(1-s_{M'}(\sigma))\mathcal H$ by +$S_{\rho,\sigma}(x\xi_\sigma+\eta):=s_M(\sigma)x^*\xi_\rho$, and let +$\Delta_{\rho,\sigma}:=S_{\rho,\sigma}^*\overline{S_{\rho,\sigma}}$ be the +relative modular operator, with spectral measure $E_{\rho,\sigma}$ on +$s_M(\rho)s_{M'}(\sigma)\mathcal H$. Writing $f(t)=t\log t$ on $(0,\infty)$, so that +$f(0^+)=0$ and $f'(+\infty)=+\infty$, the **Umegaki relative entropy of $\rho$ +with respect to $\sigma$** is +$$ +D(\rho\|\sigma)\;:=\;\int_{(0,+\infty)}t\log t\;d\|E_{\rho,\sigma}(t)\xi_\sigma\|^2 +\;+\;0\cdot\sigma(1-s_M(\rho))\;+\;(+\infty)\cdot\rho(1-s_M(\sigma)), +$$ +the integral taken over the **open** interval, under the scalar conventions +$(+\infty)\cdot0:=0$ and $(+\infty)c:=+\infty$ for $c>0$. Its value lies in +$(-\infty,+\infty]$ for every pair; it is $+\infty$ exactly when +$s_M(\rho)\not\le s_M(\sigma)$ or the spectral integral diverges. Natural +logarithm (C3). The definition carries **no** hypothesis of semifiniteness, +σ-finiteness, separability, faithfulness, normalisation, or finite dimension — +(A1)–(A5) are all absent from it — and the only standing assumption is that $M$ +is presented in a standard form, which is (A6) and is provable. + +This is **(D4)**, i.e. **(D3) totalised**. The discriminator is **(X4) type III +generality loss**: on the local algebra $\mathcal A_{\mathcal U}$ of a wedge region there is no +trace at all, so (D1)/(D2)/(D8) have no referent there, while every ingredient +above exists for every von Neumann algebra — and (D4) loses nothing, because it +reproduces the trace expression on $\mathcal B(\mathcal H)$ ((R3)) and the classical +$f$-divergence on an abelian algebra ((R4)). **(X3)** shows the choice is forced +already one type earlier: the density-matrix form (D8) fails on any type II +factor, where a trace exists but $\mathrm{Tr}\,$-densities do not. Two further decisions +inside the modular family are settled by **(X5)**: the domain is $M_*^+$ rather +than the faithful states of (D7) or the cyclic-separating-vector states of (D6), +because the named pair $\rho=\mathrm{diag}(1,0)$, +$\sigma=\mathrm{diag}(\tfrac12,\tfrac12)$ on $M_2(\mathbb C)$ has relative +entropy $\log 2$ with $\rho$ not faithful, and neither (D6) nor (D7) reaches it. + +Two limits of this choice, stated here rather than buried: + +- **Nothing in this corpus proves that the adopted form agrees with (D1)/(D2) on + a general semifinite algebra.** What is proved is agreement on $\mathcal B(\mathcal H)$ (R3) and + in finite dimensions (R13); the nearest general statement, (R17), is restricted + to pairs related by a *bounded* relative Hamiltonian, is written with the + canonical trace on a crossed product rather than a trace on $M$, and is hedged + by its own source as a "complete resemblance". See `## Open questions`. +- (D7)'s domain is strictly **larger**: HS17 defines $H$ on the positive + functionals of a C\*-algebra, assigning $+\infty$ off the normal ones. That is + a totalisation by stipulation, recorded as (C8); the adopted form does not + include it. + +## Notation and conventions + +| (C#) | Axis | This note | Per source | Translation | +|---|---|---|---|---| +| (C1) | which argument is differentiated, and its letter | first argument, called $\rho$ | role order identical in **all six** sources: $(\text{first})(\log\text{first}-\log\text{second})$. Letters: [HIA18] [KW20] [WIT18] write $\rho$/$\Psi$ first; [UME62] [VED02] call that argument $\sigma$ | $D_{\mathrm{HIA}}(\rho\|\sigma)=I_{\mathrm{UME}}(\sigma,\rho)=S_{\mathrm{VED}}(\sigma\|\rho)$ after renaming | +| (C2) | subscript order on $S_{\cdot,\cdot}$, $\Delta_{\cdot,\cdot}$ | HIA18's: acts on the **second** index's vector | [HIA18] $S_{\rho,\sigma}(x\xi_\sigma+\eta)=s_M(\sigma)x^*\xi_\rho$; [HS17] $S_{\omega,\omega'}a\|\Omega'\rangle=a^*\|\Omega\rangle$; [WIT18] $S_{\Psi\|\Phi}a\|\Psi\rangle=a^\dagger\|\Phi\rangle$ — the **first** index | $\Delta^{\mathrm{WIT}}_{\Psi\|\Phi}=\Delta^{\mathrm{HIA}}_{\Phi,\Psi}$. HS17's $+$ with $\Delta_{\omega,\omega'}$ and HIA18's $-$ with $\Delta_{\sigma,\rho}$ are the same expression twice | +| (C3) | base of the logarithm | natural | explicit `\ln` [VED02]; explicit `\log_2` [KW20]; implicit natural [UME62] [HIA18] [WIT18] [HS17] | $D_{\mathrm{KW20}}=D/\ln 2$. Weightless for the definition ((X10)) but **two corpus statements are base-locked**: WIT18's positivity step $\log\lambda\le\lambda-1$ is false in base 2, and any pairing of $D$ with an exponential (Stein exponents) fixes the base | +| (C4) | normalisation of the arguments | none — both range over $M_*^+$ | both states: [UME62] main text, [VED02], [WIT18], [HS17] base case, [HIA18] (D2). First normalised / second free: [KW20], and **[UME62] footnote 5) in the semifinite case**. Both free: [HIA18] (D4), [HS17]'s extension | [HS17] gives the two-parameter rule $H(\lambda\omega,\lambda'\omega')=\lambda H+\lambda\log(\lambda/\lambda')$; [HIA18] states only joint homogeneity $S_f(\lambda\rho\|\lambda\sigma)=\lambda S_f(\rho\|\sigma)$. Consistent, but (R10)(h4) needs the former | +| (C5) | mechanism for the singular case | boundary term | hypothesis [UME62]; case split [HIA18](D2) [KW20]; boundary term [HIA18](D4); regularised limit [KW20]; consequence of a zero eigenvalue [WIT18]; silence [VED02] | all give $+\infty$ where all are defined; only the *shape* differs | +| (C6) | spelling of the support condition | $s_M(\rho)\le s_M(\sigma)$ | $s(\rho)\le s(\sigma)$ [HIA18]; $\operatorname{supp}(\rho)\subseteq\operatorname{supp}(\sigma)$ [KW20]; $\sigma\prec\rho$ [UME62], in its own letters | one condition, three spellings; always the dominated support is on the differentiated argument | +| (C7) | conjugate-linearity of the inner product | first slot | **no source in the corpus states it.** Inferred (tier (b)) from [HIA18] writing $\sigma(x)=\langle\xi_\sigma,x\xi_\sigma\rangle$ with $J$ a conjugate-linear involution, and [WIT18]'s Dirac notation | value-irrelevant for $\langle\xi,A\xi\rangle$ with $A$ self-adjoint | +| (C8) | domain of the functional | $M_*^+\times M_*^+$ | [HS17] extends to **all** positive functionals of a C\*-algebra, setting $H=\infty$ off the normal ones | a stipulated totalisation, not covered by the adopted form. **[HS17] is internally inconsistent**: its Definition totalises on the second argument only, while its (h1) reads as totalising on both | + +## Results and dependencies + +Grouped by source. Numbering is this note's. + +### (R1) $S_f(\rho\|\sigma)$ is well defined in $(-\infty,+\infty]$ + +For every $\rho,\sigma\in M_*^+$ and convex $f$, the expression of the adopted +form is well defined with values in $(-\infty,+\infty]$; in particular the +relative entropy never takes the value $-\infty$. + +- Source: [HIA18] Lemma 2.2 · tier (a) · **proved in source** +- Depends on: (A6), convexity of $f$ — which $t\log t$ satisfies, and which is + strictly weaker than the (A7) needed later, [ext: HIA18 §2.1 cites + [Ar2, Lemma 2.2] for the closability of $S_{\rho,\sigma}$ and + $F_{\rho,\sigma}$ and the identity + $S^*_{\rho,\sigma}=\overline{F_{\rho,\sigma}}$, which is what makes + $\Delta_{\rho,\sigma}$ a positive self-adjoint operator] +- Conventions: (C4) +- Proof route: (i) convexity gives an affine minorant $f(t)\ge a+bt$; (ii) + integrate it against $d\|E_{\rho,\sigma}(t)\xi_\sigma\|^2$; (iii) the constant + term evaluates to $a\,\sigma(s_M(\rho))$ by the support identity of (A6); (iv) + the linear term evaluates to $b\,\rho(s_M(\sigma))$, using + $J\Delta^{1/2}\xi_\sigma=s_M(\sigma)\xi_\rho$ from the closability edge; (v) + both are finite reals, so the integral exceeds $-\infty$. +- Verbatim: + > For every $\rho,\sigma\in M_*^+$, $S_f(\rho\|\sigma)$ is well defined with values in $(-\infty,+\infty]$. + +### (R2) Invariance, degenerate values, homogeneity, direct-sum additivity + +(1) $S_f(\rho\circ\Phi\|\sigma\circ\Phi)=S_f(\rho\|\sigma)$ for a +\*-isomorphism $\Phi$. (2) $S_f(0\|\sigma)=f(0^+)\sigma(1)$, +$S_f(\rho\|0)=f'(+\infty)\rho(1)$, $S_f(\sigma\|\sigma)=f(1)\sigma(1)$. +(3) $S_f(\lambda\rho\|\lambda\sigma)=\lambda S_f(\rho\|\sigma)$ for +$\lambda\ge0$. (4) Additivity over a **direct sum** $M=M_1\oplus M_2$. + +- Source: [HIA18] Proposition 2.3 · tier (a) · **proved in source** +- Depends on: (A6), [ext: Haagerup's uniqueness of the standard form up to a + unitary intertwining the modular conjugations and natural cones — needed for + (1) only] +- Conventions: (C4) +- Proof route: (1) uniqueness of the standard form; (2) directly from the + definition; (3) substitute $\xi_{\lambda\sigma}=\sqrt\lambda\,\xi_\sigma$ and + $\Delta_{\lambda\rho,\lambda\sigma}=\Delta_{\rho,\sigma}$, with $\lambda=0$ + from (2); (4) the standard form of a direct sum is the direct sum of the + standard forms, so $\Delta$ decomposes and the definition reads off. +- **This is direct-sum, not tensor-product, additivity.** No tensor additivity + statement appears in [HIA18]; the tensor statement is (R11), (R18), (R10)(h7), + (R15)(3), and they must not be merged. + +### (R3) The modular form reproduces the trace form on a type I factor + +For $M=\mathcal B(\mathcal H)$ with $\mathcal H$ arbitrary, +$\Delta_{\rho,\sigma}=L_{D_\rho}R_{D_\sigma^{-1}}$ (generalised inverse), and +$S_f(\rho\|\sigma)$ evaluates to the double sum +$\sum_{a>0}\sum_{b>0}bf(a/b)\mathrm{Tr}\, P_aQ_b$ plus the two boundary terms. At +$f=t\log t$ this is $\mathrm{Tr}\, D_\rho(\log D_\rho-\log D_\sigma)$ when +$s_M(\rho)\le s_M(\sigma)$ and $+\infty$ otherwise. + +- Source: [HIA18] Example 2.6 and Remark 2.7 · tier (a) · **proved in source** +- Depends on: (R1), the standard form of $\mathcal B(\mathcal H)$ on the Hilbert–Schmidt class with + $J=\ {}^*$ +- Conventions: (C5), (C6) +- **This is the corpus's cleanest statement that the adopted form restricts to + the trace form — but it is for $\mathcal B(\mathcal H)$, not for a general semifinite algebra.** +- Verbatim: + > On the other hand, $S_{t\log t}(\rho\|\sigma)$ in \eqref{F-2.6} coincides with the usual \emph{relative entropy} + +### (R4) The commutative case is the classical $f$-divergence + +For $M\cong L^\infty(\Omega,\mu)$ with $\mu$ σ-finite and $\rho,\sigma$ given by +densities $\phi,\psi$, $\Delta_{\rho,\sigma}$ is multiplication by the classical +Radon–Nikodym derivative and $S_f(\rho\|\sigma)$ equals +$\int_{\{\phi>0\}\cap\{\psi>0\}}\psi f(\phi/\psi)\,d\mu$ plus the two boundary +integrals — the classical $f$-divergence, hence at $f=t\log t$ the +Kullback–Leibler divergence. + +- Source: [HIA18] Example 2.5 · tier (a) · **proved in source** (HIA18 calls the + computation straightforward) +- Depends on: (R1) +- Conventions: (C6) +- The σ-finiteness here is the *example's*, needed to write $M_*\cong L^1$; it is + not a hypothesis of the adopted form. Mutually singular measures give + $+\infty$, the correct classical value. + +### (R5) Transpose symmetry + +$S_f(\rho\|\sigma)=S_{\widetilde f}(\sigma\|\rho)$ with +$\widetilde f(t):=tf(1/t)$. Since $t\log t$ and $-\log t$ are transposes, +$D(\sigma\|\rho)=S_{t\log t}(\sigma\|\rho)=S_{-\log t}(\rho\|\sigma)$. + +- Source: [HIA18] Proposition 2.4 and Example 3.12 · tier (a) · **proved in + source** +- Depends on: (R1), (A6) — for the support identity + $s_M(\rho)s_{M'}(\sigma)=Js_M(\sigma)s_{M'}(\rho)J$ used at step (ii), + [ext: HIA18 §2.2 cites [Ar2, Theorem 2.4] for + $\Delta_{\rho,\sigma}=J\Delta_{\sigma,\rho}^{-1}J$] +- Proof route: (i) $\widetilde f(0^+)=f'(+\infty)$ and + $\widetilde f{}'(+\infty)=f(0^+)$, so only the spectral term needs work; + (ii) apply the $J$-conjugation identity together with + $s_M(\rho)s_{M'}(\sigma)=Js_M(\sigma)s_{M'}(\rho)J$; (iii) convert the spectral + measure of one pair into that of the other; (iv) change variables in the + integral. + +### (R6) Variational expression + +For $f$ operator convex, $S_f(\rho\|\sigma)$ equals a supremum over $n$ and over +$M$-valued step functions of an explicit expression; see (D9). + +- Source: [HIA18] Theorem 3.5 · tier (a) · **proved in source** +- Depends on: (R1), (A7), [ext: the Lesniewski–Ruskai integral representation of + an operator convex function on $(0,\infty)$ as + $a+b(t-1)+c(t-1)^2+\int(t-1)^2/(t+s)\,d\mu(s)$ with $c\ge0$, $\mu\ge0$ + and the data unique — HIA18 cites it as `\cite{LR}`, which is + **Lesniewski**–Ruskai 1999, not Lieb–Ruskai] +- Conventions: (C4) +- Proof route: (i) rewrite $S_{f_n}$ using the truncated representation; (ii) + HIA18 Lemma 3.4 turns the spectral integral into an infimum over $M$-valued + step functions; (iii) take $\sup_n$ and apply HIA18 Lemma 3.2's monotone + convergence $S_{f_n}\nearrow S_f$. +- **This is the engine of the whole theory**: (R7) and everything after it is + read off from it. + +### (R7) Joint lower semicontinuity, joint convexity, monotonicity, martingale convergence + +For $f$ operator convex and $\rho,\sigma\in M_*^+$: (i) $(\rho,\sigma)\mapsto +S_f(\rho\|\sigma)$ is jointly lower semicontinuous in the $\sigma(M_*,M)$ +topology; (ii) it is jointly convex and jointly subadditive; (iii) it is +monotone in each argument under the stated sign conditions on $f(0^+)$ and +$f'(+\infty)$; (iv) **data processing** — for $\Phi:N\to M$ **unital**, positive, +normal and a Schwarz map, $S_f(\rho\circ\Phi\|\sigma\circ\Phi)\le +S_f(\rho\|\sigma)$; (v) martingale convergence along an increasing net of unital +subalgebras generating $M$. + +- Source: [HIA18] Theorem 4.1 · tier (a) · **proved in source**, all five parts +- Depends on: (R6), (R2)(3), (A7), (A11) +- Conventions: (C4). $\Phi$ acts on observables, so states pull back — the + Heisenberg picture. **Schwarz is strictly weaker than 2-positive and much + weaker than completely positive**, so this is a stronger theorem than the + usual CPTP data-processing inequality. +- Proof route: (i)+(ii) the bracketed expression in the variational formula is + affine and $\sigma(M_*,M)$-continuous in $(\rho,\sigma)$, and a supremum of + such is jointly lsc and jointly convex — convexity and subadditivity being + equivalent by the homogeneity of (R2)(3); (iii) read off the sign of the + boundary coefficients in the same formula; (iv) push an $N$-valued step + function $x(\cdot)$ to $y(s):=\Phi(x(s))$ and use unitality plus the Schwarz + inequality to dominate each bracket, then take suprema; (v) take the dense + subspace in (R6) to be $\bigcup_\alpha M_\alpha$, so that restricting the step + functions recovers the restricted divergence. +- Verbatim: + > \emph{Monotonicity:} Let $N$ be another von Neumann algebra and $\Phi:N\to M$ be a unital positive linear map that is normal + +### (R8) Peierls–Bogoliubov and strict positivity + +(1) $S_f(\rho\|\sigma)\ge\sigma(1)f(\rho(1)/\sigma(1))$, with equality (for +non-linear $f$, nonzero arguments) iff $\rho=(\rho(1)/\sigma(1))\sigma$. +(2) If $f$ is non-linear with $f(1)=0$ and $\rho(1)=\sigma(1)>0$ then +$S_f(\rho\|\sigma)\ge0$ with equality iff $\rho=\sigma$. At $f=t\log t$ this is +**non-negativity of the relative entropy with the equality case, on an arbitrary +von Neumann algebra**. + +- Source: [HIA18] Corollary 4.2 · tier (a) · **proved in source** +- Depends on: (R7)(iv), (A3), (A7), (A8), and HIA18's own Lemma 4.3 — an + elementary strict-convexity lemma it proves in source "since we find no + suitable reference" +- Conventions: (C4) — the hypothesis $\rho(1)=\sigma(1)$ is not decorative; see + the degeneracy row for dropped normalisation. +- Proof route: (i) apply (R7)(iv) to the subalgebra $\mathbb C1$, which is + exactly the scalar computation and yields the Peierls–Bogoliubov bound; + (ii) if $\rho=k\sigma$ then $\Delta_{\rho,\sigma}=k\Delta_\sigma$, the spectral + measure is a point mass and equality holds; (iii) conversely apply (R7)(iv) to + $\mathbb Ce+\mathbb Ce^\perp$ for each projection $e$; (iv) Lemma 4.3 forces + proportionality on every projection, hence $\rho=(\rho(1)/\sigma(1))\sigma$; + (v) part (2) is part (1) evaluated at $f(1)=0$ under the equal-mass hypothesis + $\rho(1)=\sigma(1)>0$ of (A3), with (A8) supplying the strictness. +- **Dependency inversion worth recording**: here non-negativity is a *corollary + of data processing*, not of Klein's inequality. +- Verbatim: + > \emph{Strict positivity:} Assume that $f$ is non-linear with $f(1)=0$ and $\rho(1)=\sigma(1)>0$. Then $S_f(\rho\|\sigma)\ge0$, and $S_f(\rho\|\sigma)=0$ $\iff$ $\rho=\sigma$. + +### (R9) Support reduction, orthogonal-support additivity, $\varepsilon$-regularisation + +(1) If $s_M(\rho),s_M(\sigma)\le e$ then $S_f(\rho\|\sigma)=S_f(e\rho e\|e\sigma e)$ +computed in $eMe$. (2) If the joins of the supports of two pairs are orthogonal, +$S_f$ is additive over the sum. (3) If $S_f(\omega_1\|\omega_2)<+\infty$ then +$S_f(\rho\|\sigma)=\lim_{\varepsilon\searrow0}S_f(\rho+\varepsilon\omega_1\|\sigma+\varepsilon\omega_2)$. + +- Source: [HIA18] Corollary 4.4 · tier (a) · **proved in source** +- Depends on: (R6), (R7)(i), (R7)(ii), (R7)(iv), (R2)(3), (R2)(4) +- Proof route: (1) "$\le$" by (R7)(iv), "$\ge$" by pushing $x(s)\mapsto ex(s)e$ + through (R6); (2) "$\ge$" via the subalgebra $eMe\oplus e^\perp Me^\perp$ with + (R7)(iv) and (R2)(4) and part (1), "$\le$" via the unital map + $x\mapsto exe+e^\perp xe^\perp$ and (R7)(iv); (3) "$\le$" from (R7)(i), + "$\ge$" from (R7)(ii) with (R2)(3). +- **(3) is the general-algebra analogue of (R14)**, and it is *conditional*: it + needs a reference pair of finite divergence, which $\sigma+\varepsilon\mathbb1$ + supplies only where a trace does. + +### (R10) The property list of the Araki relative entropy + +[HS17] enumerates (h1) positivity with the equality case, and $H=\infty$ for +non-normal arguments; (h2) weak lower semicontinuity **on the positive +functionals of a C\*-algebra**; (h3) subadditivity, equivalently convexity; (h4) +superadditivity in the first argument; (h5) monotonicity in the arguments; (h6) +Uhlmann's monotonicity theorem for normalised completely positive maps, with +equality for a faithful normal conditional expectation preserving a faithful +normal state; (h7) the tensor **chain rule** +$H(\omega,\omega'_1\otimes\omega'_2)=H(\omega,\omega_1\otimes\omega_2)+H(\omega_1,\omega'_1)+H(\omega_2,\omega'_2)$. + +- Source: [HS17], the (h1)–(h7) enumeration · tier (a) for the list, tier (c) for + each claim · **asserted** — every item is cited to Ohya–Petz, none is proved +- Depends on: [ext: Ohya–Petz, *Quantum entropy and its use* — a monograph + treatment of the Araki relative entropy establishing positivity with the + equality case, joint lower semicontinuity, joint convexity, superadditivity in + the first argument, monotonicity in both arguments and under normalised + completely positive maps, and the tensor chain rule. HS17 gives no theorem + numbers]; for (h6) additionally [ext: Lindblad 1973 and Uhlmann 1977 — + monotonicity of the relative entropy under normalised completely positive maps + between von Neumann algebras] +- Conventions: (C4) — (h4) needs HS17's two-parameter rescaling rule and is + **not** derivable from the joint homogeneity of (R2)(3); (C8) for (h2). +- Independent corpus support: (h1) is (R8)(2), (R12), (R15); (h2) is (R7)(i) but + only on $M_*^+$, a smaller domain; (h3) is (R7)(ii); (h5) is (R7)(iii); + (h6) is (R7)(iv), (R13), (R14), (R19). **(h4) and (h7) rest on Ohya–Petz alone + within this corpus.** Both were verified in finite dimensions during the + refutation pass — (h4) reduces exactly to the Holevo bound + $\chi\le H(\{\lambda_i\})$ via the compensation identity, with equality iff the + states are mutually orthogonal; (h7) is an algebraic identity needing no + product hypothesis on $\omega$. +- **(h5) is degenerate as printed**: for positive functionals on a unital + algebra, $\phi\le\omega$ with $\|\phi\|=\|\omega\|$ forces $\phi=\omega$, so + its first-argument clause is vacuous and (h5) states only monotonicity in the + second argument. +- Verbatim: + > (h1)] (positivity) $H(\omega, \omega') \ge 0$, and $H(\omega,\omega') = 0 \Rightarrow \omega=\omega'$ for states $\omega, \omega'$. + +### (R11) Additivity over tensor products (Umegaki) + +For $A_1,A_2$ **of finite class** with faithful normal traces, $A=A_1\otimes A_2$ +with $\tau=\tau_1\otimes\tau_2$, and $a_i\prec b_i$ in $\mathcal E(A_i)$: +$I(a_1\otimes a_2,\,b_1\otimes b_2)=I(a_1,b_1)+I(a_2,b_2)$, and likewise for the +divergence $J$. Corollary: $H(a_1\otimes a_2)=H(a_1)+H(a_2)$. + +- Source: [UME62] Theorem 3 and Corollary 3.1, §6 · tier (b), + `mineru-unchecked` · **proved in source** +- Depends on: (R7'), (A2), (A3), [ext: the tensor product of two von Neumann + algebras of finite class, with the tensor product of two faithful normal + traces, is again of finite class with a faithful normal trace — UME62 cites + Dixmier and Misonou by name for the direct-product construction] +- Conventions: (C1), (C4) +- Proof route: (i) split $\log(a_1\otimes a_2)$ into + $\log(a_1\otimes1)+\log(1\otimes a_2)$, an identity UME62 uses without proof; + (ii) regroup into the two bracketed differences, each of which is an + information in its own factor and therefore defined by (R7') under (A2); + (iii) factor $\tau=\tau_1\otimes\tau_2$ across elementary tensors, which is + where the Dixmier–Misonou edge enters; (iv) use $\tau(a_i)=1$ from (A3) to + kill the cross terms. +- Umegaki identifies this as the operator generalisation of the + Kullback–Leibler form of the Shannon–Wiener theorem for independent events. + It is **not** (R2)(4). + +### (R7') Non-negativity of the information + +For $a,b\in\mathcal E$ with $a\prec b$, $I(a,b)$ is uniquely determined and, when +finite, non-negative. Likewise for normal states of finite entropy. + +- Source: [UME62] Theorem 1, Corollary 4.1, Theorem 1′, §4 · tier (b), + `mineru-unchecked` · **proved in source** +- Depends on: (A2), (A3), and UME62's own Lemma 7.1 ($a\prec b$ implies + $a^e\prec b^e$) and Corollary 2.1, both proved in source, [ext: the operator + entropy $h(a)=-a\log a$ is operator concave and satisfies $h(E[a|B])\ge + E[h(a)|B]$ for every von Neumann subalgebra $B$ — UME62 attributes this to + Nakamura–Umegaki and independently to Davis, generalising Segal], [ext: the + classical Kullback–Leibler inequality that the information of two probability + densities is non-negative, with the second-order mean-value expansion of + $-h(\lambda)$ — UME62 cites Kullback–Leibler] + ((A1) is standing in [UME62] and so carries no edge, though the whole + statement is written through its trace.) +- Proof route: (i) for bounded $b$, Proposition 4.1(ii) makes $I(a,b)$ + unambiguous; (ii) condition onto the **commutative** algebra generated by $b$ + and apply the operator Jensen inequality $a^e\log a^e\le(a\log a)^e$; + (iii) Lemma 7.1 propagates $a\prec b$ to $a^e\prec b^e$, so by Corollary 2.1 + the cross term conditions cleanly; + (iv) subtracting reduces the claim to $I(a^e,b)\ge0$, where the two + operators commute; (v) expand $-h(\lambda)$ to second order and use + $\tau(a)=\tau(b)=1$ to kill the linear part, leaving a manifestly non-negative + trace; (vi) for unbounded $b$, approximate monotonically inside the algebra + generated by $b$ and rule out two limits by a commutation argument. +- **The dependency order is the reverse of (R8)**: here non-negativity is + primitive and monotonicity ((R12)) is derived from it. + +### (R12) Monotonicity under a conditional expectation — **doubly conditional** + +Theorem 4: for $a,b\in\mathcal E$ **affiliated with the commutant $B'$**, +$I(a^e,b^e)\le I(a,b)$ where $e=E[\cdot|B]$. Theorem 4′: for normal states of +finite entropy **in the $B$-tracelet space $T_B$** — i.e. $\sigma(xy)=\sigma(yx)$ +for all $x\in A$, $y\in B$ — $I_B(\sigma,\rho)\le I(\sigma,\rho)$. + +- Source: [UME62] Theorems 4 and 4′, §7 · tier (b), `mineru-unchecked` · + **proved in source** +- Depends on: (R7'), (A2), (A3), (A10), and UME62's Corollary 2.1 and Lemma 7.2, + both proved in source +- Proof route: (i) expand $I(a,b)-I(a^e,b^e)$ using Corollary 2.1 to + replace $\tau(a^eX)$ by $\tau(aX)$; (ii) invoke Lemma 7.2 — this is + precisely where affiliation with $B'$ buys mutual commutation of the four + operators — to combine the three logarithms; (iii) recognise the result as + $I(a,\,b\,a^e(b^e+(I-q'))^{-1})$, whose second argument is positive with trace + 1 by (A3); (iv) apply (R7') to that residual pair, which needs (A2); + (v) for Theorem 4′, transport + $\sigma\prec\rho$ through the densities and identify $(d\sigma/d\tau)^e$ with + $d\sigma_B/d\tau_B$. +- **[UME62] does not prove data processing in the modern sense.** Neither + hypothesis appears in (R7)(iv); this is the note's sharpest correction to the + standard story that "Umegaki proved monotonicity". + +### (R13) Sufficiency of a subalgebra as the equality case + +Under a commutativity hypothesis on the density system, $B$ is sufficient for a +family $S$ of faithful normal states iff $I_B=I$ on every pair of $S$, iff +$J_B=J$ on every pair. + +- Source: [UME62] Theorem 5, §8 · tier (b), `mineru-unchecked` · **proved in + source**, importing a criterion from Umegaki's own part III +- Depends on: (R12), (A2), (A10), UME62's Lemma 7.2, and UME62's Theorem 2 — the + equality case $I(a,b)=0\iff a=b$ under $ab=ba$, proved in source, [ext: + Umegaki, *Conditional expectation in an operator algebra III* — a von Neumann + subalgebra $B$ is sufficient for a family $S$ of faithful normal states iff + $d(\sigma)d(\rho)^{-1}=d(\sigma)^ed(\rho)^{e-1}$ for every pair in $S$] +- Proof route: (i) replace sufficiency by that criterion; (ii) Lemma 7.2 makes + the four densities mutually commuting, which is what (A10) buys and what makes + (R12) applicable to the family $S\subset T_M\subset T_B$; (iii) **the + monotonicity defect is itself an information**, + $I(\sigma,\rho)-I_B(\sigma,\rho)=I(d(\sigma),\,d(\rho)d(\sigma)^ed(\rho)^{e-1})$, + which is defined because the pair lies in $\mathcal E$ by (A2); (iv) apply + Theorem 2 to that commuting pair; (v) symmetrise for the $J$ version. +- Step (iii) is the mathematically interesting content and is worth carrying + forward independently of the sufficiency statement. + +### (R14) The finite-dimensional reduction of the Araki form (Witten) + +For $\mathcal H_1\otimes\mathcal H_2$ with $\Psi$ cyclic separating, reduced density matrices +$\rho_1,\rho_2$, and a second vector $\Phi$ with reduced density matrices +$\sigma_1,\sigma_2$: $\Delta_{\Psi|\Phi}=\sigma_1\otimes\rho_2^{-1}$, hence +$\mathcal S_{\Psi|\Phi}=\mathrm{Tr}\,\rho_1(\log\rho_1-\log\sigma_1)$. + +- Source: [WIT18] §"Monotonicity of Relative Entropy In The Finite-Dimensional + Case" · tier (a) · **proved in source** +- Depends on: (A4), (A5), WIT18's earlier computation of $\Delta_{\Psi|\Phi}$, + and the conjugacy of the two reduced density matrices of a pure state +- Conventions: (C2), (C3) +- Proof route: (i) substitute WIT18's $\Delta_{\Psi|\Phi}=\sigma_1\otimes\rho_2^{-1}$, + available because (A4) makes $\Delta_{\Psi|\Phi}$ densely defined and (A5) + makes $\rho_2^{-1}$ an honest inverse, and split the logarithm; + (ii) trace out $\mathcal H_2$ in the first term; (iii) in the + second, use conjugacy to rewrite $\mathrm{Tr}_2\rho_2\log\rho_2$ as + $\mathrm{Tr}_1\rho_1\log\rho_1$; (iv) combine. +- Verbatim: + > We have arrived at the usual definition of the relative entropy in nonrelativistic quantum mechanics. + +### (R15) Non-negativity in the vector formulation, with a different equality case + +$\mathcal S_{\Psi|\Phi}(\mathcal U)\ge0$, vanishing **precisely when $\Phi=a'\Psi$ for a unitary +$a'$ in the commutant $\mathcal A_{\mathcal U}'$** — equivalently, when $\Phi$ and $\Psi$ induce +the same state on $\mathcal A_{\mathcal U}$. + +- Source: [WIT18] §"Relative Entropy In Quantum Field Theory" · tier (a) · + **proved in source**, for a general von Neumann algebra with a cyclic + separating vector +- Depends on: (A3), (A4), [ext: WIT18 credits the scalar-inequality argument to + Araki's 1976 paper] +- Conventions: (C3) — the step $\log\lambda\le\lambda-1$ is base-locked +- Proof route: (i) $\log\lambda\le\lambda-1$ lifts by functional calculus to + $-\log\Delta_{\Psi|\Phi}\ge1-\Delta_{\Psi|\Phi}$; (ii) take the expectation in + $\Psi$ and use the normalisation $\|\Psi\|=\|\Phi\|=1$ of (A3) to get $0$; + (iii) equality saturates + the scalar inequality only at $\lambda=1$, forcing + $\Delta_{\Psi|\Phi}\Psi=\Psi$; (iv) this equates all matrix elements of + $\mathcal A_{\mathcal U}$ in the two vectors; (v) cyclicity (A4) turns $a\Psi\mapsto a\Phi$ into + a unitary in the commutant. +- **Do not merge this equality case with (R8)(2)'s $\rho=\sigma$.** They agree: + $\Phi=a'\Psi$ is exactly the condition that the two vectors give the same state. +- Verbatim: + > An important elementary property is that $\gdef\S{{\mathcal S}}\gdef\U{{\mathcal U}}\S_{\Psi|\Phi}(\U)$ is always non-negative, and vanishes precisely if $\gdef\a{{\sf a}}\Phi=\a'\Psi$ + +### (R16) Monotonicity under shrinking the region + +If $\widetilde{\mathcal U}\subset\mathcal U$ then $\mathcal S_{\Psi|\Phi}(\mathcal U)\ge\mathcal S_{\Psi|\Phi}(\widetilde{\mathcal U})$, +reduced to the operator inequality +$\Delta_{\widetilde{\mathcal U}}\ge\Delta_{\mathcal U}$ in the resolvent sense. + +- Source: [WIT18] §"Monotonicity of Relative Entropy" and §"The Proof" · + tier (a) · **proved in source**, self-containedly +- Depends on: (A4), (A9), [ext: the projection onto the graph of a closed densely + defined operator $T$ is the explicit $2\times2$ matrix with entries built from + $(1+T^*T)^{-1}$ — WIT18 attributes the computation to Stone and to Halmos], + [ext: Borchers' argument that the modular operator increases as the region + shrinks, from the ordering of graph projections] +- Proof route: (i) write the graph projection explicitly; (ii) if $T_1$ extends + $T_0$ then the graphs nest and the projections are ordered; (iii) evaluate the + projections on vectors of the form $(\psi,0)$ to get + $\langle\psi,(1+T_0^*T_0)^{-1}\psi\rangle\le\langle\psi,(1+T_1^*T_1)^{-1}\psi\rangle$; + (iv) rescale $T_i\mapsto T_i/\sqrt s$ to get the resolvent statement at every + $s>0$; (v) take $T_0=S_{\widetilde{\mathcal U}}$, $T_1=S_{\mathcal U}$, legitimate because the + larger algebra has more vectors $a|\Psi\rangle$; (vi) operator monotonicity of + $\log$ (A9) from the resolvent integral representation; (vii) take the + expectation in $\Psi$ and flip the sign. +- Verbatim: + > The inequality (\ref{wonorf}) is a direct consequence of an operator inequality + +### (R17) The Araki form in Haagerup $L^1$ — the closest thing to a general reduction + +For $\varphi\in M_*^+$ faithful and $\omega=\varphi^h$ with $h\in M_{\mathrm{sa}}$, one has +$h_\omega=\exp(\log h_\varphi+h)$ in Haagerup's $L^1(M)$ and consequently +$D(\omega\|\varphi)=\mathrm{tr}(h_\omega(\log h_\omega-\log h_\varphi))$, with $\mathrm{tr}$ the +canonical trace on the crossed product. + +- Source: [HIA18], Appendix B closing remark · tier (a) · **sketched** — the key + identity is imported wholesale +- Depends on: HIA18's Proposition 5.3(3), that the relative entropy is the + $\alpha\to1$ limit of the Petz–Rényi divergences, and its Theorem B.2, both + proved in source, [ext: a Trotter-type product formula in the spatial + $L^p$-spaces], [ext: Terp's isomorphism between spatial and Haagerup + $L^p$-spaces], [ext: HIA18 cites Araki's 1973 relative-Hamiltonian paper for + uniqueness of the relative Hamiltonian], [ext: HIA18 cites Donald's theorem + that a finite infimum of $h(\rho)+D(\rho\|\varphi)$ over normal states is attained + at a unique normal state] +- **The source's own hedge and its own limitation**: HIA18 calls this a + "complete resemblance" to Umegaki's formula, and says explicitly that when the + relative Hamiltonian is unbounded above it is problematic whether the formulas + still make sense. The trace is on a crossed product, not on $M$. **This is not + a reduction theorem.** +- Verbatim: + > which has a complete resemblance to Umegaki's relative entropy in the semifinite case (see \eqref{F-1.1}). + +### (R18) The support condition as an $\varepsilon$-limit + +$D(\rho\|\sigma)=\lim_{\varepsilon\to0^+}\mathrm{Tr}\,[\rho(\log_2\rho-\log_2(\sigma+\varepsilon\mathbb1))]$, +hence $D(\rho\|\sigma)=\lim_{\varepsilon\to0^+}D(\rho\|\sigma+\varepsilon\mathbb1)$. + +- Source: [KW20] Proposition (`prop-rel_ent_lim`) · tier (a) · **proved in + source** +- Depends on: (A5) +- Conventions: (C3), (C5), (C6) +- Proof route: (i) $\sigma+\varepsilon\mathbb1$ has full support, so the + expression is finite for each $\varepsilon>0$; (ii) block-decompose $\rho$ and + $\sigma$ along $\operatorname{supp}\sigma\oplus\ker\sigma$; (iii) if supports nest, the + off-support blocks vanish and the limit is the finite value; (iv) otherwise the + $\log_2\varepsilon$ term diverges, giving $+\infty$. +- **This is what justifies the $+\infty$ branch as the right value rather than a + convention.** Its general-algebra analogue is (R9)(3), which is conditional. + +### (R19) Basic properties in finite dimensions + +Isometric invariance; Klein's inequality $D(\rho\|\sigma)\ge0$ **under +$\mathrm{Tr}\,\sigma\le1$**; faithfulness; monotonicity in $\sigma$; tensor additivity with +the rescaling corollary $D(\rho\|\beta\sigma)=D(\rho\|\sigma)+\log_2(1/\beta)$; +and the direct-sum property for classical–quantum states. + +- Source: [KW20] Proposition (`prop-rel_ent`) · tier (a) · **proved in source** +- Depends on: (R18), (A5) +- Conventions: (C3), (C4). **Note the shape of Klein's inequality here.** Because + the second argument need only be positive semi-definite, non-negativity is + conditional on $\mathrm{Tr}\,\sigma\le1$; (R8)(2) instead requires + $\rho(1)=\sigma(1)>0$; (R10)(h1) assumes both are states. **Three different + statements.** + +### (R20) Data processing in finite dimensions, and the properties derived from it + +$D(\rho\|\sigma)\ge D(\mathcal N(\rho)\|\mathcal N(\sigma))$ for every quantum +channel $\mathcal N$. Klein's inequality, isometric invariance and joint +convexity are then all corollaries. + +- Source: [KW20] Theorem (`thm-monotone_rel_ent`), via the Petz–Rényi route, and + the following Proposition · tier (a) · **proved in source**, with the operator + Jensen inequality and Stinespring's theorem also proved in the book +- Depends on: (R19), (A5), (A11), and — proved in [KW20] itself, not cited — + Stinespring's theorem, the operator Jensen inequality in the form + $f(V^*XV)\le V^*f(X)V$ for isometric $V$, and the convergence + $D_\alpha\to D$ as $\alpha\to1$, [ext: KW20 states the operator convexity + of $x\mapsto x^\beta$ on $[-1,0)\cup[1,2]$ and operator concavity on $(0,1]$ as + a numbered fact, deferring the proof to its bibliographic notes] +- Proof route: (i) Stinespring plus isometric invariance reduce a channel to a + partial trace; (ii) reduce to invertible operators by a double limit; + (iii) write the Petz–Rényi quantity as a vector expectation of + $f(\rho^{-1}\otimes\sigma^{T})$ with $f(x)=x^{1-\alpha}$; (iv) construct the + isometry $V$ carrying the smaller purification to the larger; (v) apply + operator Jensen in the form $f(V^*XV)\le V^*f(X)V$; (vi) compute $V^*(\cdot)V$ + to be the reduced object; (vii) translate through $\log$, flipping direction + where $\alpha<1$; (viii) take $\alpha\to1$. +- Verbatim: + > One of the remarkable aspects of the data-processing inequality for the qua\-ntum relative entropy is that it alone can be used to prove many of the properties of the quantum relative entropy stated in Proposition~\ref{prop-rel_ent}. + +### (R21) The operational meaning: quantum Stein's lemma + +For all states $\rho,\sigma$, the optimal achievable rate and the strong converse +rate for asymmetric quantum hypothesis testing both equal $D(\rho\|\sigma)$; in +the singular case both are $+\infty$. + +- Source: [KW20] Theorem (`thm-q_Stein_lemma`) · tier (a) · **proved in source** + (the achievability and strong-converse halves were not read through here, so no + proof route is filed) +- Depends on: (R18), (R20), (A5), [ext: Hiai–Petz 1991 — the relative entropy is + the optimal type-II error exponent in asymmetric hypothesis testing between + i.i.d. copies of two states], [ext: Ogawa–Nagaoka 2000 — the strong converse] +- **This is the answer to "why this quantity and not another."** +- Verbatim: + > For all states $\rho$ and $\sigma$, the optimal achievable and strong converse rates are equal to the quantum relative entropy of $\rho$ and $\sigma$ + +### Three incompatible orders of derivation + +Reading the corpus as one graph, the same properties are obtained three ways, and +none is the textbook order: + +- **[UME62]**: operator concavity of $-a\log a$ → non-negativity (R7') → the + equality case → *conditional* monotonicity (R12) → sufficiency (R13). +- **[HIA18]**: the Lesniewski–Ruskai integral representation → the variational + expression (R6) → monotonicity, joint convexity and joint lower semicontinuity + all at once (R7) → non-negativity (R8). +- **[WIT18] / [KW20]**: an operator-monotonicity fact → monotonicity + (R16)/(R20) → non-negativity, isometric invariance, joint convexity, + subadditivity and strong subadditivity. + +In four of the six sources **monotonicity is primitive and non-negativity is its +corollary.** Only [UME62] goes the other way, and its monotonicity is the weak, +hypothesis-laden version. + +## Hypotheses + +| (A#) | Statement | Class | Evidence | Witness | Scope | Tier | Used by | +|---|---|---|---|---|---|---|---| +| (A1) | $M$ carries a faithful normal (semi)finite trace | model-dependent | — | the local algebra $\mathcal A_{\mathcal U}$ of a wedge region: [WIT18] states QFT local algebras are type III and "do not have a trace -- even one defined only on part of the algebra" | standing in [UME62]; **absent from the adopted form** | a | (R7'), (R11), (R12), (R13); (D1), (D2), (D8) | +| (A2) | finite entropy $\|H(a)\|<\infty$ ([UME62]'s class $\mathcal E$) | model-dependent | — | on $A=L^\infty([0,1],dx)$ with $\tau=\int dx$ — inside UME62's own setting — the density $C/(x\log^2 x)$ on $(0,\tfrac12)$ is integrable while $\int f\log f$ diverges, so $H(f)=-\infty$ | local to [UME62] | b | (R7'), (R11), (R12), (R13) | +| (A3) | both arguments normalised (equal mass, in the general form) | provable | [HS17] extends $H$ off normalised states by the explicit two-parameter rule, so the unnormalised value is *determined*, not assumed; [HIA18] defines $S_f$ on all of $M_*^+$; [KW20] says restricting the first argument to a state is expository | — | local | a | (R7'), (R8)(2), (R11), (R12), (R15); **and it is load-bearing inside them** — see the dropped-normalisation degeneracy row | +| (A4) | $\Psi$ cyclic and separating for the algebra | model-dependent | — | any Schmidt-rank-one product vector, e.g. $\|1\rangle\otimes\|1\rangle$ in $\mathbb C^2\otimes\mathbb C^2$: [WIT18] states $\Psi$ is cyclic and separating iff all Schmidt coefficients are nonzero | local to [WIT18]'s (D6) | a | (R14), (R15), (R16) | +| (A5) | finite dimension | model-dependent | — | every infinite-dimensional system; in particular every QFT local algebra | standing in [KW20], implicit in [VED02] | a | (R14), (R18), (R19), (R20), (R21) | +| (A6) | $M$ is presented in a standard form | **provable** | [HIA18] Appendix A **constructs** one unconditionally from an arbitrary faithful normal semifinite weight: `Then $(M,L^2(M),J=\,^*,L^2(M)_+)$ becomes a standard form of $M$` — existence is tier (a) *inside the corpus*; only uniqueness up to unitary equivalence remains cited to Haagerup | — | standing throughout the adopted form | a | (R1)–(R9), and the adopted form itself | +| (A7) | $f$ is operator convex | **provable** for the two functions this object needs | $t\log t=\int_0^\infty\bigl(\tfrac{t}{1+s}-\tfrac{t}{t+s}\bigr)ds$; writing the integrand as $\tfrac{t}{1+s}-1+s(t+s)^{-1}$ exhibits it as an affine function plus a positive multiple of the operator convex $t\mapsto(t+s)^{-1}$, and operator convexity survives positive combinations and pointwise limits. Independently, [KW20] states it outright: `The function $x\mapsto x\log_b(x)$, for every base $b>0$ and $x\in[0,\infty)$, is operator convex` | — | standing in [HIA18] §§3–4 | a | (R6), (R7), (R8), (R9) | +| (A8) | a non-affine operator convex function on $(0,\infty)$ is strictly convex | **provable** | from the integral representation of (R6): $\frac{d^2}{dt^2}\frac{(t-1)^2}{t+s}=\frac{2(1+s)^2}{(t+s)^3}>0$, so $f''(t)\ge 2c+\int\frac{2(1+s)^2}{(t+s)^3}d\mu(s)>0$ unless $c=0$ and $\mu=0$, i.e. unless $f$ is affine | — | local to [HIA18] Cor. 4.2 | b | (R8) | +| (A9) | $\log$ is operator monotone | provable | the resolvent integral representation $\log R=\int_0^\infty(\tfrac1{s+1}-\tfrac1{s+R})ds$, which [WIT18] derives in source | — | local | a | (R16) | +| (A10) | [UME62]'s operators are affiliated with $B'$ / its states lie in the tracelet space $T_B$ | model-dependent | — | any pair of normal states of a non-abelian $A$ failing $\sigma(xy)=\sigma(yx)$ for some $x\in A$, $y\in B$; $T_B$ is automatic only when $B$ lies in the centre | local to [UME62] §§7–8 | b | (R12), (R13) | +| (A11) | $\Phi$ is **unital** | model-dependent | — | $\Phi:\mathbb C\to M_2(\mathbb C)$, $\Phi(\lambda)=\lambda p$ with $p=\mathrm{diag}(0,1)$: CP, normal, sub-unital and Schwarz with equality, yet $D(\rho\circ\Phi\|\sigma\circ\Phi)=\tfrac12\log 5>\tfrac12\log(25/9)=D(\rho\|\sigma)$ for $\rho=\mathrm{diag}(\tfrac12,\tfrac12)$, $\sigma=\mathrm{diag}(\tfrac9{10},\tfrac1{10})$ | local to (R7)(iv) | b | (R7)(iv), (R20) | +| (A12) | $M$ is σ-finite | model-dependent | — | $\mathcal B(\mathcal H)$ for $\mathcal H=\ell^2(I)$ with $I$ uncountable: an uncountable orthogonal family of rank-one projections cannot all receive positive value under a normal state | standing in [HS17]; in [HIA18] a **proof convenience only** — it says σ-finiteness lets one "sometimes reduce arguments", offered after the theorems are proved without it | a | (R10) only | +| (A13) | Hilbert spaces are separable | model-dependent | — | same as (A12) | standing in [HS17] — and **used by no result about this object** | a | **none** | +| (A14) | both states faithful | model-dependent as stated, **removable** | [HIA18] §2.1 supplies the modification [HS17] defers to Ohya–Petz: extending the domain from $M\xi_\sigma$ to $M\xi_\sigma\oplus(1-s_{M'}(\sigma))\mathcal H$, dense for every $\sigma$, replaces cyclicity; inserting $s_M(\sigma)$ into the value replaces separatingness, since $x\xi_\sigma=0$ forces $s_M(\sigma)x^*=0$ | $\rho=\mathrm{diag}(1,0)$ on $M_2(\mathbb C)$ | local to [HS17]'s (D7) | a | (D7) only | +| (A15) | normality of the arguments | provable that it is not a hypothesis | [HS17] admits non-normal functionals and assigns $+\infty$; [HIA18] builds normality into the ambient $M_*^+$, a typing decision | — | — | a | none | + +**(A13) is a standing hypothesis that no result about this object consumes.** +[HIA18] proves the same properties, in greater generality, with no separability +anywhere. It is presumably consumed elsewhere in [HS17] (nuclearity, modular +estimates), which is outside this object. + +Checked and found **not** to be hypotheses of this object: statistical +independence of two subalgebras and nuclearity (both attach to [HS17]'s +entanglement measure $E_R$, not to $H$); hyperfiniteness (searched all six +caches); commutativity $\rho\sigma=\sigma\rho$ (a hypothesis of a +well-definedness lemma only, superseded exactly as (A2) is); the type of the +algebra, which is a hypothesis of the *trace* row (A1) and of nothing else. + +## Degeneracies and boundary cases + +| Case | Effect on the adopted form | Tier | +|---|---|---| +| intended case is nonvacuous | **Holds, with a construction.** (R17)'s underlying identity gives $D(\rho\|\omega)=-h(\rho)+D(\rho\|\varphi)$ for $\omega=\varphi^h$ on an **arbitrary** von Neumann algebra; at $\rho=\omega$ this reads $D(\omega\|\varphi)=\omega(h)$, which [HIA18] uses outright. Instantiate on a QFT local algebra $\mathcal A(O)$ with $\varphi$ the vacuum — faithful, the vacuum being cyclic and separating, which [HS17] states at theorem strength for Lechner's integrable models along with "each $\mathcal A(O)$ is of type III$_1$" — and $h$ any nonzero positive element of $M_{\mathrm{sa}}$: then $D(\omega\|\varphi)=\omega(h)\in(0,\infty)$, **finite and strictly non-zero on a type III₁ algebra with no trace anywhere**. Corroborated non-constructively by [HS17]'s $E_R(\omega_0)>0$ together with its finite upper bound for massive BW-nuclear theories. The scaling pair $\rho=k\sigma$ is *not* an acceptable witness here: [HIA18]'s own computation shows it sees only the point spectral measure at $k$, so it never probes the relative modular operator and holds on every algebra | b | +| zero object / scalars | No effect on definedness; the values are forced. $D(0\|\sigma)=0$, $D(\rho\|0)=+\infty$ for $\rho\ne0$, $D(0\|0)=0$, $D(\sigma\|\sigma)=0$. On $M=\mathbb C$: $D(p\|q)=p\log(p/q)$, hence $0$ on states — degenerate only because $\mathbb C$ has one state, and this case is *load-bearing*, since (R8) feeds it in as the subalgebra $\mathbb C1$. On $M=\{0\}$ the only pair is $(0,0)$ | a | +| finite-dimensional | No effect on definedness; the modular form **reproduces** the trace form (R3), (R14). But "finite-dimensional ⇒ finite value" is **false**: dimension 2 already gives $+\infty$ | a | +| commutative | Reduces to the classical $f$-divergence, hence to Kullback–Leibler — **proved** in the corpus (R4). The sanity check the object exists to pass | a | +| non-separable / non-σ-finite | **No effect.** [HIA18]'s Example 2.6 is stated for "an arbitrary Hilbert space", and σ-finiteness appears only as a proof convenience (A12). [UME62] needs σ-finiteness for its $L^1(A)$ machinery and disclaims even that | a | +| type III | **Decisive.** No trace, so (D1)/(D2)/(D8) have no referent: no $\tau$, no $d_\rho$, no $\mathrm{Tr}\,$-density. The adopted form is unaffected, because the standard form exists for every von Neumann algebra (A6). [HS17] states the obstruction from the other side: generalising the *von Neumann entropy* to arbitrary type is hard, while the *relative* entropy generalises | a | +| non-unital / degenerate representation | **Non-unital algebra**: outside the class — a von Neumann algebra is unital, and no source contemplates otherwise. **Non-unital map**: fatal, see (A11). **Degenerate/non-standard representation**: no effect on the adopted form, since (R2)(1) makes it a function of the algebra and the functionals only; but fatal to (D6)/(D7), which are stated for a *given* representation and a *given* vector | a | +| universally orthogonal index element ($\operatorname{supp}\rho\perp\operatorname{supp}\sigma$) | Value $+\infty$, and this is the *correct* value, not a convention (R18). Named separating pair, computed from [WIT18]'s own finite-dimensional formula: $\mathcal H=\mathbb C^2\otimes\mathbb C^2$, $\Psi=\tfrac1{\sqrt2}(\|1,1\rangle+\|2,2\rangle)$, $\Phi=\|1,1\rangle$, i.e. $\rho_1=\mathrm{diag}(\tfrac12,\tfrac12)$, $\sigma_1=\mathrm{diag}(1,0)$. This is the pair that kills (X1) | a | +| quantifier swap: $\forall\Phi$ ↦ $\exists\Phi$ in monotonicity | **Destroys the statement.** $\Phi=\mathrm{id}$ satisfies the existential with equality for *every* two-argument functional, so the $\exists$ form carries no information | b | +| quantifier swap: $\sup_n\sup_{x(\cdot)}$ in (R6) | No-op — two suprema over independent index sets commute. Nor does $\sup_n$ differ from $\lim_n$, the net being increasing by [HIA18] Lemma 3.2. A third, non-trivial invariance: the inner quantifier may range over $L$-valued step functions for **any** subspace $L\ni1$ dense in $M$ in the strong\* topology, which is what powers the martingale-convergence proof | a | +| hypothesis dropped: (A3) normalisation | **Non-negativity fails**, and this is the cleanest degeneracy in the note: $D(\rho\|2\rho)=-\log 2<0$. Confirmed four independent ways — [HIA18]'s Peierls–Bogoliubov equality case, [HS17]'s two-parameter rescaling rule, [KW20]'s $D(\rho\|\beta\sigma)=D(\rho\|\sigma)+\log_2(1/\beta)$, and [WIT18]'s positivity proof, whose final step is exactly $\|\Psi\|^2-\|\Phi\|^2=0$. Everything else — monotonicity, joint convexity, lower semicontinuity, additivity — survives; only the *sign* is lost | a | +| hypothesis dropped: (A11) unitality of $\Phi$ | **Data processing fails**, with the named counterexample in (A11). The mechanism: the predual of a sub-unital map is trace-*decreasing*, and $D$ is not jointly scale-invariant, so unequal shrinkage of the two arguments can increase the value | b | +| hypothesis dropped: support condition | Nothing is dropped from the adopted form — it is total and returns $+\infty$. Under (D1) the quantity becomes **undefined**, not $+\infty$; that total-versus-partial difference is (D1)'s only structural departure | a | +| hypothesis dropped: (A14) faithfulness | Splits asymmetrically. Second argument: faithfulness is what makes $S_{\omega,\omega'}$ well defined in (D7), and dropping it needs (A14)'s modification. First argument: not needed at all — $\rho=\mathrm{diag}(1,0)$, $\sigma=\mathrm{diag}(\tfrac12,\tfrac12)$ gives $\log 2$, one bit | a | +| hypothesis dropped: (A4) cyclicity of $\Psi$ | **Definedness** fails for (D6), not merely the value: [WIT18] says $S_{\Psi\|\Phi}$ makes sense as a densely defined operator only then. The adopted form needs nothing of the sort | a | +| hypothesis dropped: (A2) finite entropy | No effect on the adopted form, which never splits $\tau(a\log a)-\tau(a\log b)$ into two separately-finite terms. It is an artefact of (D1)'s difference-of-two-traces presentation | b | +| hypothesis dropped: (A7) operator convexity, keeping convexity | The *definition* survives — [HIA18] Definition 2.1 needs only convexity, and (R1) still gives well-definedness. What fails is (R6) and hence everything after it. The definition and the main theorem genuinely have different hypotheses | a | + +## Rejected formulations and refuted claims + +**Append-only.** + +| id | Candidate or claim | Disposition | Discriminator | Tier | Date | +|---|---|---|---|---|---| +| (X1) | $\mathrm{Tr}\,\rho(\log\rho-\log^+\sigma)$ with $\log^+0:=0$ — the totalised no-branch formula (Petz's quasi-entropy at $k=1$) | rejected | **(X4) source disagreement** — [HIA18] Remark 2.7 states the candidate and calls it "improper as a standard $f$-divergence" (under its hypotheses $M=\mathcal B(\mathcal H)$, $\dim\mathcal H<\infty$, $f(0^+)<\infty$); **and (X2) degeneracy** — on $\rho=\mathrm{diag}(\tfrac12,\tfrac12)$, $\sigma=\mathrm{diag}(1,0)$ it returns $-\log2<0$ for two genuine states | a | 2026-08-16 | +| (X2) | $-S(\rho)-\mathrm{Tr}\,\rho\log\sigma$, splitting into two separately-defined terms | rejected | **(X2) degeneracy** — the split is $\infty-\infty$ already in type I, at any normal state of $\mathcal B(\mathcal H)$ with infinite von Neumann entropy, whereas the difference is defined. No type III algebra is needed; [HS17] frames the contrast as one of *definability*, not generality. [UME62] itself performs the split only under "the entropy $H(a)$ is finite and $b$ is bounded" | b | 2026-08-16 | +| (X3) | density matrices on $\mathcal B(\mathcal H)$ / finite dimensions as **the** definition | rejected | **(X3) generality loss** — witness the **hyperfinite type II₁ factor**, which [WIT18] constructs: a trace exists there, but $\mathrm{Tr}\,$-densities do not, so the formula has no referent while the adopted form does. [KW20] scopes itself explicitly to finite dimensions | a | 2026-08-16 | +| (X4) | the trace formula (D1)/(D2) taken as the **general** definition | rejected | **(X3) generality loss** — witness the local algebra $\mathcal A_{\mathcal U}$ of a wedge region, type III with no trace even on part of the algebra. **Not** a rejection of (D1)/(D2) in their own semifinite scope, where they are correct and agree with the adopted form on $\mathcal B(\mathcal H)$ (R3) | a | 2026-08-16 | +| (X5) | requiring both states faithful ([HS17]) or the first vector cyclic separating ([WIT18]) as the definition's domain | rejected as a *domain choice*; both remain variants under `## Definition` | **(X3) generality loss** — named pair $\rho=\mathrm{diag}(1,0)$, $\sigma=\mathrm{diag}(\tfrac12,\tfrac12)$ on $M_2(\mathbb C)$, relative entropy $\log 2$, $\rho$ not faithful; neither (D6) nor (D7) reaches it, and both sources concede the gap. Note the asymmetry: [WIT18] constrains only the first argument, [HS17] both | a | 2026-08-16 | +| (X6) | *claim*: [UME62] introduced the "relative entropy" | refuted **as a claim about the name**; upheld as a claim about the object | `grep -F "relative entropy"` returns **zero hits** in all three independent extractions of [UME62] (MinerU `source.txt`, `source.flat.txt`, and the pypdf PDF text layer); all occurrences of "relative" are the ordinary English adverbial. The paper's object is *information* $I(\cdot,\cdot)$; *entropy* is the one-argument $H(a)$. [VED02] says as much in the corpus: "this quantity was first considered by Umegaki (1962), but for consistency reasons I name it after von Neumann" | b | 2026-08-16 | +| (X7) | *claim*: [HIA18] misdescribes [UME62]'s hypothesis by attributing the definition to a **semifinite** algebra | refuted | [UME62]'s standing hypothesis is indeed finite class + σ-finite, so HIA18's sentence is not a transcription — but §1 asserts the semifinite extension of every theorem in the paper, and **footnote 5) extends the definition itself** to the semifinite case. Of the four semi-trace footnotes, three support the extension and only footnote 4) carves anything out, and that concerns the operator-entropy of $L^p$ elements, not $I(\cdot,\cdot)$. HIA18's sentence is a **defensible generalisation of a claim the source makes about itself**; the residual gap is *proof*, not scope, since UME62's extension is asserted ("can be shown by a little or simply modified proofs") with an unverified side condition on subalgebras | b | 2026-08-16 | +| (X8) | the regularised form $\lim_{\varepsilon\to0^+}D(\rho\|\sigma+\varepsilon\mathbb1)$ | equivalent | — ([KW20] proves it, (R18)) | a | 2026-08-16 | +| (X9) | monotonicity for sub-unital (non-unital) Schwarz maps | rejected | **(X1) separating object** — $\Phi:\mathbb C\to M_2(\mathbb C)$, $\Phi(\lambda)=\lambda\,\mathrm{diag}(0,1)$, with $\rho=\mathrm{diag}(\tfrac12,\tfrac12)$ and $\sigma=\mathrm{diag}(\tfrac9{10},\tfrac1{10})$: $D(\rho\circ\Phi\|\sigma\circ\Phi)=\tfrac12\log5\approx0.805>\tfrac12\log(25/9)\approx0.511=D(\rho\|\sigma)$. The statement is **false**, not open | b | 2026-08-16 | +| (X10) | base 2 versus natural logarithm | preference-only — no discriminator found | — | a | 2026-08-16 | +| (X11) | the symmetrised divergence $J(\sigma,\rho)=I(\sigma,\rho)+I(\rho,\sigma)$ | **not a candidate reading of this object** — [UME62] gives it a separate Definition 2 on a stricter domain ($a\sim b$) and it is symmetric | — | b | 2026-08-16 | + +## Prior art + +| System | Found | Relation to variants | How searched | Measured at | +|---|---|---|---|---| +| Mathlib | `InformationTheory.klDiv` on measures via `Measure.rnDeriv`/`llr`, valued in `ℝ≥0∞`, `⊤` unless absolutely continuous **and** the log-likelihood ratio is integrable, with a correction term for non-probability measures | the classical special case; shares the `+∞` convention of (D2)/(D8) and is incompatible with (D5); carries a *second* `⊤` branch (non-integrability) that no quantum variant has | directory listing, file read, two `lean_leansearch` queries | mathlib rev `5450b53e` | +| Mathlib | `InformationTheory.klFun x = x*log x + 1 - x`, `strictConvexOn_klFun`, `klDiv_eq_integral_klFun` | the commutative shadow of (D4) at $f(t)=t\log t$, affinely normalised so $f(1)=f'(1)=0$; a single instance, not a general $f$-divergence | file read | mathlib rev `5450b53e` | +| Mathlib | `Real.negMulLog`, `strictConcaveOn_negMulLog`, `strictConvexOn_mul_log`, `deriv2_mul_log`; `Real.binEntropy`, `Real.qaryEntropy` | the scalar backbone of every variant, real-variable only | grep, `lean_leanfinder` | mathlib rev `5450b53e` | +| Mathlib | `Analysis/InnerProductSpace/StandardSubspace.lean` — `StandardSubspace`, `IsCyclic`, `IsSeparating`, with a file TODO reading "Define the Tomita conjugation, prove Tomita's theorem, prove the KMS condition" | a precursor to the apparatus of (D3)/(D6)/(D7); the modular operator itself is absent | `grep "Tomita\|Takesaki\|Araki\|Umegaki"` — the only match in all of Mathlib | mathlib rev `5450b53e` | +| Mathlib | `CFC.log a := cfc Real.log a` with `log_exp`, `exp_log`, `log_pow` | the operator logarithm exists at C\*-generality. **The singular case is the point**: `cfc` returns a junk value when the function is not continuous on the spectrum, and `Real.log 0 = 0`, so `CFC.log` coincides with the support-restricted logarithm (D1)'s convention intends exactly when $0$ is *isolated* in the spectrum — always so in finite dimensions — and is junk when $0$ is non-isolated | read `ExpLog/Basic.lean` and the `cfc` definition | mathlib rev `5450b53e` | +| Mathlib | `CFC.log_monotoneOn` on strictly positive elements — (A9). Operator **concavity** of log appears only as a file TODO | (A9) present; (A7) not found | grep, file reads | mathlib rev `5450b53e` | +| Mathlib | `WStarAlgebra` (predual merely exists), `VonNeumannAlgebra H` with `commutant_commutant` — one file | the ambient object of (D1)–(D4) exists as a definition; could not find normal states, traces, $L^1(A)$, or the type classification, having searched `grep -rni "semifinite\|faithful normal\|normal state"` over Mathlib | grep, file read | mathlib rev `5450b53e` | +| Mathlib | `instLoewnerPartialOrder`, `Matrix.PosSemidef`/`PosDef`, `Matrix.trace`; positive linear maps `A →ₚ[ℂ] A₂`, `PositiveLinearMap.PreGNS`, completely positive maps `A₁ →CP A₂` | the order in which (C6) and the corpus's operator inequalities are stated, and the finite-dimensional data of (D8). Could not find a support-projection notion, a bundled `State`, or normality, having searched `grep -rn "IsState\|StateSpace"` and `lean_leansearch "state on a C-star algebra positive linear functional norm one"` | grep, `lean_leansearch` | mathlib rev `5450b53e` | +| Mathlib | could not find: any quantum or von Neumann relative entropy; von Neumann entropy; Shannon entropy of a distribution; Rényi/Hellinger/Pinsker/hypothesis testing; a general $f$-divergence; trace-class or Hilbert–Schmidt operators; non-commutative $L^p$, Haagerup $L^1$, standard form, or a Radon–Nikodym derivative for states; the modular or relative modular operator; support projections; Löwner's theorem; the operator Jensen inequality; Lieb concavity; Lieb–Ruskai/Uhlmann joint convexity; Kosaki's variational expression; Stinespring dilation; density matrices, channels or POVMs; an operator $x\log x$ | — | ~20 greps plus five `lean_leansearch`/`lean_leanfinder` queries, all recorded in the lane notes | mathlib rev `5450b53e` | +| this repository | `Matrix.relativeEntropy (ρ σ : DensityMatrix n) : EReal`, `QuantumSystem/Analysis/Entropy/RelativeEntropy.lean`, with the docstring citing Umegaki 1962; surrounding `relativeEntropy_nonneg`, `_eq_zero_iff`, `_channel_le`, `_channel_eq_iff_recoverable`, `_jointly_convex`, and `Analysis/Matrix/{LiebConcavity,Effros,Pinching}` | closest to **(D8)**, with two stated differences: the log base is $e$, not 2; and the second argument is a density matrix, not merely positive semi-definite. No regularised form (R18). Nothing for (D1)–(D4), (D6), (D7), (D9). **The `implemented-as` field of this note is `none` only because that field belongs to `math-review`, not to this skill; a declaration for the finite-dimensional case exists** | repository grep, file read | 2026-08-16 | +| Physlib (`leanprover-community/physlib`) | `QuantumInfo/Entropy/Relative.lean`: `qRelativeEnt (ρ σ : MState d) : ENNReal`, docstring "Also called the Umegaki quantum relative entropy", defined as the $\alpha=1$ case of a sandwiched Rényi family, natural log, `⊤` when supports fail to nest. Around it: `qRelativeEnt_joint_convexity`, `_additive`, `lowerSemicontinuous`, `Entropy/DPI.lean`, `Entropy/SSA.lean`, `Channels/Pinching.lean`, `ResourceTheory/SteinsLemma.lean`, and `TraceInequality/{LownerHeinzTheorem,JensenOperatorInequality,LiebAndoTrace,OperatorGeometricMean}` | **direct prior art**, (D8)-shaped and finite-dimensional. Its variational formula is for the *sandwiched Rényi* quantity, not Kosaki's (D9) for the Umegaki form. Covers, finite-dimensionally, the external edges Löwner–Heinz, operator Jensen, Lieb concavity, joint convexity, pinching, support projections | GitHub trees API, raw file reads | 2026-08-16 | +| Physlib | `PhyslibAlpha/QuantumMechanics/StinespringDilation.lean` — Kraus form and `QuantumChannel`, over a general ring; its own TODO notes a second, different Stinespring elsewhere in the repository | supplies (R20)'s Stinespring edge, finite-dimensional | trees API, raw file read | 2026-08-16 | +| Rocq — `infotheo` | `probability/divergence.v`: `div = \sum_(a in A) P a * log (P a / Q a)`, base 2, with `div_ge0`, `div0P` | the classical case on finite distributions; base 2 as in (D8), but **real-valued with no `+∞`** — dominance is a lemma hypothesis, not a case split. Could not find a quantum relative entropy in the library, having listed all 87 `.v` files and read the divergence file | trees API, raw reads | 2026-08-16 | +| Rocq — CoqQ | density matrices, quantum Hoare logic, `majorization.v`; the only entropy hits are `entropy_majority`/`majority_entropy_le` on real vectors | could not find a quantum relative entropy | trees API, greps of the three likeliest files | 2026-08-16 | +| Isabelle/HOL main library | `HOL-Probability.Information`: `KL_divergence b M N` via `entropy_density` and `RN_deriv`, parametric base | the classical case; **real-valued with no `+∞`**, absolute continuity as a lemma hypothesis | WebFetch of the library page | 2026-08-16 | +| Isabelle AFP | only *Source Coding Theorem* under probability theory; *Isabelle Marries Dirac* is matrix quantum computation. Could not find a quantum relative entropy | — | topic index page plus two web searches; **the AFP full-text search endpoint returned only the search form**, so this is not a full-text sweep | 2026-08-16 | +| Lean Zulip | **unreliable miss** — the domain-restricted search returned no pages from those domains at all, which indicts the search channel, not the archive | — | `WebSearch` with `allowed_domains` | 2026-08-16 | + +## Open questions + +- **Does the adopted form agree with (D1)/(D2) on a general semifinite von + Neumann algebra?** Nothing in this corpus proves it. Proved: agreement on + $\mathcal B(\mathcal H)$ (R3) and in finite dimensions (R14). Nearest general statement: (R17), + restricted to bounded relative Hamiltonians, written with a crossed-product + trace, and hedged by its own source. This sits exactly at the object's + definition and is the note's principal limitation. +- Is [UME62]'s asserted semifinite extension (X7) actually valid, including its + unverified side condition that $\tau$ restrict to a semi-trace on every + subalgebra used? +- Is [UME62]'s $I(a,b)=0\Rightarrow a=b$ true without the commutativity + hypothesis its Theorem 2 carries? (R8)(2) settles the *mathematics* — it is — + so this is a proof-technique gap in the 1962 paper, not a truth gap. +- Do (R10)(h4) and (h7) have a source other than Ohya–Petz? Both were verified in + finite dimensions during this extraction; neither is proved anywhere in the + corpus. +- Is [UME62]'s footnote 7) polarity as read here? MinerU dropped all four + footnotes, so the PDF text layer is the sole witness, its OCR is damaged, and + no page-image check was possible in this container. The reading adopted is + "**is** satisfied"; an earlier reading of the same line said "is not + necessarily satisfied". Both come from the same OCR. + +## Sources + +**Append-only.** + +| Key | Work | Status | Cache | Version | Tier reached | Retrieved | +|---|---|---|---|---|---|---| +| UME62 | H. Umegaki, *Conditional expectation in an operator algebra, IV (entropy and information)*, Kodai Math. Sem. Rep. **14** (1962) 59–85 | retrieved (converted) | `references/umegaki-1962-cond-exp-iv/` | published | b | 2026-08-16 | +| HIA18 | F. Hiai, *Quantum $f$-divergences in von Neumann algebras I. Standard $f$-divergences*, arXiv:1805.02050; J. Math. Phys. **59** (2018) 102202 | retrieved | `references/arxiv-1805.02050/` | arXiv | a | 2026-08-16 | +| VED02 | V. Vedral, *The role of relative entropy in quantum information theory*, arXiv:quant-ph/0102094; Rev. Mod. Phys. **74** (2002) 197 | retrieved | `references/arxiv-quant-ph-0102094/` | arXiv | a | 2026-08-16 | +| WIT18 | E. Witten, *Notes on some entanglement properties of quantum field theory*, arXiv:1803.04993 | retrieved | `references/arxiv-1803.04993/` | arXiv | a | 2026-08-16 | +| HS17 | S. Hollands, K. Sanders, *Entanglement measures and their properties in quantum field theory*, arXiv:1702.04924 | retrieved | `references/arxiv-1702.04924/` | arXiv | a | 2026-08-16 | +| KW20 | S. Khatri, M. M. Wilde, *Principles of Quantum Communication Theory: A Modern Approach*, arXiv:2011.04672 | retrieved | `references/arxiv-2011.04672/` | arXiv | a | 2026-08-16 | +| ARA76 | H. Araki, *Relative entropy of states of von Neumann algebras*, Publ. RIMS **11** (1976) 809–833 | not retrieved — tried Project Euclid's download endpoint (returns HTML), its `.full` article page (404), `kurims.kyoto-u.ac.jp/~prims/pdf/11-3/*.pdf` (404), the `ems.press` volume index (404) | — | — | c | 2026-08-16 | +| ARA77 | H. Araki, *Relative entropy for states of von Neumann algebras II*, Publ. RIMS **13** (1977) 173–192 | not retrieved — same routes as ARA76 | — | — | c | 2026-08-16 | +| KOS86 | H. Kosaki, *Relative entropy of states: a variational expression*, J. Operator Theory **16** (1986) 335–348 | not sought | — | — | c | 2026-08-16 | +| PET88 | D. Petz, *A variational expression for the relative entropy*, Comm. Math. Phys. **114** (1988) 345–349 | not sought | — | — | c | 2026-08-16 | +| OP93 | M. Ohya, D. Petz, *Quantum entropy and its use*, Springer 1993 | not sought | — | — | c | 2026-08-16 | +| LR99 | A. Lesniewski, M. B. Ruskai, *Monotone Riemannian metrics and relative entropy on noncommutative probability spaces*, J. Math. Phys. **40** (1999) 5702–5724 | not sought | — | — | c | 2026-08-16 | +| HAA75 | U. Haagerup, *The standard form of von Neumann algebras*, Math. Scand. **37** (1975) 271–283 | not sought | — | — | c | 2026-08-16 | + +## Not investigated + +- **The `[ext]` coverage gap.** These external edges appear in `## Results` and + were **not** checked against any library or formalization: the strict-convexity + fact (A8); the Lesniewski–Ruskai integral representation under (R6); Petz's + variational expression (R16); the tensor product of two finite-class von + Neumann algebras under (R11); Umegaki's own part III sufficiency criterion + under (R13); Borchers' modular-operator ordering and the Stone/Halmos graph + projection under (R16); Donald's and Araki's relative-Hamiltonian results under + (R17); the classical Kullback–Leibler inputs of (R7'); the Peierls–Bogoliubov + inequality as [VED02] imports it; the polar-decomposition lemma [VED02] takes + from Reed–Simon; and — named as unchecked by the prior-art lane itself — the + operator Schwarz inequality, the Gibbs variational principle, Klein's + inequality, Petz recovery beyond this repository's own lemma, Araki's + Radon–Nikodym cocycle, and the Connes cocycle of (D7). +- **Araki's own papers were never opened.** Every claim in this note about what + Araki assumed, defined or proved is a claim about how [HIA18], [HS17] and + [WIT18] *report* him. In particular, whether Araki's original definition + assumed faithfulness is not settled here: [HS17] attributes the faithful + version to him and [HIA18] attributes the total version to the same two papers. +- **No page-image check of [UME62] was possible.** `pdftoppm`, `pdftotext`, + `mutool` and `gs` are all absent from this container, so the UME62 rows rest on + a MinerU conversion cross-checked against the PDF's own OCR text layer — two + extractions, no image. MinerU dropped the paper's footnotes entirely, so every + footnote claim (including (X7)'s footnote 5) and the open question about + footnote 7) rests on the text layer alone, uncross-checked. +- **[KW20] was searched, not read.** 4.3 million characters were grepped and + jumped; its later chapters were not swept for further statements about this + object. Likewise unread: [HIA18]'s Lemmas 3.1–3.4 and §5 Rényi proofs, its + Appendix A/B derivations beyond the statements quoted, [WIT18]'s + $\Delta_D\ge\Delta_N$ section, and the achievability and strong-converse halves + of (R21). +- **Variants sighted and not pursued**: [UME62]'s $I(\cdot;B)$ relative to a + subalgebra; [HIA18]'s Rényi and max-relative entropies and the $\alpha\to1$ + characterisation (R10's neighbour), where a further definitional variant could + hide; [VED02]'s measured/asymptotic characterisation + $S(\sigma\|\rho)=\lim_N S_N$, which is arguably a definition in its own right; + the Haagerup $L^p$ route of (R17) as a source of further variants. +- **Prior art not swept**: Physlib's spectral-theory and unbounded-operator + files were listed but not opened; the AFP was not full-text searched; Mizar, + HOL Light, `QuantumLib`/SQIR and `mathcomp-analysis` were not searched at all; + the Lean Zulip result is an unreliable miss. +- **The unexamined base.** Everything above rests on a tier-(c) floor: the + closability of $S_{\rho,\sigma}$ and the identity + $\Delta_{\rho,\sigma}=J\Delta_{\sigma,\rho}^{-1}J$ — the two facts that make the + adopted form's relative modular operator exist and behave — are attested only + through [HIA18]'s citations to Araki, whose papers could not be obtained. So + is Haagerup's uniqueness of the standard form under (R2)(1), the + Lesniewski–Ruskai representation under (R6), and the whole of (R10). The + adopted form itself is therefore **(c)**, which is what `worst-tier` records. diff --git a/goals.yaml b/goals.yaml deleted file mode 100644 index 36b19f2..0000000 --- a/goals.yaml +++ /dev/null @@ -1,120 +0,0 @@ -# Formalization targets for QuantumSystem. -# -# `declarations` is the **truth source**: each goal is satisfied iff every -# fully-qualified Lean name in `declarations` exists in the project's Lean -# tree. Run with: -# -# python3 .claude/skills/gap-filler/scripts/detect_gaps.py \ -# references/ --goals goals.yaml --lean-root QuantumSystem \ -# --output references/gaps.json -# -# `concepts` is **informational**: it lists the prerequisite knowledge a -# goal depends on. `gap-filler` uses these hints to plan ingestions but -# they do not affect goal satisfaction. Each concept may be a bare string -# (any references/<slug>/INDEX.md mentioning it counts) or a structured -# entry that pins it to specific slugs and/or section anchors. - -goals: - - id: gelfand-naimark-theorem - description: >- - Every (possibly non-unital) C*-algebra embeds isometrically as a - *-subalgebra of B(H), realized as a direct sum of GNS reps. - declarations: - - gelfand_naimark_theorem - - GNS.DirectSum.directSumAlgHom_injective - - GNS.DirectSum.directSumAlgHom_isometry - concepts: - - name: GNS representation - sources: - - { slug: "92737", anchor: { definition: GNSRepresentation } } - - - id: gns-construction - description: >- - Cyclic representation (π_ω, H_ω, Ω_ω) with - ω(a) = ⟨Ω_ω, π_ω(a) Ω_ω⟩ for any state ω on a C*-algebra. - declarations: - - GNS.Representation - - GNS.Representation.unique_up_to_unitary_equivalence - - GNS.Construction.isFaithful_iff_separating - concepts: - - name: GNS representation - sources: - - { slug: "92737", anchor: { definition: GNSRepresentation } } - - - id: von-neumann-bicommutant - description: >- - Hard half of the bicommutant theorem: every WOT-closed (or - SOT-closed) unital *-subalgebra A of B(H) equals its double - commutant A″. - declarations: - - WOTClosedSubalgebra.doubleCommutant_eq_of_isWOTClosed - - SOTClosedSubalgebra.doubleCommutant_eq_of_isSOTClosed - - SOTClosedSubalgebra.isWOTClosed_of_isSOTClosed_starSubalgebra - - - id: von-neumann-entropy - description: >- - S(ρ) = −Tr(ρ log ρ) on finite-dimensional density matrices, plus - non-negativity, the dimension bound S(ρ) ≤ log(dim), and concavity - in ρ. - declarations: - - Matrix.vonNeumannEntropy - - Matrix.vonNeumannEntropy_nonneg - - Matrix.vonNeumannEntropy_le_log_dim - - Matrix.vonNeumannEntropy_concave - - - id: umegaki-relative-entropy - description: >- - Umegaki relative entropy D(ρ‖σ) = Tr ρ (log ρ − log σ) on EReal - so non-overlapping support is admitted as +∞. - declarations: - - Matrix.relativeEntropy - - Matrix.relativeEntropy_nonneg - - Matrix.relativeEntropy_eq_zero_iff - - - id: strong-subadditivity-region-explicit - description: >- - S(ρ_AB) + S(ρ_BC) ≥ S(ρ_ABC) + S(ρ_B) for tripartite finite - tensor products. Currently region-explicit on a fixed `LocalNet` - with concrete Finset regions. - declarations: - - DensityMatrix.vonNeumannEntropy_SSA - concepts: - - name: localNet - sources: - - { slug: "92737", anchor: { definition: localNet } } - - Lieb concavity - - - id: lieb-concavity - description: >- - Joint concavity of (A, B) ↦ Tr(A^p K† B^(1−p) K) on positive - semidefinite matrices for 0 ≤ p ≤ 1 (Effros' matrix-convex - argument). - declarations: - - Matrix.lieb_joint_concavity_semidef - - Matrix.lieb_joint_concavity_rect_semidef - - # Open TODO surfaced in the README itself. - - id: strong-subadditivity-abstract-local-net - description: >- - TODO. Abstract SSA over a generic local net of algebras (currently - only the region-explicit form is formalized). - declarations: [] - concepts: - - name: localNet - sources: - - { slug: "92737", anchor: { definition: localNet } } - - name: quasiLocalAlgebra - sources: - - { slug: "92737", anchor: { definition: quasiLocalAlgebra } } - - name: local algebra A(O) - sources: - - { slug: arxiv-2507.00900, anchor: { section: "1.2" } } - - name: Tomita-Takesaki modular operator - sources: - - { slug: arxiv-2507.00900, anchor: { section: "2.1" } } - - name: KMS condition - sources: - - { slug: arxiv-2507.00900, anchor: { section: "2.1" } } - - name: Araki-Uhlmann relative entropy - sources: - - { slug: arxiv-2507.00900, anchor: { section: "2.4" } } diff --git a/lefthook.yml b/lefthook.yml index b9837d9..e1f727a 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -5,7 +5,7 @@ commit-msg: commands: "lint commit message": - run: npx commitlint --edit {1} + run: uv run cz check --commit-msg-file {1} # Scan for secrets in staged files pre-commit: diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index ca1d422..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1410 +0,0 @@ -{ - "name": "quantum-system", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "quantum-system", - "devDependencies": { - "@commitlint/cli": "20.2.0", - "@commitlint/config-conventional": "20.2.0", - "lefthook": "2.0.11" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@commitlint/cli": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.2.0.tgz", - "integrity": "sha512-l37HkrPZ2DZy26rKiTUvdq/LZtlMcxz+PeLv9dzK9NzoFGuJdOQyYU7IEkEQj0pO++uYue89wzOpZ0hcTtoqUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/format": "^20.2.0", - "@commitlint/lint": "^20.2.0", - "@commitlint/load": "^20.2.0", - "@commitlint/read": "^20.2.0", - "@commitlint/types": "^20.2.0", - "tinyexec": "^1.0.0", - "yargs": "^17.0.0" - }, - "bin": { - "commitlint": "cli.js" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/config-conventional": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.2.0.tgz", - "integrity": "sha512-MsRac+yNIbTB4Q/psstKK4/ciVzACHicSwz+04Sxve+4DW+PiJeTjU0JnS4m/oOnulrXYN+yBPlKaBSGemRfgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.2.0", - "conventional-changelog-conventionalcommits": "^7.0.2" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/config-validator": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.2.0.tgz", - "integrity": "sha512-SQCBGsL9MFk8utWNSthdxd9iOD1pIVZSHxGBwYIGfd67RTjxqzFOSAYeQVXOu3IxRC3YrTOH37ThnTLjUlyF2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.2.0", - "ajv": "^8.11.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/ensure": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.2.0.tgz", - "integrity": "sha512-+8TgIGv89rOWyt3eC6lcR1H7hqChAKkpawytlq9P1i/HYugFRVqgoKJ8dhd89fMnlrQTLjA5E97/4sF09QwdoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.2.0", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.upperfirst": "^4.3.1" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/execute-rule": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", - "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/format": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.2.0.tgz", - "integrity": "sha512-PhNoLNhxpfIBlW/i90uZ3yG3hwSSYx7n4d9Yc+2FAorAHS0D9btYRK4ZZXX+Gm3W5tDtu911ow/eWRfcRVgNWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.2.0", - "chalk": "^5.3.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/is-ignored": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.2.0.tgz", - "integrity": "sha512-Lz0OGeZCo/QHUDLx5LmZc0EocwanneYJUM8z0bfWexArk62HKMLfLIodwXuKTO5y0s6ddXaTexrYHs7v96EOmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.2.0", - "semver": "^7.6.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/lint": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.2.0.tgz", - "integrity": "sha512-cQEEB+jlmyQbyiji/kmh8pUJSDeUmPiWq23kFV0EtW3eM+uAaMLMuoTMajbrtWYWQpPzOMDjYltQ8jxHeHgITg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/is-ignored": "^20.2.0", - "@commitlint/parse": "^20.2.0", - "@commitlint/rules": "^20.2.0", - "@commitlint/types": "^20.2.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/load": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.2.0.tgz", - "integrity": "sha512-iAK2GaBM8sPFTSwtagI67HrLKHIUxQc2BgpgNc/UMNme6LfmtHpIxQoN1TbP+X1iz58jq32HL1GbrFTCzcMi6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/config-validator": "^20.2.0", - "@commitlint/execute-rule": "^20.0.0", - "@commitlint/resolve-extends": "^20.2.0", - "@commitlint/types": "^20.2.0", - "chalk": "^5.3.0", - "cosmiconfig": "^9.0.0", - "cosmiconfig-typescript-loader": "^6.1.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/message": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.0.0.tgz", - "integrity": "sha512-gLX4YmKnZqSwkmSB9OckQUrI5VyXEYiv3J5JKZRxIp8jOQsWjZgHSG/OgEfMQBK9ibdclEdAyIPYggwXoFGXjQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/parse": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.2.0.tgz", - "integrity": "sha512-LXStagGU1ivh07X7sM+hnEr4BvzFYn1iBJ6DRg2QsIN8lBfSzyvkUcVCDwok9Ia4PWiEgei5HQjju6xfJ1YaSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.2.0", - "conventional-changelog-angular": "^7.0.0", - "conventional-commits-parser": "^5.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/read": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.2.0.tgz", - "integrity": "sha512-+SjF9mxm5JCbe+8grOpXCXMMRzAnE0WWijhhtasdrpJoAFJYd5UgRTj/oCq5W3HJTwbvTOsijEJ0SUGImECD7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/top-level": "^20.0.0", - "@commitlint/types": "^20.2.0", - "git-raw-commits": "^4.0.0", - "minimist": "^1.2.8", - "tinyexec": "^1.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/resolve-extends": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.2.0.tgz", - "integrity": "sha512-KVoLDi9BEuqeq+G0wRABn4azLRiCC22/YHR2aCquwx6bzCHAIN8hMt3Nuf1VFxq/c8ai6s8qBxE8+ZD4HeFTlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/config-validator": "^20.2.0", - "@commitlint/types": "^20.2.0", - "global-directory": "^4.0.1", - "import-meta-resolve": "^4.0.0", - "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/rules": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.2.0.tgz", - "integrity": "sha512-27rHGpeAjnYl/A+qUUiYDa7Yn1WIjof/dFJjYW4gA1Ug+LUGa1P0AexzGZ5NBxTbAlmDgaxSZkLLxtLVqtg8PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/ensure": "^20.2.0", - "@commitlint/message": "^20.0.0", - "@commitlint/to-lines": "^20.0.0", - "@commitlint/types": "^20.2.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/to-lines": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", - "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/top-level": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.0.0.tgz", - "integrity": "sha512-drXaPSP2EcopukrUXvUXmsQMu3Ey/FuJDc/5oiW4heoCfoE5BdLQyuc7veGeE3aoQaTVqZnh4D5WTWe2vefYKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^7.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/types": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.2.0.tgz", - "integrity": "sha512-KTy0OqRDLR5y/zZMnizyx09z/rPlPC/zKhYgH8o/q6PuAjoQAKlRfY4zzv0M64yybQ//6//4H1n14pxaLZfUnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/conventional-commits-parser": "^5.0.0", - "chalk": "^5.3.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@types/conventional-commits-parser": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz", - "integrity": "sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/conventional-changelog-angular": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", - "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", - "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.mjs" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", - "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "jiti": "^2.6.1" - }, - "engines": { - "node": ">=v18" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=9", - "typescript": ">=5" - } - }, - "node_modules/dargs": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", - "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/find-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", - "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^7.2.0", - "path-exists": "^5.0.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/git-raw-commits": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", - "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dargs": "^8.0.0", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.mjs" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/global-directory": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "text-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/lefthook": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-2.0.11.tgz", - "integrity": "sha512-/91k4dt9MRNkzeSr1iMjNi/z8dNuh+XvNfXrWA6PV+M1ZxiNY6uN6bGnr13n+j7N89f4h7YWBhCqhzhK33M5cA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "lefthook": "bin/index.js" - }, - "optionalDependencies": { - "lefthook-darwin-arm64": "2.0.11", - "lefthook-darwin-x64": "2.0.11", - "lefthook-freebsd-arm64": "2.0.11", - "lefthook-freebsd-x64": "2.0.11", - "lefthook-linux-arm64": "2.0.11", - "lefthook-linux-x64": "2.0.11", - "lefthook-openbsd-arm64": "2.0.11", - "lefthook-openbsd-x64": "2.0.11", - "lefthook-windows-arm64": "2.0.11", - "lefthook-windows-x64": "2.0.11" - } - }, - "node_modules/lefthook-darwin-arm64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-2.0.11.tgz", - "integrity": "sha512-RfpdcJJQXstdgDiIBDRffncayKiXx+0LyMUCunIxDEO2JMXPpYK2hIdpUU0rkitzptAADchG7u1OXJ31rrtIAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-darwin-x64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-2.0.11.tgz", - "integrity": "sha512-D013UNKQa4FKgpxDMqdaU109U2/Pidtrt9CobQoq8te4eGUglcwxMzuYVTgaYnenz0FgKxSfVaCZsZgwqeMWqA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-freebsd-arm64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-2.0.11.tgz", - "integrity": "sha512-mgfNqG1tiJkCuGNwPG0LEfnAHGJA+Qzl6KidOtX/Zhxmj/sM+6hxiP4LOeEAhCnaZF5kuPtQgbFzShFHc2BK6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-freebsd-x64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-2.0.11.tgz", - "integrity": "sha512-rnHOlQbJfLGCibr7yHM44kPNgf/tFpEbj/cWVHRhjRdbgYSCAjJk0uKd/EVo3v/vjfId2na0AhWbLvO/aY3wQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-linux-arm64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-2.0.11.tgz", - "integrity": "sha512-1XjDo2/4fM0TbJBwxZh8w+WMOFueg9oYHkryM8vc3vp8wTajdWBazg1K37JIS3FUco3tcOs+eWHQg0ekVjpWoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-linux-x64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-2.0.11.tgz", - "integrity": "sha512-OKOcfEvozXhO7+y2xgUzvc2kkqfhluql/sjQSzd8Ka+iK3hM4KCfbfgYx9q61Pjr34a0+i03cuH5DF2dlq/rrg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-openbsd-arm64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-2.0.11.tgz", - "integrity": "sha512-n1KEx196M3SKaWVNTQXGgxzBsiYAsdAy6Of6I6TAZwPhG7yoRrKGkQrhOlPgMzYl36udG1Lk4D+mfY9T0oOUYQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-openbsd-x64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-2.0.11.tgz", - "integrity": "sha512-WAEtKpYUVvuJMVLA38IBoaPnTNSiaEzvUYxjTBlYTLHJwn7HC2GG6P1cnvoua8rfxb9/Bfi7C3D3IPa9VmB33Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-windows-arm64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-2.0.11.tgz", - "integrity": "sha512-HBqW1qfAnmmbpet7gSWatB6H5YIFdGxCqzolMCLwY/0o8oPFiMwdNE5RGp5JMmhZdz/h3XlbaUlIhnxoW8dk5g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/lefthook-windows-x64": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-2.0.11.tgz", - "integrity": "sha512-e5TYmV5cBZfRrhPVFCqjauegLI5CjdAd8exyAbMzGHkiwp3ZK145Su/pntgEP3d+ayS9mpgYPJmXYOSL7WHlyg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, - "license": "MIT" - }, - "node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 235d230..0000000 --- a/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "quantum-system", - "devDependencies": { - "@commitlint/cli": "20.2.0", - "@commitlint/config-conventional": "20.2.0", - "lefthook": "2.0.11" - }, - "commitlint": { - "extends": [ - "@commitlint/config-conventional" - ] - } -} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8a03aed --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[project] +name = "quantum-system" +version = "0.1.0" +description = "Development environment for quantum system" +requires-python = ">=3.13" +dependencies = [] + +[dependency-groups] +dev = [ + "commitizen>=4.17.0", + "lefthook>=2.1.10", +] +# PDF conversion for the math-extract skill. Its own group so it can be skipped +# deliberately (`uv sync --no-group mineru`) -- it pulls torch and ~100 packages. +mineru = [ + "mineru[pipeline,vlm]>=3.4.5", + # mineru 3.4.5's pipeline imports six without declaring it (measured: every + # conversion fails with "No module named 'six'" otherwise). + "six>=1.17.0", + # Capped so the cu128 wheels below stay resolvable: cu128 stops at torch 2.11. + # mineru itself asks only for torch<3,>=2.6.0. + "torch>=2.6,<2.12", + "torchvision", +] + +[tool.uv] +# Both groups sync by default, so a bare `uv sync` cannot silently uninstall the +# converter. Skip the heavy one with `uv sync --no-group mineru`. +default-groups = ["dev", "mineru"] + +# torch must match the *host* NVIDIA driver, not the newest CUDA: the default +# PyPI wheel is built for CUDA 13 and needs driver 580+, while this host runs +# 573.22 (CUDA 12.8), where it fails with "The NVIDIA driver on your system is +# too old (found version 12080)" and falls back to CPU. cu128 wheels work there +# (measured). Bump this index only after the host driver moves past 580. +[[tool.uv.index]] +name = "pytorch-cu128" +url = "https://download.pytorch.org/whl/cu128" +explicit = true + +[tool.uv.sources] +torch = [{ index = "pytorch-cu128" }] +torchvision = [{ index = "pytorch-cu128" }] + +[tool.commitizen] +name = "cz_conventional_commits" diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..9ebd404 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2174 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version < '3.14'", +] + +[[package]] +name = "accelerate" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/75/94cd5d389649578aca399e5aa822637eec18319a1dadc400ffe2f9a7493f/accelerate-1.14.0.tar.gz", hash = "sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d", size = 412167, upload-time = "2026-06-11T13:45:52.326Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/db/253133d7e7cb40d3af384bb2f5c0b4a2b7fdcffbc95c688cc67a20a3c103/accelerate-1.14.0-py3-none-any.whl", hash = "sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6", size = 389246, upload-time = "2026-06-11T13:45:50.477Z" }, +] + +[[package]] +name = "aiofiles" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, +] + +[[package]] +name = "anyio" +version = "4.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, +] + +[[package]] +name = "argcomplete" +version = "3.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/6f/5a73f04007ca950701765949209f068da628bd11f9c2da287278ce91e0ee/argcomplete-3.7.2.tar.gz", hash = "sha256:aad8b69a0b9969edb62db0d1752354c0d50717b10e0cbb00e2a958381b9fc6b9", size = 74473, upload-time = "2026-08-06T04:53:21.662Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl", hash = "sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", size = 43294, upload-time = "2026-08-06T04:53:20.246Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571, upload-time = "2026-06-07T16:44:20.453Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" }, +] + +[[package]] +name = "certifi" +version = "2026.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/31/4971872b3ed8715346231fb6eb4da8fcba65a4143c189db151ee28a2812b/charset_normalizer-3.5.0.tar.gz", hash = "sha256:49bd5feb59b0bf3cbf6ebcf4352e371c95b9da9bacd4449f8b64d0ad2c10a26e", size = 169295, upload-time = "2026-08-12T14:35:31.624Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/be/cc7b7b6fc41984902c0d31b06f5d9297e67705c1dae9352608e5540fad09/charset_normalizer-3.5.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:5c23fa4f6eccdd601949cb00f3988c01d64e671d8faba356397971077022e144", size = 211050, upload-time = "2026-08-12T14:32:47.81Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ae/e3ec8f17313609f43f7b323012fdb1ee37b83432277ca4eceba83e00366c/charset_normalizer-3.5.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:07f6f42b5a6325df35b458004fb5f9f29bf502d89287a33c7cdef3590e31de0f", size = 222768, upload-time = "2026-08-12T14:32:49.027Z" }, + { url = "https://files.pythonhosted.org/packages/c5/50/9f9c0d7ccc1512d49e27a0e7c12c58ec71dfe91698fa4326f058c33e1f1b/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8efc3f1563ed431882dd0dc0411b5f8ace1b1b89074981deaf6bd8af77dbe1bc", size = 193907, upload-time = "2026-08-12T14:32:50.414Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1d/cfe7b745ef7f4c3b7214581955b5a0869ba2ac551a58fc11036281ae167c/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:368eb2fc9482158b3a3386e8f01fa61f479c968e9a19ceab8f0188b86b312991", size = 197135, upload-time = "2026-08-12T14:32:51.605Z" }, + { url = "https://files.pythonhosted.org/packages/28/55/30fafdcfca9ba616bc394240545e4cd52f4f66dea43ded81b7d2d5274fde/charset_normalizer-3.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:826a295a039178479a325be1ae60eded1f0b10f7dda749df59e2440de8f61d64", size = 339892, upload-time = "2026-08-12T14:32:52.82Z" }, + { url = "https://files.pythonhosted.org/packages/f4/08/bdca5fc2bdc36ee443673dc7d12b23885a5a7b282bef85a1a4c3b325b40e/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70ff1c16eb0eb5ee6bb12739292347f981a5ba764cc4df1bc2e69b0405d4ac3b", size = 239439, upload-time = "2026-08-12T14:32:54.058Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9e/506c8d7a7722bba7c8cdd78c1b5ef23bda92bfbe0b3e28ea84673d519a0f/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cfdab178a4add5483e26a9bb1c16d8018ccf39b4be7a3aea6c3979e6828f2ee", size = 227896, upload-time = "2026-08-12T14:32:55.326Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1a/dd828f2b1d6f4bf10821b9a74d866be05ffcdbfcddfc501d6fe6428762a7/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6083d10a846218502d664375b9448508d9fa580bd834567423156c6abfbe899d", size = 262548, upload-time = "2026-08-12T14:32:56.484Z" }, + { url = "https://files.pythonhosted.org/packages/be/81/196d26f6bd78b93e0d451b69082a71027ceeddd4b0be9170b81bb038f824/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f211c21aa316cb6e2662e54a1194633a79d98a50a876addacfce7ba5b34b09f", size = 259986, upload-time = "2026-08-12T14:32:57.661Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6a/5b964a1eb0f9075ecd45083eeb21aaec215334f98bac3d400302ea73875d/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b08ebf9488c7ff5eff038e48e6ea938178dfd9dcc8598b5ca941e4ae27b20be", size = 249853, upload-time = "2026-08-12T14:32:59.123Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b4/aee3a9d82edd0e931091ef3e9f03e46491ae3590e96e998d0975dadbe17c/charset_normalizer-3.5.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95450fce59f00c6d08eff6572ec2e736e5054c9450253afd5748f8416f2eb9", size = 244217, upload-time = "2026-08-12T14:33:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/22/3e/33f72ca11c1b619b220fd9f35905ebd171cbd0e0470f2357e467b9e861ee/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5780a29823e1d2bec69b7a104ead4195a43f3e97782efaedbf1f79a0157af715", size = 241307, upload-time = "2026-08-12T14:33:01.589Z" }, + { url = "https://files.pythonhosted.org/packages/78/27/6029dccba958621c7f3a65136f87c5512d712aef9e890f09512cc171bd03/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:054420b5db984971d886e5e4e2c37c760ae6682aedbd066687ff0949d9ed5f08", size = 233305, upload-time = "2026-08-12T14:33:02.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/d7/691c967be459153fe9faf49bf78bc95639ef8bf6dd008f38cc6389a349eb/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d016dc857136c726958102c3b8a3986acdc65ace6fbf12cfdc09cc4bfa2935b2", size = 263465, upload-time = "2026-08-12T14:33:04.166Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2d/9202221be5c90b2a835924191e362690ed8dc8c7d6606100c2bd03fe0f8c/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f2ce3d39fb4a9d674e6639dd5d3146b2e273475d2260f10163228d66fc04433d", size = 245060, upload-time = "2026-08-12T14:33:05.325Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9a/298772fd0a0cbccadf36451a1cd7eef4b66a11e99b4a7f6fafc47cc62c75/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a5613a3a82c974227bde18f03409e30c467f8065cb56d822e3eb83708a5f223d", size = 261091, upload-time = "2026-08-12T14:33:06.475Z" }, + { url = "https://files.pythonhosted.org/packages/82/3b/1a11fe66e555dbe2f5714ade6ba74fa29edc9155d9cf1001d4d6ed096aa7/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fded2e82ff082e5d8e017e2ddcc1411bd8cb83b8585097fc401ef574f756b888", size = 251857, upload-time = "2026-08-12T14:33:07.784Z" }, + { url = "https://files.pythonhosted.org/packages/17/fc/73b817e8af3f1d25ec5cf458d405abba5a144cf9812238a61530f5eac186/charset_normalizer-3.5.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:8f006866047c6ec4b627ec144b1e0bbc7427cb31fd7c08d19897d0ac9032af3d", size = 139745, upload-time = "2026-08-12T14:33:09.123Z" }, + { url = "https://files.pythonhosted.org/packages/b3/fb/ddb66303c86f7dc5043a457dad9fa82b4d6d0cb97094f9bcdde21693fd58/charset_normalizer-3.5.0-cp313-cp313-win32.whl", hash = "sha256:196e270c4e80827b5072eed7d6aa661d133afada94fe366669f9609e718d305e", size = 177217, upload-time = "2026-08-12T14:33:10.305Z" }, + { url = "https://files.pythonhosted.org/packages/fb/88/6018cc8d76ea2b7cb02918f37e23e86c261d1a102713d7e88d2cfb8b211c/charset_normalizer-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:72982d9958a42f8132bf2d6b90214ed66477295ef1188731f98ae3511c6eeb5a", size = 198896, upload-time = "2026-08-12T14:33:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/20/2e/04c0bbfc8d9abf91959f7a3d207d45cbf63a8116984caae2381890019bb5/charset_normalizer-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:0b373bab0b867b68b8eb249da9478cab9181a42993437cd2f5dba5fb0b4fbd1b", size = 179193, upload-time = "2026-08-12T14:33:12.726Z" }, + { url = "https://files.pythonhosted.org/packages/43/14/d098868dac5ff27e0258f548b1c74c6484be528384965d8fcf8fc6a4011d/charset_normalizer-3.5.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d95244906ed69d0f79f190893c65e336c15959003e21449256dc05c001b52ea2", size = 211664, upload-time = "2026-08-12T14:33:14.153Z" }, + { url = "https://files.pythonhosted.org/packages/e7/da/a944b32a46601ae5a4c3499e8d64ecd14fe82313f00da74dcdf00273a0b4/charset_normalizer-3.5.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d788e2ded0c4c47efa4d73cfe59eaf975ee32f425219873d2cb3e3fbaa00f636", size = 224375, upload-time = "2026-08-12T14:33:15.472Z" }, + { url = "https://files.pythonhosted.org/packages/f7/db/eabb5996be2f529744755e7b2fc9396eff4a64961f034e7fd49d54b9afb2/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:f9f91d3e8382900f3a68fa0ce94294479de9cd2de6bc0c70acd0f0dfd511836b", size = 194364, upload-time = "2026-08-12T14:33:16.607Z" }, + { url = "https://files.pythonhosted.org/packages/78/65/4ad3c5be108930310d8003f5602861d5b89f728293b9f09c3a4837f7ba10/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d54625cbf4e6b60bf0639728cb8b4cb541e340f6d7cafae5806051a40ddf4c45", size = 197643, upload-time = "2026-08-12T14:33:17.88Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8fee3201b98d52289be60a775797d69be05a04fb6cfb48c1587dad33e649/charset_normalizer-3.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:1f99a8c3a1da5d955edbad18208b3d627bdd54c48a6e739fa877bdca98c686d6", size = 341384, upload-time = "2026-08-12T14:33:19.239Z" }, + { url = "https://files.pythonhosted.org/packages/a7/dd/9e757101d1f76c35c0643684ba499ac3a181fb2b264c68174bf727d627e8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf1e75dc07a3850b53d1e5f75e04d3ae12afe56284be7821771eaa2466350c73", size = 241637, upload-time = "2026-08-12T14:33:20.619Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/7857023015400bc4aa0a82fbcca29fa2dc7ec25f971a130764cb2dc7a589/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ac5a9cc079c67d75f4ddf343276031879eadbb333d1bb231cce297b8d7b9aae8", size = 226170, upload-time = "2026-08-12T14:33:21.773Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ae/8b52935b304f7b6bbf33151ed2b75266b09aa4b6f8f04230d948885b2577/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0dfe83c1b4d00abbf433998117a14f56a5c2bc68226c0d331709eed0d1ce539b", size = 265093, upload-time = "2026-08-12T14:33:22.999Z" }, + { url = "https://files.pythonhosted.org/packages/dc/78/6e838f6bb059f2c0afc60a4e7f294252f043c254656ad4114c50302cae4d/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e8fa586df2208ef040684751345f10f503834a757c9a74ecd19c1a2f9b1ccd", size = 262789, upload-time = "2026-08-12T14:33:24.214Z" }, + { url = "https://files.pythonhosted.org/packages/c2/08/189b27e51fddc9d6b3695331da0e31792c1d88b953ad854e57f06e9b2cc8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82cc5835997ec78afe293a192e385099355770a7db94b2fb1239d36b32796f1c", size = 250580, upload-time = "2026-08-12T14:33:25.707Z" }, + { url = "https://files.pythonhosted.org/packages/ac/55/64854e99b25841f83e8e37d9df2f3d1f96f693439f80e5fabd542a7e47ab/charset_normalizer-3.5.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:19e52bda45086df8a4be4bb5910af6f5d9d3b538c78712c8ae09ef10b85bf458", size = 245008, upload-time = "2026-08-12T14:33:26.971Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/7720c904fa635d4260b4dced6029cf3d298c57b26741365d5a8d28c54043/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3418edd0ecb72a0a3861cf72f31be0ad9b7fe338ce2b58fb5cc80b9aeb792700", size = 243892, upload-time = "2026-08-12T14:33:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/70/50/7bfcb327631d4870c720872b548745f6ec8baa044d51c21b5d1d32ac4e3a/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:125ee619611019471b177c70bc3e9d4cda9fad7e01d93523501d3b188df0193a", size = 230996, upload-time = "2026-08-12T14:33:29.511Z" }, + { url = "https://files.pythonhosted.org/packages/24/51/40c45d6d940c04005ed721aa54bdebf1ebb2930f8a2ae537e8d60484fb27/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a3ad0e3da22852533858663848608f3f24c0d35e5cde415a4903476f2b4c88ec", size = 265834, upload-time = "2026-08-12T14:33:30.689Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d4/ef7a227ef89d215b47f9df79c3966610b17faa13bb2f236989207a631622/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:4ebebb410bc517e1d284c52a123e82704b21e4e7e26a21ebecf7439d0647b8a3", size = 245544, upload-time = "2026-08-12T14:33:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/37/a9/a4ca9156964ded61c7718eba410ce11be2fd2b263fda4bcf08367b6578cd/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:91f9f7c151e772acebe489eaec96e96a2877202d7dd144e3f96b8676881715a0", size = 264110, upload-time = "2026-08-12T14:33:33.13Z" }, + { url = "https://files.pythonhosted.org/packages/38/6a/838364bb8702229c6e5f8b23f80ff0f052a12dfaf3113a12fd6acbe92a44/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:401ea6e7af9e7852ed818f64714b579c1935482049670847ca3bd7ba45dc63fb", size = 252303, upload-time = "2026-08-12T14:33:34.98Z" }, + { url = "https://files.pythonhosted.org/packages/9c/5f/d88032edce951f499a2321cf7ae0d35a043c74be12bc22d81084cc7afbcc/charset_normalizer-3.5.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f7496aed56b06325a1ad419c5bf23c6dd042558e874f71dd1b958f3e255f3053", size = 139964, upload-time = "2026-08-12T14:33:36.195Z" }, + { url = "https://files.pythonhosted.org/packages/37/ae/1c4a46b6b00d1c34d2ee355ef99ad6173674166800d1af0f05f85028d513/charset_normalizer-3.5.0-cp314-cp314-win32.whl", hash = "sha256:606a86c1c3196f3738de39a67a7490bbd61cb31c0e0436070bd0c6a48170b38e", size = 179790, upload-time = "2026-08-12T14:33:37.356Z" }, + { url = "https://files.pythonhosted.org/packages/01/51/f94dcf34fa8eba48c1fb89b6490a5f1426e19488fe5f38aac6c648c99057/charset_normalizer-3.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:ec6c464cf45867f66a2273e2214d9199a8fbad5cb95ca0fd45f6a2fe1d9d2cf4", size = 203723, upload-time = "2026-08-12T14:33:38.639Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ba/91d386870b5d9e4b0d8c4034f63877cc2e47b99c81ef05f3e6d42bf9a53f/charset_normalizer-3.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:dc28949de1bb5f7f30a46f15d74ce7ac5aaa63e03c5de04d68f571c7423af834", size = 183423, upload-time = "2026-08-12T14:33:39.899Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c9/534ecb17b7fb95f9052c4a44cf316316a27d4a8f73e8475ff55e778dcdd7/charset_normalizer-3.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:68b7e84ae8239a94f8d2c8f3f3a3a81bcde54805ec8f42a34de927d155688ec6", size = 368967, upload-time = "2026-08-12T14:33:41.093Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/ad7c3242d7fe55cd55126c22c65cb1b49779782cdf8932fd01d12232d86a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58ca5dc0a0ef99f2801ec0574214c978e9574055bc783830bbb6e7433218609f", size = 239478, upload-time = "2026-08-12T14:33:42.428Z" }, + { url = "https://files.pythonhosted.org/packages/7e/62/77f0b850048e430fc350ec58876b0c020f5c8d0d3956fd1a4d6ae2fa292f/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6c57af4084c10cb3286688d65e4c654190ff5edcbc2411d08cdca0a8a44c59a1", size = 227036, upload-time = "2026-08-12T14:33:43.635Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ba/47d951e1a51dddbaad0a1410baf49fb1d897ceb00281568f1183b79bce9a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1619a3cc174a7e3963dd34348e6fceb6e50db0ddeb0031bd7c73a58286454fa", size = 260772, upload-time = "2026-08-12T14:33:44.96Z" }, + { url = "https://files.pythonhosted.org/packages/b0/61/8c7ff4c81b2a88271126acf4b83ab3e31f6d63868b0f01d331eaa0f9cb67/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1328cc57dd4372be1265f68232cee890e087416e3e6e93e6ffb32c2bad4d36a4", size = 259273, upload-time = "2026-08-12T14:33:46.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fd/36129689be08dc287b951306946657ff70d76e287dd57018861f86d0e474/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06f4fb62a9139bef056b8b2da6773c94c2f259f90e4b8e53b166f3d0372d7cf6", size = 248086, upload-time = "2026-08-12T14:33:47.54Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f8/bcae67f994c8fd31dda445e5ebf84045823c31443fe46f0e9ee6aca99aa0/charset_normalizer-3.5.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:478650a70a750d75d5add401606c77f77069c32e4ba2c9131dc6cee566962ca0", size = 242671, upload-time = "2026-08-12T14:33:48.746Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/0472cdad1061c2f0e4d3aee29973eb6e81bb8fe256ff2860cf115b15f1c9/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48920bf6fe83eb2226756ac623fa54940487154eb18f80889d5735cf234965c0", size = 241311, upload-time = "2026-08-12T14:33:50.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/89/04a03de5d27c77c624d9fcf6287073754bd438df1b58cb7d030c57c2824d/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:168a0cb536b5123a77bc42ecf5e0bf6f923d0d9ae43c42a14eb0677c19ac6c19", size = 229898, upload-time = "2026-08-12T14:33:51.523Z" }, + { url = "https://files.pythonhosted.org/packages/42/a2/639c4278adcb7ed1f4db608dd9ac19b6774fa2285a96b1c0bdb9c124ccbd/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f278e131afa96a3622cef9211c406ea2ad1b68eb06f8837cd443684a40e0ae50", size = 262852, upload-time = "2026-08-12T14:33:52.924Z" }, + { url = "https://files.pythonhosted.org/packages/9d/95/02e34c97bedfd0c5574efb9179c850591acc7f967ba039ed8dd29d332b73/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d6100f877d2ed95f0856a3fde25334153add94bf2224c43f45f88e7039262aaa", size = 242913, upload-time = "2026-08-12T14:33:54.18Z" }, + { url = "https://files.pythonhosted.org/packages/a3/64/0946aeab6462dad9f160a50dfb4704d3f58a5ee708f085abc2105fbbff0c/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4c440122e1ea68b1f8b44a631ebf49c39180f6869b1da22d76e8a724208ec6e9", size = 257938, upload-time = "2026-08-12T14:33:55.802Z" }, + { url = "https://files.pythonhosted.org/packages/79/77/36787d41ead124746506a4425c729f4f17c68280af8a6a5baa0a598cae86/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e5f834965c2fe589837bac1002e07e25734ff70381903ccd95b3d649e22bfa40", size = 249467, upload-time = "2026-08-12T14:33:57.081Z" }, + { url = "https://files.pythonhosted.org/packages/65/10/d9f6c5589cd24198d4ce6cd2948191c18e657272f433e5a00d258d9f5c22/charset_normalizer-3.5.0-cp314-cp314t-win32.whl", hash = "sha256:076cf9d3f3c7e410295c09d96355cf3b1bcae74990034d80e4371e20fe1ba4c6", size = 190624, upload-time = "2026-08-12T14:33:58.449Z" }, + { url = "https://files.pythonhosted.org/packages/6c/81/43e0584a802051a22c725795ebe1df78263abc7de858eef6cdc9b36637e9/charset_normalizer-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3288a560dc3114d5d2ebe309b1ef43f8af355eafe25856832415c2a8196c9db3", size = 215902, upload-time = "2026-08-12T14:33:59.753Z" }, + { url = "https://files.pythonhosted.org/packages/30/f3/af6a1160fef0eac4510d035241e11eccf78e5350e4cd4de79e79fe02a5e5/charset_normalizer-3.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a284c36b9c6616bf0a8aa4aabba668a0c75ba65ccf40a79868aeaa69ad996897", size = 193452, upload-time = "2026-08-12T14:34:01.017Z" }, + { url = "https://files.pythonhosted.org/packages/42/a4/dee470afb7a55c4f78b6fef37306c51fed17ebf94dbe530798c91d394350/charset_normalizer-3.5.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:c38d1e9bc2073b0984d2099ea647fd7f6c0d8f83a1e14e0cd32926f16e4c44ce", size = 341595, upload-time = "2026-08-12T14:34:02.4Z" }, + { url = "https://files.pythonhosted.org/packages/6a/32/9c3126dc429c6d9d7f79c52681a7c4453ed20a26267c9a8275d7ab620aba/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9f45186390aee4d1f26f723c615b67df346766c3b16df000d84d6e374f06757", size = 242177, upload-time = "2026-08-12T14:34:03.741Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9d/5b616a887301ff4cc0916b39ba44257390d3da80deeed6e8b6f2f26b14a8/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f0fde5e5100c735b2274ab898f0742a5dcde492796296cfbe7e0ad6a4cd1a396", size = 236730, upload-time = "2026-08-12T14:34:04.991Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/d6d3e70be93ebe5fabef65e4c7ac113e1d1705cbaeb5fb72467e713aca17/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d00e18e7bbf47e332ab63903d18bae31efc701b1d8cca0382b97784a621fc44", size = 265158, upload-time = "2026-08-12T14:34:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/3f1fafa87e2643257474f9c4eec609f2193a61d907dce7dd4f3f2390ebd5/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b81980668800dd1c69faad8aea6e85a8cee0e13bcd3bba7671695ff16260293", size = 262931, upload-time = "2026-08-12T14:34:07.511Z" }, + { url = "https://files.pythonhosted.org/packages/0a/df/ebeb224a949d91829e5e114c6b64372a3c792b00762a9e951ce416f3a32d/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb3e0d1345b9c0fe73673ea656375f38a78ec679c2edeae0c24800f04798a85", size = 251388, upload-time = "2026-08-12T14:34:08.949Z" }, + { url = "https://files.pythonhosted.org/packages/a0/64/9a6ce2e7acc5cf1b4636f78f82e89ff581e06a0216a40678b28bd4d832c4/charset_normalizer-3.5.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2401f7671242e921e604f609d429f6b282ea4ca787a6ffd22ed7372011ddb9d1", size = 251821, upload-time = "2026-08-12T14:34:10.138Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b1/6e69b8056f615e5ccff6b91ca16db2d47922251f016821a300c115267fef/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:96720f2aeed3434bc48f4d52fbad64ecc820cfed88915d664780ed9ba09ede78", size = 244507, upload-time = "2026-08-12T14:34:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/0f/34/02c15d6a0aa6b934dcdc136b111da63ae857b9fd51cf5505b0736337c2eb/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:c455829625df983f716cbaecbba77f2d1dc2e0e0ed1638c059cece15a279344b", size = 240951, upload-time = "2026-08-12T14:34:12.991Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/0fe893d3e1c7d111280bd6c4bd4c1e431487a1124a1bcbce78dfeda3a3a8/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:c41b067eddcfa5ee6b1169c287605be7fb6b0ea22bba6474c5bb978a668def4f", size = 266162, upload-time = "2026-08-12T14:34:14.232Z" }, + { url = "https://files.pythonhosted.org/packages/55/ea/eca03527307670f5d102c295671a800c404ca958cf94fefd10fc963a72f0/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:e31786a947b136329bfdc458c82c06d4ec539b4a4436b7da4df4aafc9902ee80", size = 251835, upload-time = "2026-08-12T14:34:15.48Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/fee5633081e595fe9e191df6f215106c791ad596eddf5e41e39b8ea0f2e2/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:c5c6d47a865147e0ae3322ce92e7fb52ba3169d94b447deda56897ea2aa6fac9", size = 264314, upload-time = "2026-08-12T14:34:16.679Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fb/17f47ae6ca35b562fb6e6f4b05f7aec6034217353eb4a23aaa3566dc7340/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c75191e3c8052045179646cb40e280800a4e0bdfda34d9c949c2f268d44e80e4", size = 253194, upload-time = "2026-08-12T14:34:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/59/88/f2b0f7ebb92493e925889ff29239b3b0073ffafd91230dbfc69e5cf9389c/charset_normalizer-3.5.0-cp315-cp315-win32.whl", hash = "sha256:83b62410bd36bb1178a7d563e2ee0cf21eb1c980c912ab99c2c78f06227f1731", size = 179800, upload-time = "2026-08-12T14:34:19.409Z" }, + { url = "https://files.pythonhosted.org/packages/e2/f0/afb5bfdea52fd943b1960403847a276b8e900c6e4cd6a38752321b4eda64/charset_normalizer-3.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:e3b9eaa99a6d8c9ace4cd303915947ef55088d4cd87c6676874f98c5c03aa040", size = 203726, upload-time = "2026-08-12T14:34:20.656Z" }, + { url = "https://files.pythonhosted.org/packages/fc/71/219783eb691aa2ec879c0e521afdfe2b826f9678eed51b9c039d03e0db2b/charset_normalizer-3.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:fec352b793cdc183cc9e7e0b6c10fd7bff38ec54ba44cc43599b9b56f7f3db2e", size = 183428, upload-time = "2026-08-12T14:34:21.975Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d0/14aef3b9f80f2593c039d897e89034635b9eb0eb44b6ce5173bbd79ff338/charset_normalizer-3.5.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:c9bde7a960720c8b8e1b5ef7afaa0c9a2f3b55c44abd635b2b29dd066b298e3a", size = 368728, upload-time = "2026-08-12T14:34:23.221Z" }, + { url = "https://files.pythonhosted.org/packages/10/fc/b249466ddbbeffa448b6597631e9091d1f01b5132ff8e7a0e21a6eb72b63/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8cd1283a9fe6c2065c807e9d5da81afe5e1e004caef39adc0d8ae86dd883698", size = 240925, upload-time = "2026-08-12T14:34:24.504Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b9/c17e72aaa1b3e1ca6c184e8025cf138ed492d01a54f85286ff7d31253a4b/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1c010dd86d3f4c4433c9634d33ce8147393b270dfa54f217f965540b8ae8e075", size = 234932, upload-time = "2026-08-12T14:34:25.822Z" }, + { url = "https://files.pythonhosted.org/packages/18/d7/f84ef0966bbe216f71029e34e7fa425a16b1682e2a40265e679dedf2b655/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5e68229977b2dea28e7061c0c0630a23f2f9f6e9c6fb38d77d3d6dbfe3768b74", size = 261733, upload-time = "2026-08-12T14:34:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/3b/73/3e887fa0781a395339355ed934ab6561ceb5bb52574160f070224039c630/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a60773eb5fda796e6e6f76b9c152d270fe59f9788a51a6ff8ba44082d8548ae4", size = 258460, upload-time = "2026-08-12T14:34:28.431Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/52ebd9849bf9e35d0b21fff115cb6543162a8e1f2f564e8f87121a336b8c/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9419f44e568f7fafcdc0b3b5c766a2364e705a9b34fb8a56b431e0d1f3f4258", size = 249894, upload-time = "2026-08-12T14:34:29.698Z" }, + { url = "https://files.pythonhosted.org/packages/85/f3/9366492b8a5fe0187de282e001d61345740cf79eb4a5f20181d769be02b5/charset_normalizer-3.5.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75e243abbb528c1a774390ed71e3f868a9f37b1373442e4bbadd401cfc505ff4", size = 249540, upload-time = "2026-08-12T14:34:30.96Z" }, + { url = "https://files.pythonhosted.org/packages/0b/af/28bb5e5dbd3e67cb9196a62781ac2b6d79492f4fc7a069b6ca7d6d6c8d58/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:54c963ce6404e52255b737e8a06d356fc762d59096ae566203a67cf2b7d050f2", size = 242734, upload-time = "2026-08-12T14:34:32.481Z" }, + { url = "https://files.pythonhosted.org/packages/26/d6/7ccfa62b53b40fc06b2d3504825aa400764740bd10cf248fdc4272441b93/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:63ea0cc840c66670183578c2630d138c0e944aeadfc33f25173ee240f5db780d", size = 239580, upload-time = "2026-08-12T14:34:33.916Z" }, + { url = "https://files.pythonhosted.org/packages/0b/82/71c0c9b046697b8da66b3acefa8d5f92d00a9ef433ad7c3522b971d0369a/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6c06875a1d4a7537bef70f659b55c6b55b9a47ec3ba8f2db610350c2d9915e6e", size = 263281, upload-time = "2026-08-12T14:34:35.333Z" }, + { url = "https://files.pythonhosted.org/packages/d6/01/d027583c869f40ba980c1c76994adbd522c360a6327e72beb44d7c267385/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:4253da1b4456b633651a8d59eb1dc7a8a8fa38241014dd7c217b353e547ae394", size = 250027, upload-time = "2026-08-12T14:34:36.991Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e9/6475d739e0ec8bb1236e06263dc3affaffdf947d8114ad27024932f325da/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:f044cb1cf44012184715f46584658993b5fee9344d71c4b0c455a17a299730c0", size = 257547, upload-time = "2026-08-12T14:34:38.277Z" }, + { url = "https://files.pythonhosted.org/packages/a5/60/d1f502fcaa048a2aca3ab80bfef8407659c131e4f1792fa805fec14b4960/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a17864853f7c518ae7d4b368af98f427f9396805476af40af8698560f09d7d97", size = 251718, upload-time = "2026-08-12T14:34:39.562Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/76343dcf4381a698807ff8a20d89f66bbdd9f6222b0b17740f77ab764335/charset_normalizer-3.5.0-cp315-cp315t-win32.whl", hash = "sha256:9e726478d7a213847860219d74665a6892a643ac93b8f76580f6cf9ed39996b7", size = 190757, upload-time = "2026-08-12T14:34:41.053Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ea/d18147626a1667cc773c42104ab155a4ca5d6d4d174b7a35e01062213ea5/charset_normalizer-3.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:d7229a99120c6c2792d96f4857c2648ce5530e93667a2c2388c5ef69a6b84775", size = 215431, upload-time = "2026-08-12T14:34:42.501Z" }, + { url = "https://files.pythonhosted.org/packages/47/21/4869598aae0872d94faa5933918a4fe37ab2c5af9d095786e241f9506fed/charset_normalizer-3.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:527e28a5e751d9e11369b9c5f9ab35c748eb9c109101920c7deb40d6eadf8d03", size = 193205, upload-time = "2026-08-12T14:34:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f3/7b523d807cb5e73562ef8acf21d39cdb9d704955327362c781bc3478a73d/charset_normalizer-3.5.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:5a4ee37248dfac25107c758bda99d545ce73e60b44d2dd39e4a2bb9f2831e9f5", size = 330840, upload-time = "2026-08-12T14:34:45.06Z" }, + { url = "https://files.pythonhosted.org/packages/f0/de/fc68978fe78ca97063c96d764e41ff92ca639948f319271e0ff450e577a2/charset_normalizer-3.5.0-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a864bdcacd8bff58bb4845304e031f821a3ec64b2b7259f2d409cd49c9e59ca3", size = 251862, upload-time = "2026-08-12T14:34:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/a9/cb/82b41a0ab7fb1a88065f1d78ad32696ad88ea3fe8e25b8189d08833938de/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:84b736e3b391601bc47b86da381c749c0f894e9191aaca9f31f30c2632206df3", size = 239484, upload-time = "2026-08-12T14:34:47.869Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0c/19608b631f4538f908098d4a2d56a8f79a665e27cc58e9d90479761a9227/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6abb1f356fb865baeb6ebc3fadd843e9a96fbf49b9adcca55037f3cceccb7438", size = 230602, upload-time = "2026-08-12T14:34:49.265Z" }, + { url = "https://files.pythonhosted.org/packages/29/db/f648eb30e14eba301aed61e11672156f137905c1bdbb530151abe8065943/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d366548d2ee28a8cfdcc4296363978cc644a728333be9824d2de4652e83df0a", size = 259208, upload-time = "2026-08-12T14:34:50.632Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e0/ed2c8bdbac484d69614d6993143aeb6cb0f4dd1561c883402517b623c8ef/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d672f329ae504ee240eb39b6effb3318aa8e7e8924c0ce8eee5760b3fad98539", size = 253659, upload-time = "2026-08-12T14:34:52.11Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/b4907cb9ec5b521d9d024ced13611240b86ef065c2eb15b3ad2334dc9940/charset_normalizer-3.5.0-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c54036a518748b6c02e666f6d46c3817561998fb904c3be25b56fb4fe3dc5706", size = 248821, upload-time = "2026-08-12T14:34:53.399Z" }, + { url = "https://files.pythonhosted.org/packages/12/b2/e2d1abcfbc05822f0030869efb4e9f8a3658e13b4821796d4b62da917327/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:22a1889f1c9b752c63c36758a0c2145458e3cadb20fced7a0790002e9dd12b26", size = 240271, upload-time = "2026-08-12T14:34:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f9/4ba127ad610542fa3eabfa41c45bf12d357860a815b3566374ec0188e213/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b7eb3eab5c646d3de7dcb14a7c9caebace5249c5767da39e1761cb1576e521a3", size = 232155, upload-time = "2026-08-12T14:34:56.543Z" }, + { url = "https://files.pythonhosted.org/packages/01/68/40613182366d00bd6dbd5f6c84a926cbd120960e038a8269e9ae7d782762/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:2080aa129a28267984cdc902898993d788c995c384e285d0d19199f56760d52e", size = 259674, upload-time = "2026-08-12T14:34:57.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/53/4574a14fa4c9de4a6c9f31725354bfa40b67f653e6d594ce1654f9a41b32/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:fd68c825548a611158230e2f9222e210ceb2e3391995c0aa5865cbdf3ab4bd49", size = 246122, upload-time = "2026-08-12T14:34:59.337Z" }, + { url = "https://files.pythonhosted.org/packages/5a/02/bd8030d13d92c058ca7b2b9615bbb3169569e144db64d65c149cd45abf5e/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:d8a9316f4da85e937242642b537c6d55d7e9287dd38e5634732f8233932aff45", size = 255221, upload-time = "2026-08-12T14:35:00.71Z" }, + { url = "https://files.pythonhosted.org/packages/77/9d/10ecd3bcbe2666b3d4d4026c97b48f73990682815db516052a1e8f4a31c5/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d90254c8f609338c53ec180fcd4c4f9c16502e238e3fc88ca7fd4c2f38d445b8", size = 253450, upload-time = "2026-08-12T14:35:02.217Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0f/d044c4872c0938a84f87b5027a698c0e61bacfc5c3551a4e749ca9b7bc5c/charset_normalizer-3.5.0-cp37-abi3-win32.whl", hash = "sha256:8b3e9e29b8b07cc461b9ce7768db7693a93979d0dadf22046f6f3555ded2f516", size = 173594, upload-time = "2026-08-12T14:35:03.845Z" }, + { url = "https://files.pythonhosted.org/packages/10/6b/6046773901f1944b9a89436351529811ee958afc7b774563be9d74a6f0c3/charset_normalizer-3.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:0c8953d9d1617794cfc40d81179571c9ba3805dd029623a15c93f1fb70e60a74", size = 198959, upload-time = "2026-08-12T14:35:05.187Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a6/b57708ac92aefc8e8389d51d5178129b81f03196da61ee2c23e687b8178a/charset_normalizer-3.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:562d24ca7797c1af8852994950c2e623a907b201fc4b0ed29e92af173d3828ca", size = 267055, upload-time = "2026-08-12T14:35:06.533Z" }, + { url = "https://files.pythonhosted.org/packages/22/c7/754d09943a616937df61e4ba367c409ded2a987e872972098d51a6fcf73b/charset_normalizer-3.5.0-py3-none-any.whl", hash = "sha256:993dfcbe75a85a3784abb5084f2c41b915767c90546fcc92803cffa28611baea", size = 67943, upload-time = "2026-08-12T14:35:30.363Z" }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + +[[package]] +name = "cobble" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/7a/a507c709be2c96e1bb6102eb7b7f4026c5e5e223ef7d745a17d239e9d844/cobble-0.1.4.tar.gz", hash = "sha256:de38be1539992c8a06e569630717c485a5f91be2192c461ea2b220607dfa78aa", size = 3805, upload-time = "2024-06-01T18:11:09.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/e1/3714a2f371985215c219c2a70953d38e3eed81ef165aed061d21de0e998b/cobble-0.1.4-py3-none-any.whl", hash = "sha256:36c91b1655e599fd428e2b95fdd5f0da1ca2e9f1abb0bc871dec21a0e78a2b44", size = 3984, upload-time = "2024-06-01T18:11:07.911Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "colorlog" +version = "6.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/55/ba79756cb90c8d69d599d57785398ac87bba7b19c80e87f4e8a562197c93/colorlog-6.12.0.tar.gz", hash = "sha256:2a7924c1dadf18b22a0eb8b06d1c7b01d5341707ec1641eb6fcc4fde0c3e8e5f", size = 18151, upload-time = "2026-07-23T13:40:40.71Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", hash = "sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", size = 12239, upload-time = "2026-07-23T13:40:39.562Z" }, +] + +[[package]] +name = "commitizen" +version = "4.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "charset-normalizer" }, + { name = "colorama" }, + { name = "decli" }, + { name = "deprecated" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "prompt-toolkit" }, + { name = "pyyaml" }, + { name = "questionary" }, + { name = "termcolor" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/af/510e0659cf66dadddd769c1c955dc0b28a4685dd69bfd34e6e6ee35c7c41/commitizen-4.17.0.tar.gz", hash = "sha256:e5bcabed3474029cc8f5a5c62216130bc2999f7bc29a3b9884bcd426d7f35c5a", size = 67643, upload-time = "2026-07-29T09:17:19.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/a0/68f2683c1d736c20f48d8428a1db7486231f1b49928cf9cfaa9863b45998/commitizen-4.17.0-py3-none-any.whl", hash = "sha256:2e1842d12b7c29591bbeb2094d115aa47d10c6a92f5d6430bf24590434520726", size = 89566, upload-time = "2026-07-29T09:17:18.366Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "12.9.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/7b/f1575e41e1a17dc2f2a408b2e8e864c9324e41e3e23f6401e5efc54c152a/cuda_bindings-12.9.7-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:266379e4942051f544a8e7ea1a30ead8d7e8199b6b30fcdc8917cae2bf614e61", size = 6978549, upload-time = "2026-05-27T18:44:18.839Z" }, + { url = "https://files.pythonhosted.org/packages/9d/dc/62d62eb4f91eb721bcf46da51b13e9872ccd8fa7e60eb8ba7b7baeac72c6/cuda_bindings-12.9.7-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:59cf4a37b0d662ba15037c9ceebe1a306ebf2c01a8235a09be13cd07094fdb74", size = 7457675, upload-time = "2026-05-27T18:44:20.637Z" }, + { url = "https://files.pythonhosted.org/packages/f9/77/94d9b85f26add6fe9c9cb7c4ec3b96bc598f7ea5cfbd7490cc0a36adf5be/cuda_bindings-12.9.7-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2dbcd4801954eb3508f4dc2fa0d0c8eb93eb3f45326fd61be2731418c371e7a0", size = 6870886, upload-time = "2026-05-27T18:44:24.164Z" }, + { url = "https://files.pythonhosted.org/packages/04/dd/3ec34b569e1b990b11276feba306bf8f446656cc38e8ed0f49b5facfeffa/cuda_bindings-12.9.7-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3747ea132642416786a8e31bf229032df3a7856911ae5426a7be53d032df183d", size = 7345663, upload-time = "2026-05-27T18:44:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/68/e4/075052d42872cf8162da53f14447a4b8abc004c3750e4b724ee502428da0/cuda_bindings-12.9.7-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:775960ac9e530717f3b48e165cc6f68684fa9a4141764fd923e4c1a9820acc73", size = 7060090, upload-time = "2026-05-27T18:44:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/ec/cd/3289c810a4d45e5364a3387a74b4c9b6f6f57ee96ae0e5b537cc61dec242/cuda_bindings-12.9.7-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c47ec1a7a441d91aab32339951df7a1be53451121a12c094bba51467717a35a", size = 7504419, upload-time = "2026-05-27T18:44:31.992Z" }, + { url = "https://files.pythonhosted.org/packages/11/43/472a6281c3d94e71687e27c657a8f60718d3579b4d94c41deea503165f8a/cuda_bindings-12.9.7-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00a833d399b31071fab4cf3de2929840ae462dc4848116eeff033d09219e7116", size = 6899146, upload-time = "2026-05-27T18:44:35.556Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/10c1d0b32a9da65142d213e0733d748457fb3fd066aee4317335266f15c6/cuda_bindings-12.9.7-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11aeafa2b33995f890086b3fb0f062075176d956e9b6a6fe1a699dddc413f6ad", size = 7369087, upload-time = "2026-05-27T18:44:37.359Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/b4/d088047afe39827556df21118cac9ffd20cc3f968c99a7681494d1eb333c/cuda_pathfinder-1.6.0-py3-none-any.whl", hash = "sha256:1503af579d8379c24bdd65528379bc57039b0455be9f5f9686cf8e473a1fce51", size = 54591, upload-time = "2026-07-21T15:03:56.224Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "12.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/c8/7dce3a0b15b42a3b58e7d96eb22a687d3bf2c44e01d149a6874629cd9938/cuda_toolkit-12.8.1-py2.py3-none-any.whl", hash = "sha256:adc7906af4ecbf9a352f9dca5734eceb21daec281ccfcf5675e1d2f724fc2cba", size = 2283, upload-time = "2025-08-13T02:03:07.842Z" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas-cu12" }, +] +cudart = [ + { name = "nvidia-cuda-runtime-cu12" }, +] +cufft = [ + { name = "nvidia-cufft-cu12" }, +] +cufile = [ + { name = "nvidia-cufile-cu12" }, +] +cupti = [ + { name = "nvidia-cuda-cupti-cu12" }, +] +curand = [ + { name = "nvidia-curand-cu12" }, +] +cusolver = [ + { name = "nvidia-cusolver-cu12" }, +] +cusparse = [ + { name = "nvidia-cusparse-cu12" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink-cu12" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc-cu12" }, +] +nvtx = [ + { name = "nvidia-nvtx-cu12" }, +] + +[[package]] +name = "decli" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/59/d4ffff1dee2c8f6f2dd8f87010962e60f7b7847504d765c91ede5a466730/decli-0.6.3.tar.gz", hash = "sha256:87f9d39361adf7f16b9ca6e3b614badf7519da13092f2db3c80ca223c53c7656", size = 7564, upload-time = "2025-06-01T15:23:41.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/fa/ec878c28bc7f65b77e7e17af3522c9948a9711b9fa7fc4c5e3140a7e3578/decli-0.6.3-py3-none-any.whl", hash = "sha256:5152347c7bb8e3114ad65db719e5709b28d7f7f45bdb709f70167925e55640f3", size = 7989, upload-time = "2025-06-01T15:23:40.228Z" }, +] + +[[package]] +name = "deprecated" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "et-xmlfile" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, +] + +[[package]] +name = "fast-langdetect" +version = "0.2.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fasttext-predict" }, + { name = "requests" }, + { name = "robust-downloader" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/17/2753b3d280e16f594e7a5d735568c021a355cd1edd3827f26cda5c9fd460/fast_langdetect-0.2.5.tar.gz", hash = "sha256:e5fe65973f5737107bb8314f3829280d196c87d4da0b4a6e95000175512cf2c3", size = 788620, upload-time = "2025-01-28T02:15:24.637Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/da/c621e64d4bc23f485468295bb7d4a5f2290ebb4d342c8dc448ab66808071/fast_langdetect-0.2.5-py3-none-any.whl", hash = "sha256:8d5ff640d94d5f30bb7653c761adbb9122b617b03fa1f166b7cc16c35e484d0e", size = 786618, upload-time = "2025-01-28T02:15:23.039Z" }, +] + +[[package]] +name = "fastapi" +version = "0.141.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/02/91e3416a8fdd715abb903a952a6bec7cdd8d14eed55d415fc8595524c319/fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1", size = 425799, upload-time = "2026-07-29T17:18:05.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, +] + +[[package]] +name = "fasttext-predict" +version = "0.9.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/0e/9defbb9385bcb1104cc1d686a14f7d9fafe5fe43f220cccb00f33d91bb47/fasttext_predict-0.9.2.4.tar.gz", hash = "sha256:18a6fb0d74c7df9280db1f96cb75d990bfd004fa9d669493ea3dd3d54f84dbc7", size = 16332, upload-time = "2024-11-23T17:24:44.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/fc/5cd65224c33e33d6faec3fa1047162dc266ed2213016139d936bd36fb7c3/fasttext_predict-0.9.2.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddb85e62c95e4e02d417c782e3434ef65554df19e3522f5230f6be15a9373c05", size = 104916, upload-time = "2024-11-23T17:23:43.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/53/8d542773e32c9d98dd8c680e390fe7e6d4fc92ab3439dc1bb8e70c46c7ad/fasttext_predict-0.9.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:102129d45cf98dda871e83ae662f71d999b9ef6ff26bc842ffc1520a1f82930c", size = 97502, upload-time = "2024-11-23T17:23:44.447Z" }, + { url = "https://files.pythonhosted.org/packages/50/99/049fd6b01937705889bd9a00c31e5c55f0ae4b7704007b2ef7a82bf2b867/fasttext_predict-0.9.2.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05ba6a0fbf8cb2141b8ca2bc461db97af8ac31a62341e4696a75048b9de39e10", size = 282951, upload-time = "2024-11-23T17:23:46.31Z" }, + { url = "https://files.pythonhosted.org/packages/83/cb/79b71709edbb53c3c5f8a8b60fe2d3bc98d28a8e75367c89afedf3307aa9/fasttext_predict-0.9.2.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c7a779215571296ecfcf86545cb30ec3f1c6f43cbcd69f83cc4f67049375ea1", size = 307377, upload-time = "2024-11-23T17:23:47.685Z" }, + { url = "https://files.pythonhosted.org/packages/7c/4a/b15b7be003e76613173cc77d9c6cce4bf086073079354e0177deaa768f59/fasttext_predict-0.9.2.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddd2f03f3f206585543f5274b1dbc5f651bae141a1b14c9d5225c2a12e5075c2", size = 295746, upload-time = "2024-11-23T17:23:49.024Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d3/f030cd45bdd4b052fcf23e730fdf0804e024b0cad43d7c7f8704faaec2f5/fasttext_predict-0.9.2.4-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:748f9edc3222a1fb7a61331c4e06d3b7f2390ae493f91f09d372a00b81762a8d", size = 236939, upload-time = "2024-11-23T17:23:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/a2/01/6f2985afd58fdc5f4ecd058d5d9427d03081d468960982df97316c03f6bb/fasttext_predict-0.9.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1aee47a40757cd24272b34eaf9ceeea86577fd0761b0fd0e41599c6549abdf04", size = 1214189, upload-time = "2024-11-23T17:23:51.647Z" }, + { url = "https://files.pythonhosted.org/packages/75/07/931bcdd4e2406e45e54d57e056c2e0766616a5280a18fbf6ef078aa439ab/fasttext_predict-0.9.2.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6ff0f152391ee03ffc18495322100c01735224f7843533a7c4ff33c8853d7be1", size = 1099889, upload-time = "2024-11-23T17:23:53.127Z" }, + { url = "https://files.pythonhosted.org/packages/a2/eb/6521b4bbf387252a96a6dc0f54986f078a93db0a9d4ba77258dcf1fa8be7/fasttext_predict-0.9.2.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4d92f5265318b41d6e68659fd459babbff692484e492c5013995b90a56b517c9", size = 1383959, upload-time = "2024-11-23T17:23:54.521Z" }, + { url = "https://files.pythonhosted.org/packages/b7/6b/d56606761afb3a3912c52971f0f804e2e9065f049c412b96c47d6fca6218/fasttext_predict-0.9.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3a7720cce1b8689d88df76cac1425e84f9911c69a4e40a5309d7d3435e1bb97c", size = 1281097, upload-time = "2024-11-23T17:23:55.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/83/55bb4a37bb3b3a428941f4e1323c345a662254f576f8860b3098d9742510/fasttext_predict-0.9.2.4-cp313-cp313-win32.whl", hash = "sha256:d16acfced7871ed0cd55b476f0dbdddc7a5da1ffc9745a3c5674846cf1555886", size = 91137, upload-time = "2024-11-23T17:23:57.886Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1d/c1ccc8790ce54200c84164d99282f088dddb9760aeefc8860856aafa40b4/fasttext_predict-0.9.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:96a23328729ce62a851f8953582e576ca075ee78d637df4a78a2b3609784849e", size = 104896, upload-time = "2024-11-23T17:23:59.028Z" }, + { url = "https://files.pythonhosted.org/packages/a4/c9/a1ccc749c59e2480767645ecc03bd842a7fa5b2b780d69ac370e6f8298d2/fasttext_predict-0.9.2.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b1357d0d9d8568db84668b57e7c6880b9c46f757e8954ad37634402d36f09dba", size = 109401, upload-time = "2024-11-23T17:24:00.191Z" }, + { url = "https://files.pythonhosted.org/packages/90/1f/33182b76eb0524155e8ff93e7939feaf5325385e5ff2a154f383d9a02317/fasttext_predict-0.9.2.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9604c464c5d86c7eba34b040080be7012e246ef512b819e428b7deb817290dae", size = 102131, upload-time = "2024-11-23T17:24:02.052Z" }, + { url = "https://files.pythonhosted.org/packages/2b/df/1886daea373382e573f28ce49e3fc8fb6b0ee0c84e2b0becf5b254cd93fb/fasttext_predict-0.9.2.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6da186c2e4497cbfaba9c5424e58c7b72728b25d980829eb96daccd7cface1", size = 287396, upload-time = "2024-11-23T17:24:03.294Z" }, + { url = "https://files.pythonhosted.org/packages/35/8f/d1c2c0f0251bee898d508253a437683b0480a1074cfb25ded1f7fdbb925a/fasttext_predict-0.9.2.4-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366ed2ca4f4170418f3585e92059cf17ee2c963bf179111c5b8ba48f06cd69d1", size = 311090, upload-time = "2024-11-23T17:24:04.625Z" }, + { url = "https://files.pythonhosted.org/packages/5d/52/07d6ed46148662fae84166bc69d944caca87fabc850ebfbd9640b20dafe7/fasttext_predict-0.9.2.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f1877edbb815a43e7d38cc7332202e759054cf0b5a4b7e34a743c0f5d6e7333", size = 300359, upload-time = "2024-11-23T17:24:06.486Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a1/751ff471a991e5ed0bae9e7fa6fc8d8ab76b233a7838a27d70d62bed0c8e/fasttext_predict-0.9.2.4-cp313-cp313t-manylinux_2_31_armv7l.whl", hash = "sha256:f63c31352ba6fc910290b0fe12733770acd8cfa0945fcb9cf3984d241abcfc9d", size = 241164, upload-time = "2024-11-23T17:24:08.501Z" }, + { url = "https://files.pythonhosted.org/packages/94/19/e251f699a0e9c001fa672ea0929c456160faa68ecfafc19e8def09982b6a/fasttext_predict-0.9.2.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:898e14b03fbfb0a8d9a5185a0a00ff656772b3baa37cad122e06e8e4d6da3832", size = 1218629, upload-time = "2024-11-23T17:24:10.04Z" }, + { url = "https://files.pythonhosted.org/packages/1d/46/1af2f779f8cfd746496a226581f747d3051888e3e3c5b2ca37231e5d04f8/fasttext_predict-0.9.2.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:a33bb5832a69fc54d18cadcf015677c1acb5ccc7f0125d261df2a89f8aff01f6", size = 1100535, upload-time = "2024-11-23T17:24:11.5Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b7/900ccd74a9ba8be7ca6d04bba684e9c43fb0dbed8a3d12ec0536228e2c32/fasttext_predict-0.9.2.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7fe9e98bd0701d598bf245eb2fbf592145cd03551684a2102a4b301294b9bd87", size = 1387651, upload-time = "2024-11-23T17:24:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5a/99fdaed054079f7c96e70df0d7016c4eb6b9e487a614396dd8f849244a52/fasttext_predict-0.9.2.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dcb8c5a74c1785f005fd83d445137437b79ac70a2dfbfe4bb1b09aa5643be545", size = 1286189, upload-time = "2024-11-23T17:24:14.615Z" }, + { url = "https://files.pythonhosted.org/packages/87/6a/9114d65b3f7a9c20a62b9d2ca3b770ee65de849e4131cc7aa58cdc50cb07/fasttext_predict-0.9.2.4-cp313-cp313t-win32.whl", hash = "sha256:a85c7de3d4480faa12b930637fca9c23144d1520786fedf9ba8edd8642ed4aea", size = 95905, upload-time = "2024-11-23T17:24:15.868Z" }, + { url = "https://files.pythonhosted.org/packages/31/fb/6d251f3fdfe3346ee60d091f55106513e509659ee005ad39c914182c96f4/fasttext_predict-0.9.2.4-cp313-cp313t-win_amd64.whl", hash = "sha256:be0933fa4af7abae09c703d28f9e17c80e7069eb6f92100b21985b777f4ea275", size = 110325, upload-time = "2024-11-23T17:24:16.984Z" }, +] + +[[package]] +name = "filelock" +version = "3.32.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/64/a02e6765de08964ed371eca577870593245afc9dfac16d037de7c10d18e6/filelock-3.32.3.tar.gz", hash = "sha256:0ffa185a3540854c95caa7fa76b76cb219d907415e2c5dc9af25fd970563487f", size = 218135, upload-time = "2026-08-13T16:00:05.577Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/8e/50f46a9c0ce8d2861a394c1347caae037ea0431d2f67d7feb151cbc4649a/filelock-3.32.3-py3-none-any.whl", hash = "sha256:7f0ca4bcc0e181c60dbbd8aa9ab5b120ebb99e4e064e83636340056f833a1f09", size = 98901, upload-time = "2026-08-13T16:00:03.974Z" }, +] + +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/78/f34251dadb8f3921264a1d9b8946f5e542014ee2614b285261b4e40e6775/fsspec-2026.7.0.tar.gz", hash = "sha256:c803c40f4cf860b49dea58ee3e1c33cb9c790520e233537e1340049f89b82a88", size = 317040, upload-time = "2026-07-28T16:34:51.052Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/3c/6a2bf344106328fd04963664a60b9bb6496fc25df8e962fcdc1367285fb9/fsspec-2026.7.0-py3-none-any.whl", hash = "sha256:b57ddbafedfaef7018c1ecab32aa200a9d7ca26b77965f64e48b70061249d279", size = 206583, upload-time = "2026-07-28T16:34:49.538Z" }, +] + +[[package]] +name = "ftfy" +version = "6.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/ab/522a2ab67f27971a9d48ca666d4fca85ef7d5282d142e31fd087e27b1bbe/hf_xet-1.6.0.tar.gz", hash = "sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef", size = 920527, upload-time = "2026-08-03T22:33:13.243Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/62/3c062f593bd92ef4e77a0ef39541e3d82a0a1d3947c8a777a02a13a27828/hf_xet-1.6.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:70cbb9c896901600128cb9b6f06e132954fbede1db30f31f7c6c63f84cb7c31d", size = 4074584, upload-time = "2026-08-03T22:32:47.364Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1e/c0ad437dd267a8e435bef594acf781bbc3874ff0b6435b4962d03ecf7cc4/hf_xet-1.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:23379c2f9ec8696d952b16414a2bae72cad86a52df869b050698ba60f538c675", size = 3867381, upload-time = "2026-08-03T22:32:49.049Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ee/7c0d7b6ab336167531b1c30af2af003f054af4c749becbd7209ae33a77c3/hf_xet-1.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f7278c05c22fd60cb436cda1269649b3e81db65ecdc8496e5e164aa4143e7b", size = 4453982, upload-time = "2026-08-03T22:32:50.568Z" }, + { url = "https://files.pythonhosted.org/packages/63/06/ad8eab1c9525246650cbaa821caa3cdbaca734ab1a5b8c91bea09cbd8d69/hf_xet-1.6.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:948f15d3a9545cfe5932f6bd8b440f6ae630aee108f14b7bd6c561f7c2dcc522", size = 4249445, upload-time = "2026-08-03T22:32:52.391Z" }, + { url = "https://files.pythonhosted.org/packages/d8/26/1eee8aedb0dafc1ab9717dc9ac602cde33361b232dc06803f1f6ed18b58c/hf_xet-1.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5153e6bb103ad49d6ea9f1b2e230db5a2ea32551ad09a706d2f61d7c7c80d80e", size = 4451099, upload-time = "2026-08-03T22:32:54.114Z" }, + { url = "https://files.pythonhosted.org/packages/67/57/0b88af1f194ab6c9c650547d9cc06bfeaab836ae4dcdb331676bfb8be95a/hf_xet-1.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:35cec30d75c6f9eb9c16a77cef68e85a103b72e24d4b473714ec9ff06428bab9", size = 4664712, upload-time = "2026-08-03T22:32:55.547Z" }, + { url = "https://files.pythonhosted.org/packages/53/a0/26b717a9d1840e8abf48dcec64b5ed8fbe472671d38ad28d30e147132b33/hf_xet-1.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5789835d7c6bc9436962853192082374297fb72d7eff7e7762ec25ceb7e25338", size = 4025906, upload-time = "2026-08-03T22:32:57.391Z" }, + { url = "https://files.pythonhosted.org/packages/49/f6/4a9966633c6fef83af997e2cff68ec1963676d412bdfd096df2a93b8e185/hf_xet-1.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:75765820ce4700db3750c94acc8fe27c5fae4c9ec000a0dbac3ca082acf97765", size = 3849221, upload-time = "2026-08-03T22:32:59.123Z" }, + { url = "https://files.pythonhosted.org/packages/a2/50/7afa2c9c787405864fc47a0d1bbc02c62e9101947ed43c1f43899fc7d91d/hf_xet-1.6.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:633dc0cd71d32da58ab8c03ad38e2fac452c15c2b0a2866ebf6ededfe0a5061d", size = 4071729, upload-time = "2026-08-03T22:33:00.721Z" }, + { url = "https://files.pythonhosted.org/packages/4b/69/55b8dcf636142ae660fec1869fcac14c4da2e8412e14d6eee1523be77e9f/hf_xet-1.6.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:f0906082d9932ae0c0057fa194041c22b4e2cdb46b2592ef3b91f020d62a081a", size = 3876287, upload-time = "2026-08-03T22:33:02.251Z" }, + { url = "https://files.pythonhosted.org/packages/67/4e/a28359bf1c1ecf11eba22123168c138698f7cb576ac678f5a2e16cd5da08/hf_xet-1.6.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f", size = 4464663, upload-time = "2026-08-03T22:33:03.802Z" }, + { url = "https://files.pythonhosted.org/packages/9a/69/1f0cbc2fb22ae6082d094f743d1b8945a3f36f6089cb95f42b7ee348cda7/hf_xet-1.6.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0e6e21fa3cdfcdcd76748564bf593870a5e013f47d97cf10aed63aa222cff5b7", size = 4262538, upload-time = "2026-08-03T22:33:05.287Z" }, + { url = "https://files.pythonhosted.org/packages/d1/3a/4f4f2301ade26e404462d3336fa11f7958d914cabbabdd6e03c3c5d5658c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4fc74352a17015bd0ee90038bc9efe38db894cde45f268b6712b04fce8cd0acb", size = 4460520, upload-time = "2026-08-03T22:33:06.81Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5f/311725e2a905534dfee2dcb5b08414f249147f1f12252bfc2bd24caa075c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c", size = 4675937, upload-time = "2026-08-03T22:33:08.616Z" }, + { url = "https://files.pythonhosted.org/packages/98/b7/8c59a66d15205024662f1d66968136f13893f96df1ddc5087e2e281fc95f/hf_xet-1.6.0-cp38-abi3-win_amd64.whl", hash = "sha256:fb4fadde1b2b70bf4c0c14a6dccbe7194b1c28947fefd5bbe3fed9d940676c3b", size = 4033128, upload-time = "2026-08-03T22:33:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/73/63/ca511b6f802f28cf3489b280fe77475bcca8de85e81a6299d7916b5b5555/hf_xet-1.6.0-cp38-abi3-win_arm64.whl", hash = "sha256:3dc3e35441ba395006af5aaacc40ef2e603c51ef46c3530b9156185f00935ea3", size = 3859359, upload-time = "2026-08-03T22:33:11.725Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-retries" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/d3/b7a8bb09543af40009717a08a2ceba90b6d4c6f0cdf171404217d8f4c37d/httpx_retries-0.6.0.tar.gz", hash = "sha256:3e0b404969a564829d368417964fd21e6b400a10d17c92d29b8bc247ce8186e3", size = 21122, upload-time = "2026-07-06T00:52:30.093Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c6/7f3d6ab3549267a1959161b38df4c0fb435eceaf2d531d8addfac01abaca/httpx_retries-0.6.0-py3-none-any.whl", hash = "sha256:d1e52a8f68a5df42de75ab89049d5020b2d0ab2f5f8bceacda008d12aa1257a3", size = 11776, upload-time = "2026-07-06T00:52:31.033Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782, upload-time = "2026-02-06T09:24:13.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395, upload-time = "2026-02-06T09:24:11.133Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jiter" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431, upload-time = "2026-06-29T13:05:13.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/c0/555fc60473d30d66894ba825e63615e3be7524fac23858356afa7a38906c/jiter-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:41977aa5654023948c2dae2a81cbf9c43343954bef1cd59a154dd15a4d84c195", size = 306203, upload-time = "2026-06-29T13:03:36.243Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2b/c3eaf16f5d7c9bad66ea32f40a95bd169b29a91217fcc7f081375157e99c/jiter-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d28bb3c26762358dadf3e5bf0bccd29ae987d65e6988d2e6f49829c76b003c09", size = 306489, upload-time = "2026-06-29T13:03:37.846Z" }, + { url = "https://files.pythonhosted.org/packages/96/3f/02fdfc6705cad96127d883af5c34e4867f554f29ec7705ec1a46156400a9/jiter-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0542a7189c26920778658fc8fcf2af8bae05bae9924577f71804acef37996536", size = 335453, upload-time = "2026-06-29T13:03:39.221Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a6/e4bda5920d4b0d7c5dfb7174ce4a6b2e4d3e11c9162c452ef0eab4cdbdbd/jiter-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fb8de1e23a0cb2a7f53c335049c7b72b6db41aa6227cdcc0972a1de5cb39450", size = 361625, upload-time = "2026-06-29T13:03:40.597Z" }, + { url = "https://files.pythonhosted.org/packages/b7/97/4e6b59b2c6e55cbb3e183595f81ad65dcfb21c915fee5e19e335df21bc55/jiter-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72d0b2990ca754a9102779ac98d8597b7cb31678958562214a007f909eab78e", size = 456958, upload-time = "2026-06-29T13:03:42.074Z" }, + { url = "https://files.pythonhosted.org/packages/15/e0/97e9557686d2f94f4b93786eccb7eed28e9228ad132ea8237f44727314a7/jiter-0.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f91b1c27fc22a57993d5a5cb8a627cb8ed4b10502716fac1ffbfe1d19d84e8", size = 372017, upload-time = "2026-06-29T13:03:43.658Z" }, + { url = "https://files.pythonhosted.org/packages/0f/94/db768b6938e0df35c86beeba3dfbbb025c9ee5c19e1aa271f2396e50864d/jiter-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c682bea068a90b764577bdb78a60a4c1d1606daf9cd4c893832a37c7cc9d9026", size = 343320, upload-time = "2026-06-29T13:03:45.226Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d6/5a59d938244a30735fe62d9433fd325f9021ea29d89780ea4596ea93bc89/jiter-0.16.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8d031aabecc4f1b6276adfb42e3aabb77c89d468bf616600e8d3a11328929053", size = 350520, upload-time = "2026-06-29T13:03:46.671Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/c4a857f49c9af125f6bbcac7e3eee7f7978ed89682833062e2dbf62576b1/jiter-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eab2cd170150e70153de16896a1774e3a1dca80154c56b54d7a812c479a7165e", size = 387550, upload-time = "2026-06-29T13:03:48.361Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d6/5fbc2f7d6b67b754caa61a993a2e626e815dec47ffc2f9e35f01adfebec7/jiter-0.16.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6edb63a46e65a82c26800a868e49b2cac30dd5a4218b88d74bc2c848c8ad60bb", size = 515424, upload-time = "2026-06-29T13:03:49.881Z" }, + { url = "https://files.pythonhosted.org/packages/ed/54/284f0164b64a5fed915fea6ba7e9ba9b3d8d37c67d59cf2e3bb99d45cdfe/jiter-0.16.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:659039cc50b5addcc35fcc87ae2c1833b7c0a8e5326ef631a75e4478447bcf84", size = 546981, upload-time = "2026-06-29T13:03:51.363Z" }, + { url = "https://files.pythonhosted.org/packages/13/c5/2a467585a576594384e1d2c43e1224deaafc085f24e243529cf98beef8e1/jiter-0.16.0-cp313-cp313-win32.whl", hash = "sha256:c9c53be232c2e206ef9cdbad81a48bfa74c3d3f08bcf8124630a8a748aad993e", size = 202853, upload-time = "2026-06-29T13:03:53.015Z" }, + { url = "https://files.pythonhosted.org/packages/88/6a/de61d04b9eec69c71719968d2f716532a3bc121170c44a39e14979c6be81/jiter-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:baad945ed47f163ad833314f8e3288c396118934f94e7bbb9e243ce4b341a4fd", size = 196160, upload-time = "2026-06-29T13:03:54.447Z" }, + { url = "https://files.pythonhosted.org/packages/19/4b/b390ed59bafb3f31d008d1218578f10327714484b334439947f7e5b11e7f/jiter-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:3c1fd2dbe1b0af19e987f03fe66c5f5bd105a2229c1aff4ab14890b24f41d21a", size = 189862, upload-time = "2026-06-29T13:03:55.754Z" }, + { url = "https://files.pythonhosted.org/packages/a7/89/bc4f1b57d5da938fd344a466396541e586d161320d70bffd929aaafcd8f4/jiter-0.16.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b2c61484666ad42726029af0c00ef4541f0f3b5cdc550221f56c2343208018ee", size = 308239, upload-time = "2026-06-29T13:03:57.205Z" }, + { url = "https://files.pythonhosted.org/packages/65/7a/c415453e5213001bf3b411ff65dec3d303b0e76a4a2cfea9768cd4960994/jiter-0.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63efadc657488f45db1c676d81e704cac2abf3fdb892def1faea61db053127e2", size = 308928, upload-time = "2026-06-29T13:03:58.643Z" }, + { url = "https://files.pythonhosted.org/packages/11/fc/1f4fb7ebf9a724c7741994f4aae18fba1e2f3133df14521a79194952c34a/jiter-0.16.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf0d73f50e7b6935677854f6e8e31d499ca7064dd24734f703e060f5b237d883", size = 336998, upload-time = "2026-06-29T13:04:00.071Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8d/72cadaac05ccfa7cc3a0a2232862e6c72443ca40cf300ba8b57f9f18b69b/jiter-0.16.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3ea07d9bc8e7d03a9fbc051295462e6dbc295b894fd72457c3136e3e43d898", size = 362112, upload-time = "2026-06-29T13:04:01.52Z" }, + { url = "https://files.pythonhosted.org/packages/58/4a/c4b0d5f651fda90a24ffce9f8d56cde462a2e09d31ae3de3c68cef34c04e/jiter-0.16.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26798522707abb47d767db536e4148ceac1b14446bf028ee85e579a2e043cfe5", size = 459807, upload-time = "2026-06-29T13:04:03.214Z" }, + { url = "https://files.pythonhosted.org/packages/80/58/ef77879ea9aa56b50824edc5a445e226422c7a8d211f3fd2a56bcb9493cf/jiter-0.16.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc837c1b9631be10abfe0191537fe8009838204cec7e44827401ace390ddb567", size = 373181, upload-time = "2026-06-29T13:04:04.629Z" }, + { url = "https://files.pythonhosted.org/packages/49/2e/ffbc3f254e4d8a66da3062c624a7df4b7c2b2cf9e1fe43cf394b3e104041/jiter-0.16.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49060fd70737fad59d33ba9dcc0d83247dc9e77187de26053a19c16c9f32bd69", size = 344927, upload-time = "2026-06-29T13:04:06.067Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f6/0be5dc6d64a89f80aa8fec984f94dedb2973e251edcae55841d60786d578/jiter-0.16.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:adbb8edeadd431bc4477879d5d371ece7cb1334486584e0f252656dd7ffada29", size = 352754, upload-time = "2026-06-29T13:04:07.477Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/7d31243b3b91cd261dd19e9d3557fc3251a80883d3d8049c86174e7ab7af/jiter-0.16.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:31aaee5b80f672c1dc21272bcfb9cbdcfc1ea04ff50f00ed5af500b80c44fa93", size = 390553, upload-time = "2026-06-29T13:04:08.92Z" }, + { url = "https://files.pythonhosted.org/packages/25/33/51ae371fde3c88897520f62b4d5f8b27ad7103e2bb10812ff52195609853/jiter-0.16.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:6722bcef4ffc86c835574b1b2fac6b33b9fb4a889c781e67950e891591f3c55a", size = 516900, upload-time = "2026-06-29T13:04:10.407Z" }, + { url = "https://files.pythonhosted.org/packages/a0/45/6449b3d123ea439ba79507c657288f461d55049e7bcbdc2cf8eb8210f491/jiter-0.16.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:5ab4f50ff971b611d656554ea10b75f80097392c827bc32923c6eeb6386c8b00", size = 548754, upload-time = "2026-06-29T13:04:12.046Z" }, + { url = "https://files.pythonhosted.org/packages/9b/e7/fd2fb11ae3e2649333da3aa170d04d7b3000bbdc3b270f6513382fdf4e04/jiter-0.16.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:710cc51d4ebdcd3c1f70b232c1db1ea1344a075770422bbd4bede5708335acbe", size = 122381, upload-time = "2026-06-29T13:04:13.413Z" }, + { url = "https://files.pythonhosted.org/packages/26/80/f0b147a62c315a164ed2168908286ca302310824c218d3aae52b06c0c9a9/jiter-0.16.0-cp314-cp314-win32.whl", hash = "sha256:57b37fc887a32d44798e4d8ebfa7c9683ff3da1d5bf38f08d1bb3573ccb39106", size = 204578, upload-time = "2026-06-29T13:04:14.813Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e6/4758a14304b4523a6f5adb2419340086aa3593bd4327c2b25b5948a90548/jiter-0.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbd18dd5e2df96b580487b5745adf57ef64ad89ba2d9662fc3c19386acce7db8", size = 198154, upload-time = "2026-06-29T13:04:16.272Z" }, + { url = "https://files.pythonhosted.org/packages/26/be/41fa54a2e7ea41d6c99f1dc5b1f0fd4cb474680304b5d268dd518e81da3a/jiter-0.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:a32d2027a9fa67f109ff245a3252ece3ccc32cc56703e1deab6cc846a59e0585", size = 191458, upload-time = "2026-06-29T13:04:17.707Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/59127338b86d9fe4d99418f5a15118bea778103ee0fe9d9dd7e0af174e95/jiter-0.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2577196f4474ef3fc4779a088a23b0897bbf86f9ea3679c372d45b8383b43207", size = 316739, upload-time = "2026-06-29T13:04:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/2d/95/49461034d5388196d3dabf98748935f017b7785d8f3f5349f834bcc4ed0d/jiter-0.16.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e89e008a93c01104161c75b4988e58716b01d62307ebfe161e52a56d2a818", size = 340911, upload-time = "2026-06-29T13:04:21.257Z" }, + { url = "https://files.pythonhosted.org/packages/cd/97/a4369f2fb82cb3dda13b98622f31249b2e014b223fe64ee534413ad72294/jiter-0.16.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2e9efbe042210df657bade597f66d6d75723e3d8f45a12ea6d8167ff8bbce3", size = 361747, upload-time = "2026-06-29T13:04:22.677Z" }, + { url = "https://files.pythonhosted.org/packages/28/51/49b6ed456261646e1906016a6760367a28aacd3c24805e4e5fe64116c1db/jiter-0.16.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f4d9e473a5ce7d27fef8b848df4dc16e283893d3f53b4a585e72c9595f3c284", size = 460225, upload-time = "2026-06-29T13:04:24.441Z" }, + { url = "https://files.pythonhosted.org/packages/33/b5/5689aff4f66c5b60be63106e591dbfcba2190df97d2c9c7cf052361ddb98/jiter-0.16.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d30a4a1c87713060c8d1cc59a7b6c8fb6b8ef0a6900368014c76c87922a2929", size = 373169, upload-time = "2026-06-29T13:04:25.884Z" }, + { url = "https://files.pythonhosted.org/packages/a2/96/3ae1b85ee0d6d6cab254fb7f8da018272b932bbf2d69b07e98aa2a96c746/jiter-0.16.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae96332410f866e5900d809298b1ed82735932986c672495f9701daacd80620", size = 350332, upload-time = "2026-06-29T13:04:27.302Z" }, + { url = "https://files.pythonhosted.org/packages/15/32/c99d7bafd78986556c95bf60ce84c6cc98786eac56066c12d7f828bb6747/jiter-0.16.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:da3d7ec75dc83bb18bca888b5edfae0656a26849056c59e05a7728badd17e7af", size = 353377, upload-time = "2026-06-29T13:04:28.731Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4b/f99a8e571287c3dec766bcc18528bbe8e8fb5365522ab5e6d64c93e87066/jiter-0.16.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6162b77d49a9939229df666dfa8af3e656b6701b54c4c84966d740e189264e", size = 387746, upload-time = "2026-06-29T13:04:30.319Z" }, + { url = "https://files.pythonhosted.org/packages/75/69/c78a5b3f71040e34eb5917df26fb7ae9a2174cad1ccbf277512507c53a6e/jiter-0.16.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:63ffdbdae7d4499f4cda14eadc12ddcabef0fc0c081191bdc2247489cb698077", size = 517292, upload-time = "2026-06-29T13:04:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f7/095b38eda4c70d03651c403f29a5590f16d12ddc5d544aac9f9cddf72277/jiter-0.16.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a111256a7193bea0759267b10385e5870949c239ed7b6ddbaaf57573edb38734", size = 549259, upload-time = "2026-06-29T13:04:33.721Z" }, + { url = "https://files.pythonhosted.org/packages/2e/c5/6a0207d90e5f656d95af98ebd0934f382d37674416f215aeda2ff8063e51/jiter-0.16.0-cp314-cp314t-win32.whl", hash = "sha256:de5ba8763e56b793561f43bed197c9ea55776daa5e9a6b91eed68a909bc9cdbf", size = 206523, upload-time = "2026-06-29T13:04:35.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/31/c757d5f30a8980fd945ce7b98be10be9e4ff59c7c42f5fd86804c2e87db8/jiter-0.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b8a3f9a6008048fe9def7bf465180564a6e458047d2ce499149cfbe73c3ae9db", size = 200366, upload-time = "2026-06-29T13:04:36.61Z" }, + { url = "https://files.pythonhosted.org/packages/7c/a2/d88de6d313d734a544a7901353ad5db67cb38dcfcd91713b7979dafc345d/jiter-0.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0fa25b09b13075c46f5bc174f2690525a925a4fc2f7c82969a2bbabff22386ce", size = 190516, upload-time = "2026-06-29T13:04:38.004Z" }, +] + +[[package]] +name = "json-repair" +version = "0.63.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/1b/c23bee35d532ea9c2038f31245fd2b50e6a8308d38a8b965bf0f9c6d49ba/json_repair-0.63.2.tar.gz", hash = "sha256:8385ca04afbf411eebd9f0ba064155d1afe37442aaad52f693825c65cc314586", size = 52426, upload-time = "2026-08-14T07:39:27.637Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/fe/edc09f67290861ab6b97dfd530189ae07092a708e90abe5a0e55f863f35c/json_repair-0.63.2-py3-none-any.whl", hash = "sha256:7354c2dd433bf15dedf98d2932ae9cd1ea197d7edc77e6c0538f08e34616580e", size = 50916, upload-time = "2026-08-14T07:39:26.339Z" }, +] + +[[package]] +name = "lefthook" +version = "2.1.10" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/a7/ff54e6b498de6daa9ca5f02e77f943923924ae6aa2d0babc0334507bd0e2/lefthook-2.1.10-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:8ae438e6a6a2984e7f7e1eef43ae08a5a1f99999a75f911f52e33106f1b198ff", size = 5424203, upload-time = "2026-07-08T12:03:27.993Z" }, + { url = "https://files.pythonhosted.org/packages/93/1d/12274ace64319fe26ed6f394dae463ceae24f504d751d84072cb3bfdbacf/lefthook-2.1.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f468de23f627837ee822beea57473c3ed541fc8bf82b030a3247d773a192b4f", size = 4946329, upload-time = "2026-07-08T12:03:29.912Z" }, + { url = "https://files.pythonhosted.org/packages/fc/3f/cbd5bcf3d481a8ffda0378b6e90bca3c85f060c085e16002837ceeab56b6/lefthook-2.1.10-py3-none-manylinux_2_17_aarch64.whl", hash = "sha256:115c6b4ba6dd12dc18d8661c61992d46dcfab1e0528f16b6a90664c4fe60c376", size = 4776557, upload-time = "2026-07-08T12:03:31.437Z" }, + { url = "https://files.pythonhosted.org/packages/0d/52/54798a744c4d656ba39ed5f0e6ccb8a81c6a4c15a3e1448978a40aefd713/lefthook-2.1.10-py3-none-manylinux_2_17_x86_64.whl", hash = "sha256:a635411b4e9263154fd5a64a81df28c628d625ab18c7f071df90e7006d0de748", size = 5356418, upload-time = "2026-07-08T12:03:33.827Z" }, + { url = "https://files.pythonhosted.org/packages/11/b9/fb10fab6e854e97ed2af8c906ff90b706e850f17d245c6cde59c076e4066/lefthook-2.1.10-py3-none-win_amd64.whl", hash = "sha256:7385e234f14ab65bcaa84ebe7ba6d6b1dafe36be6ad5a9dd852e5add6f1e701e", size = 5506468, upload-time = "2026-07-08T12:03:35.826Z" }, + { url = "https://files.pythonhosted.org/packages/77/01/054a0a18154dd8127276398a82d9797dd5534ebeeb1d8d61a9b19e6a8ef0/lefthook-2.1.10-py3-none-win_arm64.whl", hash = "sha256:be7b59b9e3483f62a217a632cb4177eaa3f75229916450bd12b99b608c51439d", size = 4858134, upload-time = "2026-07-08T12:03:37.421Z" }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, +] + +[[package]] +name = "lxml" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/3b/aab6728cae887456f409b4d75e8a01856e4f04bd510de38052a47768b680/lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40", size = 4197430, upload-time = "2026-05-18T19:19:06.424Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/eb/7e6f37c5584ccbb2ff267f56fd0339016938c1c8684cfefab9b33ffc2f36/lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736", size = 8559780, upload-time = "2026-05-18T19:17:57.661Z" }, + { url = "https://files.pythonhosted.org/packages/a1/36/587c2521cf23a2cd6c9c22108aa7528f683a1f195ed7ccd23a4b1786ad36/lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9", size = 4618006, upload-time = "2026-05-18T19:18:04.452Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ca/ab7bfe2bf4c972af5e7878262845ead3a24a929a9b04bc11c7c1ece6c82a/lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354", size = 4924139, upload-time = "2026-05-18T19:19:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6b/55/a0c72851dfee5ecc689f949723a73dea457758912542cb955b108eaf0d8f/lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca", size = 5082329, upload-time = "2026-05-18T19:19:09.728Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b6/0608f7d61a3b96cc67e5648a3d906e31a5082093e10e7be65b3886289938/lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099", size = 4993564, upload-time = "2026-05-18T19:19:13.608Z" }, + { url = "https://files.pythonhosted.org/packages/4c/66/ae227524b066d29d55bf0b453d93d2d793c40218657d643dcbbca13b8faf/lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6", size = 5613467, upload-time = "2026-05-18T19:19:16.228Z" }, + { url = "https://files.pythonhosted.org/packages/a6/76/dbe4a00b50385e40194231dcfe5a12c059de7cf90e89c83407d2b085b719/lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085", size = 5228304, upload-time = "2026-05-18T19:19:19.354Z" }, + { url = "https://files.pythonhosted.org/packages/1c/01/00b1b8442ed2041793336868ba0b9ea4b13d7da7c085c6404c207a63bf79/lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e", size = 5341607, upload-time = "2026-05-18T19:19:22.297Z" }, + { url = "https://files.pythonhosted.org/packages/63/36/1ad29931e9a4638bb707869f01d423a6c815f82152138d1a40dfcfde2b95/lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f", size = 4700168, upload-time = "2026-05-18T19:19:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d1/a9536cecf9be18a0dc72d32bead283a2332d1ffebd2dd3ac70ce444686e5/lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c", size = 5232487, upload-time = "2026-05-18T19:19:28.603Z" }, + { url = "https://files.pythonhosted.org/packages/0e/77/b4fb1e03bf5d130e879214d3100092e386418807fb74dd0adc4b0a48f351/lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b", size = 5044231, upload-time = "2026-05-18T19:18:42.246Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/d00daeeb0a5530c4028a9232aa1b93db3ef4ed2158c116ea73c79a9765b3/lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2", size = 4769450, upload-time = "2026-05-18T19:18:48.013Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6a/715a3a8d156ce42f29cf014706f5410c2ff3b02267774110fc23266409fe/lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5", size = 5635874, upload-time = "2026-05-18T19:18:51.914Z" }, + { url = "https://files.pythonhosted.org/packages/45/37/0544bc21dde2a88f3a17b504e6fc79c0e01d25a33c2f6079724e9e72b9c7/lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785", size = 5223987, upload-time = "2026-05-18T19:18:59.715Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f8/f6a5e8185bcb28c2befae3d31f8e3df3b811cb0f47746517a81279fcafe1/lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947", size = 5250276, upload-time = "2026-05-18T19:19:03.834Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f2/1a2b9f1b7a49d45495369be7ef9ad05b262930f2eab3e3145706fca8083f/lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca", size = 3596903, upload-time = "2026-05-18T19:17:29.863Z" }, + { url = "https://files.pythonhosted.org/packages/e6/99/f4ffb024f238eec2131aaa09f3278fb6129cf892741bf68e1fc1afb8c100/lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660", size = 3995869, upload-time = "2026-05-18T19:18:02.596Z" }, + { url = "https://files.pythonhosted.org/packages/d1/53/70eb8c5c6037f27448f1e3c54ebede9545a801ae63f0a7254afca4fe8e45/lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc", size = 3658490, upload-time = "2026-05-19T19:22:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/13/e2/2e325795566de01d0d7c3bb57d3c370616b2d07b01214e84eec5d3b10963/lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0", size = 8577146, upload-time = "2026-05-18T19:18:17.765Z" }, + { url = "https://files.pythonhosted.org/packages/93/cf/5630b5e4be7d2e6bee8efe83865c925221103cf0221303b104ce134b01e2/lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840", size = 4623866, upload-time = "2026-05-18T19:18:30.669Z" }, + { url = "https://files.pythonhosted.org/packages/d2/51/3904907c063451cf8d4a5c9fe0cad95fa1f4ec57f4e3884fa0731bd7a305/lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14", size = 4950022, upload-time = "2026-05-18T19:19:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/94/cd/9c7611a51c37a2830928405817cc5d56a97f64fab83cc3f628748b135749/lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909", size = 5086695, upload-time = "2026-05-18T19:19:34.764Z" }, + { url = "https://files.pythonhosted.org/packages/da/d6/24e3b5906abb0b674ff2ae195bc3ce59708df2bcd17cf17703b2d7dd643a/lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00", size = 5031642, upload-time = "2026-05-18T19:19:37.771Z" }, + { url = "https://files.pythonhosted.org/packages/2d/db/6ec54f99019838bff54785c51da07f189eb4676861c5f2730962b0d8d665/lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955", size = 5647338, upload-time = "2026-05-18T19:19:40.553Z" }, + { url = "https://files.pythonhosted.org/packages/42/3d/ef4dcfffd22d27a61805d8ed9f7fb888495bc6aa88648fa07c1eaa5586b6/lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13", size = 5239528, upload-time = "2026-05-18T19:19:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/62/bb/37fb3f0dff146bdcfa78eec47879273820b2a0bf350ec236ce14bd0b1c26/lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7", size = 5350730, upload-time = "2026-05-18T19:19:46.307Z" }, + { url = "https://files.pythonhosted.org/packages/90/42/43253f168388df4fae1f38c01df36ddb9bee39e2048167b54cdcbae85ea3/lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245", size = 4697530, upload-time = "2026-05-18T19:19:49.889Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a8/c5a8504f81bbdfc8e7094c2c850cdb4ed6777fc4d5ddd9e5ab819f3b0d54/lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5", size = 5250670, upload-time = "2026-05-18T19:19:53.199Z" }, + { url = "https://files.pythonhosted.org/packages/77/b7/c7e76ab18744d75e21f320ebf9ff9d1ceae2b54dd431ea5a64caf26c9672/lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462", size = 5084485, upload-time = "2026-05-18T19:19:08.422Z" }, + { url = "https://files.pythonhosted.org/packages/31/31/b35c53f8ef7b7c31cacd23d3638652fff7bcd1deb6eedb709ab43b685908/lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465", size = 4737635, upload-time = "2026-05-18T19:19:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/d9/06/31f23c813a7fe8e0cb1b175e915b08c9bf4e86d225b210feadbdbe519667/lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a", size = 5670681, upload-time = "2026-05-18T19:19:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bc/ce619bccc89b1fd9ad8a8e1330ee3f3beff9f2ff95b712d7bbcdd6e22fc3/lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590", size = 5238229, upload-time = "2026-05-18T19:19:18.131Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5d/b329acbbedc0b619ebc2be6cf7ee9ed07e80892c88d4dfd612c33805789a/lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb", size = 5264191, upload-time = "2026-05-18T19:19:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/d6/85/be36fb1425b30db3c3f9df75fe86343ebffb79e6320bd7f588e25bfeac39/lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603", size = 3657202, upload-time = "2026-05-18T19:17:39.509Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ce/3cf9a827342269f54d405a6202397de63f07c69cbd6ce7d183a3f0cba1e9/lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137", size = 4064497, upload-time = "2026-05-18T19:18:14.662Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3e/1a957bde8f0760039e627f94699f82caa782c9d838d86c3d28245ee67212/lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf", size = 3741991, upload-time = "2026-05-19T19:22:59.111Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/00ed55b3a2efa4658fb795c38d1090ec9b3e8a6c3683d4441fa517f09c3b/lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee", size = 8827545, upload-time = "2026-05-18T19:18:41.193Z" }, + { url = "https://files.pythonhosted.org/packages/c0/73/74573db19baa618d5f266f2407898b087ff6927115b00b71e5fc1b700847/lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c", size = 4735736, upload-time = "2026-05-18T19:18:46.761Z" }, + { url = "https://files.pythonhosted.org/packages/16/02/6f7061f4f95f51e545d48e87647c54791d204a4e881be4156e7a26ba5338/lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef", size = 4970291, upload-time = "2026-05-18T19:19:56.215Z" }, + { url = "https://files.pythonhosted.org/packages/b0/02/55fc057d8283427dea7d6edb102e7a840239c77a64a983d92f62a304c0e9/lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a", size = 5102822, upload-time = "2026-05-18T19:19:59.223Z" }, + { url = "https://files.pythonhosted.org/packages/e4/48/8e1cf78d89d66850121d9255a2a24414c98f775da93b90cf976956c24b14/lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c", size = 5027923, upload-time = "2026-05-18T19:20:01.549Z" }, + { url = "https://files.pythonhosted.org/packages/ed/00/0632a0647612c8af24d26997b3b961397daa9d5b2581444805933629a4cb/lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525", size = 5595843, upload-time = "2026-05-18T19:20:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/bc/86/ab008a7dc360711b66858d61c80a5979a70a09f2aa2b05d9698df80b803d/lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca", size = 5224515, upload-time = "2026-05-18T19:20:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/75/c6/2702ff375e728e34f56d9a45339a9cf7e4427e917f542225242d63a05afa/lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e", size = 5312511, upload-time = "2026-05-18T19:20:09.308Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/a5807c98f87a86f10ef9ffab35516df7c0f0c4b6d5d33e9f608ab9c04a31/lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038", size = 4639206, upload-time = "2026-05-18T19:20:11.704Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e1/8a0a2c35734812395f4da4eaf33748a7e5705bfb2a58b128da764339d5ec/lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e", size = 5232404, upload-time = "2026-05-18T19:20:14.064Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/0e6a4dd5ad84d01d99aa7bae7cfefd4a760a0e0f8176818241de17d9b6c0/lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072", size = 5083769, upload-time = "2026-05-18T19:19:23.758Z" }, + { url = "https://files.pythonhosted.org/packages/a0/7e/161f33d463f6ffc1c7679104b65086dea120080d49dde4d238f015aaee2f/lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52", size = 4758936, upload-time = "2026-05-18T19:19:27.256Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fb/2369825e3f6ca99305bf9f7b7085fda91c8b0922a89e54d900974aa3ef85/lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b", size = 5620296, upload-time = "2026-05-18T19:19:29.993Z" }, + { url = "https://files.pythonhosted.org/packages/30/90/d61e383146f74c5ab683947ea14dc7b82778838ab9b95ea73a23b60d0191/lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2", size = 5228598, upload-time = "2026-05-18T19:19:33.523Z" }, + { url = "https://files.pythonhosted.org/packages/76/2d/2dafd8149e94b05bb070690efd5bb2680720681e03ff03fc57d2b70a1105/lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e", size = 5247845, upload-time = "2026-05-18T19:19:36.649Z" }, + { url = "https://files.pythonhosted.org/packages/ce/68/b30e913340c380ddac9580c6e6230991fc37240ec4f64704833e4f3e2769/lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1", size = 3897345, upload-time = "2026-05-18T19:17:33.562Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4e/9eb2af5335545f9fbcd7af57bcf87c6025d31eaa31b14ec184a6c8675328/lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e", size = 4393350, upload-time = "2026-05-18T19:18:10.076Z" }, + { url = "https://files.pythonhosted.org/packages/7f/2c/0f1e93c636720e8a3eb59af2bfda99d98b55891e1c53bc30c2e0e865f01b/lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", size = 3817223, upload-time = "2026-05-19T19:22:56.823Z" }, +] + +[[package]] +name = "magika" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "onnxruntime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/be/fa7d512ec15763ad7d8ba083e39bb445fa969ce5159361fa449a02569cf9/magika-1.0.3.tar.gz", hash = "sha256:ad3216012f6dd337be34c23ae3dfab36f0623bc62fbfb329f9a1852c9ad40304", size = 3041970, upload-time = "2026-05-04T08:41:53.124Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/eb/24d94db0530029649b266ec3ca8221c07f2754f56046181f13237d2518f5/magika-1.0.3-py3-none-any.whl", hash = "sha256:938d8e033953f2ddeb8c35dc423aa289ca116bfa7a71a778f6e77460f9025803", size = 2969548, upload-time = "2026-05-04T08:41:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/23/be/9d7c34b53ff1da4f43224c109eb1f8bcb9ac335cc948d2f5359ba7f788f3/magika-1.0.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:de9af9c96892e610eefc920cca9e71179661d39d9d1d7acc1f28f357bcc8805e", size = 13863615, upload-time = "2026-05-04T08:41:46.734Z" }, + { url = "https://files.pythonhosted.org/packages/d6/78/4f341c974130a464f08813d54113ed9e63d5e9c963fbe398300838922a67/magika-1.0.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:3e9b49134e8116ee40b431664dcbed9e199413efddcce1a173c734b4e0521529", size = 16244594, upload-time = "2026-05-04T08:41:48.763Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/d414195bab93835ce764cb7471e7bf94e0eed396e455e1ff14de7f17d2de/magika-1.0.3-py3-none-win_amd64.whl", hash = "sha256:8a817588c177e81efadf2efa3690bfd46000279fd32e5bfc814776b007ee5d89", size = 13057718, upload-time = "2026-05-04T08:41:50.884Z" }, +] + +[[package]] +name = "mammoth" +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cobble" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/92/aca6c4208b3a8e56a788b224e6c9b7bb70fc68e6641124fcf784008338e1/mammoth-1.12.1.tar.gz", hash = "sha256:40521e02568583b671d9976b0fbeed50be734b40f73ca8e1939ab7146bf69bbe", size = 53797, upload-time = "2026-08-09T14:11:20.096Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/b4/e7907b278386ffec0cf631db93836e02de9a6386add4653a52d7ac6b43ae/mammoth-1.12.1-py2.py3-none-any.whl", hash = "sha256:2af047e3e796faa25740112310ddf11f8de2a24c96dc57de3c87dfd7cb6543b3", size = 55091, upload-time = "2026-08-09T14:11:18.854Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mineru" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "click" }, + { name = "fast-langdetect" }, + { name = "fastapi" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "json-repair" }, + { name = "loguru" }, + { name = "lxml" }, + { name = "magika" }, + { name = "mammoth" }, + { name = "mineru-vl-utils" }, + { name = "modelscope" }, + { name = "numpy" }, + { name = "openai" }, + { name = "opencv-python" }, + { name = "openpyxl" }, + { name = "pdftext" }, + { name = "pillow" }, + { name = "pylatexenc" }, + { name = "pypdf" }, + { name = "pypdfium2" }, + { name = "pypptx-with-oxml" }, + { name = "python-docx" }, + { name = "python-multipart" }, + { name = "reportlab" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "uvicorn" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/a6/d60b161482cdcc2a4a8d01994320c55b5f804e26fda07845440a9d3080a8/mineru-3.4.5-py3-none-any.whl", hash = "sha256:4a73b865920bb9109c1b8b1bc46567e296bf0133a67106a04effd219536ae72d", size = 1541914, upload-time = "2026-08-14T07:55:46.975Z" }, +] + +[package.optional-dependencies] +pipeline = [ + { name = "ftfy" }, + { name = "onnxruntime" }, + { name = "pyclipper" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "shapely" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "transformers" }, +] +vlm = [ + { name = "accelerate" }, + { name = "torch" }, + { name = "transformers" }, +] + +[[package]] +name = "mineru-vl-utils" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiofiles" }, + { name = "httpx" }, + { name = "httpx-retries" }, + { name = "loguru" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "tqdm" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/93/5f7917231b3f9349f1b1477b6206a5f6d984ef51bc78fd315a7074a95cda/mineru_vl_utils-1.2.1-py3-none-any.whl", hash = "sha256:9129b987c7b942af4c38b555a1cf41fa660f4efae222acd602e61f9a94ce27df", size = 98303, upload-time = "2026-08-13T11:55:07.65Z" }, +] + +[[package]] +name = "modelscope" +version = "1.39.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "modelscope-hub" }, + { name = "packaging" }, + { name = "requests" }, + { name = "setuptools" }, + { name = "tqdm" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/ae/5118b6b8cea49c4071dc54dba80e63ed20da920bde388bf1ed7916f5cd5c/modelscope-1.39.1.tar.gz", hash = "sha256:40cc02051e4cd67931c70f1956b2a22efc1b35f2a432b5110204406d4a28e17f", size = 4548222, upload-time = "2026-08-04T08:27:03.936Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/c5/ff578b44a7ba5d86edfaae1ce7db9082f95477bd22ccb2514c1aee4636b7/modelscope-1.39.1-py3-none-any.whl", hash = "sha256:c1567b9d319b11d815832d13c11f71a6413b19cf6374cef6937c54d830552a97", size = 6038125, upload-time = "2026-08-04T08:27:01.121Z" }, +] + +[[package]] +name = "modelscope-hub" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/9d/3fabfd2b147d94e1980d670fecd3c183999db04a71b80a546bcf37cc6f89/modelscope_hub-0.2.0.tar.gz", hash = "sha256:00288d3e08ad7fadfdd7539f4dffa9d75f9006c43c73abd33ea51767317f001c", size = 163316, upload-time = "2026-08-01T15:00:08.694Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/be/3b0e14c296a622c920287fc6d2c1b5a8668c8e0d98662f7b2bd8f3fcc779/modelscope_hub-0.2.0-py3-none-any.whl", hash = "sha256:84668c80c1d36d1fb1ea1ce17b707d77e044b92255288618c3b7de5b13834418", size = 156023, upload-time = "2026-08-01T15:00:07.335Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/80/db0b4559e57ec36362bedbb05530a87fafbcb6067708c946967a41d449e7/numpy-2.5.2.tar.gz", hash = "sha256:d482d171c406ae88c5b19cad3b6a1c4c5209f886ab74bc44c2c865c23f52d860", size = 20773161, upload-time = "2026-08-09T13:48:27.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/d2/6b24738a0ef4557d189b150046cd07823c50e4273e8aebd651222e24306f/numpy-2.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8e4cb9a754c8a0c62eaa88273a5fba3391f4a610d1dee893c0755da31c083f15", size = 16886595, upload-time = "2026-08-09T13:45:27.323Z" }, + { url = "https://files.pythonhosted.org/packages/65/60/f2d208d366f263f39c6e69ed309290717aab41078b6d04c9be2a84fa2a07/numpy-2.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52c808f96484f5571a5cc863775ce50247c17dfb3b0361f8ed6b4b0456f80080", size = 11896845, upload-time = "2026-08-09T13:45:31.638Z" }, + { url = "https://files.pythonhosted.org/packages/3c/79/81e0bf24f4d020a2b1d5cd297a9f60c3f24eeb116f9bba5870443f7b6a4a/numpy-2.5.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:29d81e97f668489cba8ebfd796b9bdd453525d35dd9e162e2daec94bf3fc7740", size = 5343880, upload-time = "2026-08-09T13:45:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cc/e3141cf06d1a8a2c7e107543fe1269c1d1af760d4d683c0794a4ee1127c2/numpy-2.5.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afb3f0632d6b2e3ba04dbce8d1e48d321b369138b73830b5ca371a0e8d479d56", size = 6682264, upload-time = "2026-08-09T13:45:36.7Z" }, + { url = "https://files.pythonhosted.org/packages/29/f1/2a64a307d92c5d98f5255a4014eb43bb6103ee477087b61ecae44a3aa9b9/numpy-2.5.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0aadf13b60048d501e05fa699efaf7734e2494f3498a4c2a5521d822640324f3", size = 15609566, upload-time = "2026-08-09T13:45:39.518Z" }, + { url = "https://files.pythonhosted.org/packages/7b/44/59a1eb68e773c4098d107ef34a0dbdeca501d72ffcfbff9a7707343921ce/numpy-2.5.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29b86ff8a6cc556b47ec6b64b194815cc80e6bf5eedcc6cddfd65318cb0b4eee", size = 16709995, upload-time = "2026-08-09T13:45:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4c/3e54d4ddbc359a1295f8b633e8106bcd4d7d4a206e82df051bdfb3058755/numpy-2.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6950c4b7dd562453090548ba7f5da7e59f57f85663f15d5dcc60e249192f7e59", size = 16972511, upload-time = "2026-08-09T13:45:47.094Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9f/02e371638ebf19b66d46231e4be52999e87f32d1961b113bc45656608b22/numpy-2.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9727f472d2f3888053b8a75ab0cb94745a9de224bb5846dbadc0092101bc71d", size = 18465609, upload-time = "2026-08-09T13:45:50.808Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/ad6645abc7a3510fe48e8ea1ab4598166f500057ef4ebf38bfad4f1577de/numpy-2.5.2-cp313-cp313-win32.whl", hash = "sha256:4f9744f9fbdcea0bc552e8f19e1f141f811a3f9bc2be2cc6e86d982cab23e3f4", size = 6070204, upload-time = "2026-08-09T13:45:54.111Z" }, + { url = "https://files.pythonhosted.org/packages/15/20/f3489f86d81ea460b2bcdceaed094142ca6579f6be0ec527b781d39afe68/numpy-2.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:85aaccb24182c25df891ad0ec333585967e115269d5f1b17f2c9ae005bc96657", size = 12460532, upload-time = "2026-08-09T13:45:57.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/35b31dde1b283b79de828b80f876afd8c94e28fe1e9c375f89e261cc4c0d/numpy-2.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:bd68ece1553d2023c09a4226d9e41c586ad2d20594d1a456186c33513d2cb3f2", size = 10396725, upload-time = "2026-08-09T13:46:00.478Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f8/c3b222bf075b50afd8e949a07a15c4b312a4a84bd8102a332bcd953cbbb4/numpy-2.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d787cf769c3baeb5f6235e778edb52c08dfa923789b5958f28e6450f96107cb1", size = 16885180, upload-time = "2026-08-09T13:46:03.939Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/2c1d4b1987795a92b5bbf7c24fe249ab96aa2573ab0d7604802c189d7b86/numpy-2.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:24b9dc2e3d84aa58523798805194e23e736f3f6ce2d1a5b92583ae734e6dbda8", size = 11907878, upload-time = "2026-08-09T13:46:07.045Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ee/d08226fc858044355983a6e5b94f08ff6f3969e0a2b160a4a89f0ddb3445/numpy-2.5.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:9e9413326d726c2545bfa65d2c0876871e8d8386e77f992c1d426e180bbd4323", size = 5354922, upload-time = "2026-08-09T13:46:10.04Z" }, + { url = "https://files.pythonhosted.org/packages/94/f0/6d3d933056440ebbc5e6bad92065fc6c26a48a84a36b1208580e94eea76c/numpy-2.5.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:60e902ac295855348a5ca2ea4c89108989a9f5fddfad3dfc0a8f36b10358567e", size = 6679168, upload-time = "2026-08-09T13:46:12.275Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3b/ecd49dd90033cceb2704d88ca905d4d7d89b0e8c739608754ffd325fa820/numpy-2.5.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e500dc868e9313530ce12ba470fe50ff3afe3d62993ed6eff652dacd555b65", size = 15624501, upload-time = "2026-08-09T13:46:15.322Z" }, + { url = "https://files.pythonhosted.org/packages/c7/99/461bd36dbdfac6c1c53efa370bd55a83227542d0d118f1677dbf1a3dacd5/numpy-2.5.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318b9a4c845dbea06708a29c84ee429cc3065048db34cdb799047643492050ee", size = 16713701, upload-time = "2026-08-09T13:46:18.949Z" }, + { url = "https://files.pythonhosted.org/packages/f9/9c/2b251df9e8a5d647b62b0cbc1b90a91850c1cf4859ecb532fd0b4eacff6c/numpy-2.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:34c319e2963be042673fb46570501b2f06c41924e17e3563d58646b4380dfb68", size = 16986065, upload-time = "2026-08-09T13:46:23.006Z" }, + { url = "https://files.pythonhosted.org/packages/8f/25/20de43f53ff1390534a124475055a19f01fe10c920a0fd11b8e18d6d6052/numpy-2.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f06571a052127dc1b4e8b83029b4d1b20daa2b64a31cdd181fc6bc774e9000eb", size = 18470031, upload-time = "2026-08-09T13:46:27.102Z" }, + { url = "https://files.pythonhosted.org/packages/56/5e/0c577ca308d6da5eb79b546ba10bbe5b60148192194e2da060913b1de4f1/numpy-2.5.2-cp314-cp314-win32.whl", hash = "sha256:2cc779226e476d1e1f08c74068c419e60f41a9e0e069c92f6671d31d5c985e98", size = 6121028, upload-time = "2026-08-09T13:46:30.046Z" }, + { url = "https://files.pythonhosted.org/packages/15/5c/7bcbd5b11f94199073320410cddcbb80cee62415bfeb540874b265c2d922/numpy-2.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:7587f53dfbd5edc0f7b87c6217b4c6d2d1f2ef9c3da70bc1315e7db5f8d7ec9d", size = 12597627, upload-time = "2026-08-09T13:46:32.886Z" }, + { url = "https://files.pythonhosted.org/packages/87/bc/4d0b06fba0da90ccc75af62823cb9dcedb6c9ea0cffa058cb2c9ee773a77/numpy-2.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:3e4c367352d3747784248a227fbec218e193b56f7e6692e3b64fc805478ecfdf", size = 10680414, upload-time = "2026-08-09T13:46:36.036Z" }, + { url = "https://files.pythonhosted.org/packages/cd/17/f429aac9dc08833a0d0f188eba38c532a751b1a1f2ca6018a37b455cb321/numpy-2.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b879fb674276e331513fb136b78dbc6bd3c848309e0d841cfd63be3896c4cfc1", size = 12026967, upload-time = "2026-08-09T13:46:39.084Z" }, + { url = "https://files.pythonhosted.org/packages/ca/9f/d0849de96a2a4ceaa16662f18ee13eaa9c0aa418269fdc8c4857c56b11da/numpy-2.5.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f", size = 5473874, upload-time = "2026-08-09T13:46:42.075Z" }, + { url = "https://files.pythonhosted.org/packages/89/3c/8df216d4a4a5422a3de045301cf7df8ea47286d76f5cb7160b0128ac26b7/numpy-2.5.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:3a2f061cebd9e3d23bdcfaaded5e2293a4c6a5b60fa42df85d410a725ce621bf", size = 6789276, upload-time = "2026-08-09T13:46:44.387Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3a/20d7e9891c4ddfadd6ff8d95bf4b29f353d8e1770553de2099880551dfb9/numpy-2.5.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6df895598c0edcb41030126c89e0f353b07d93238116143b7405e937359736c4", size = 15659154, upload-time = "2026-08-09T13:46:47.538Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d6/f3aa3d2688bf501b858835c6bd087ae9b51a56ae6fca8e2b0990abd177af/numpy-2.5.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ab3d4a901f844ea836c3e80bf463c6a27d7f3c14e8e292fcf28d348b25b9bce", size = 16748909, upload-time = "2026-08-09T13:46:51.442Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8f/1c5cae8d2baf86ab802ae97a00be55bc7e21ebc11b12bbc33376c5f05342/numpy-2.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:cebc2d6dbb605a7703d59751dea4bd6b0ab127a5a4338a6f432df1936fef8b26", size = 17027685, upload-time = "2026-08-09T13:46:55.095Z" }, + { url = "https://files.pythonhosted.org/packages/5c/27/71d3467404aedc1c24ce79610f91b52b0b0f466c43a701aa56fc75c145ab/numpy-2.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eaca7ff36f0f52e2111ec71f169d8fd3e889e7ddc0d2592e0d703fd8d3ce8fac", size = 18501181, upload-time = "2026-08-09T13:46:59.09Z" }, + { url = "https://files.pythonhosted.org/packages/14/2f/42921d27c40aea7e077f4a423ae509fd9220b028cd787bafefd8ab2b3a5f/numpy-2.5.2-cp314-cp314t-win32.whl", hash = "sha256:ddf47472af2e4280d79bac82304f5e80150211f1b9e614b760061d5fdfbb6eba", size = 6271085, upload-time = "2026-08-09T13:47:01.903Z" }, + { url = "https://files.pythonhosted.org/packages/75/e6/bad5f5d56de9b1971bac959963dda276d35c40f1854475005434bbe08692/numpy-2.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:44ef9675d908e65f9953063837c3277730f3f4437615a4cdab67b366cabaf884", size = 12787971, upload-time = "2026-08-09T13:47:04.963Z" }, + { url = "https://files.pythonhosted.org/packages/df/05/f608795cb34391acd67e38d94a3c36abd8d8576293a3a80727d7595c372c/numpy-2.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:eaa088384c46f519dacb93b7ec483a6d6b19a4a2085ae4f25ab9b1c43d387d1e", size = 10750306, upload-time = "2026-08-09T13:47:07.976Z" }, + { url = "https://files.pythonhosted.org/packages/33/c6/28de0191c5f82b7d42a0a51390ba98587048aa93a39fafb05bdbe6e8d00c/numpy-2.5.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f", size = 16885274, upload-time = "2026-08-09T13:47:11.439Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/973ca116000d244897e468ea1aff30b589e5022e3c8744b71706fe33bd57/numpy-2.5.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:50a68f4bacd8a2b33d8da3d2269d0d78500f86ea582e4786dc10f5ef2c2c6842", size = 11907846, upload-time = "2026-08-09T13:47:15.128Z" }, + { url = "https://files.pythonhosted.org/packages/78/d9/8c4b3937ef204cb2fd88d389ccd0f265a2ffb11f35a01d2064cf46714bd6/numpy-2.5.2-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:e79aba74ffaf5f78a050d777c184cddf8fdffabab38acf5f3ef1fecbc17895d6", size = 5354892, upload-time = "2026-08-09T13:47:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/b6ee65ea2999fdb7023935e108e6fb776ee4082aa15f159acfa857e578c8/numpy-2.5.2-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:9a0731745a72a184490a582fb4af2533512bd071ace67785b5fdffc0ae58dce8", size = 6679309, upload-time = "2026-08-09T13:47:20.456Z" }, + { url = "https://files.pythonhosted.org/packages/43/f3/acb18d8b137a393c8e7803a8c994c9e64bde3930692a69d826993113a159/numpy-2.5.2-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ec954036759bcee3aa484f8603bd9c14f3e776293b85578b8734c2d72777c69", size = 15625850, upload-time = "2026-08-09T13:47:24.365Z" }, + { url = "https://files.pythonhosted.org/packages/a9/bf/a8e9bb0db815a0e265b5744ebedd3af0bd5faad8604e5b50a1cd012f3c91/numpy-2.5.2-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc649493697006bc90614a5f0bbc8cb3cb1866715c474e473694968d7e6b99ab", size = 16713664, upload-time = "2026-08-09T13:47:27.965Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/6e913736b3dd6582344af32418b5fb9dab34282e8a8174ae1d54ceb0fc13/numpy-2.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:cf7de32f486e4ac9e2d93b810f9e9ac72a728dd46a32a0bb403222f27f653514", size = 16986749, upload-time = "2026-08-09T13:47:31.541Z" }, + { url = "https://files.pythonhosted.org/packages/80/09/7d3b23eff5c7428ef6c01e6f7052bb60d504c4d33e317b36b8959c24ad97/numpy-2.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2ffa7bacab3e2ee1b19ed31766bb60bb380b68c23f051e199c5cc598afd68710", size = 18470495, upload-time = "2026-08-09T13:47:35.364Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a4/68a321d825374f6eb677ffe8ef8c6b9a328304e6fd2e39d9530822776607/numpy-2.5.2-cp315-cp315-win32.whl", hash = "sha256:6b588cc8f902d6bff201c19fd00c43ab8545671e3554d014e12e14139e5e8617", size = 6120696, upload-time = "2026-08-09T13:47:38.561Z" }, + { url = "https://files.pythonhosted.org/packages/c8/23/deafbb1700f79fae9cd1e91220f133d124cc267de1b584da3fbf6db2f6cd/numpy-2.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:07d4e89f3a9ab0a9ba24264ccdb642b3dd951b2281e8883a5481a4aa79cc31a7", size = 12597324, upload-time = "2026-08-09T13:47:41.401Z" }, + { url = "https://files.pythonhosted.org/packages/33/cd/3272ba105e3bbbdaeb11357eda31e7a6825ffe159e8171665660299a948f/numpy-2.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:a610dc7e3c52edd39c2bc2375ff9c3fd59cb3ad00e4472d36f83bc1457145788", size = 10680466, upload-time = "2026-08-09T13:47:44.873Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0e/58370637b1bb70a5c9ce2b43f4b521ccb224e36ccb76a6596b17ae4b447c/numpy-2.5.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:40f4d451aed46a8046a1aae41c4e55fb3612273df9c502480135e1501576a34b", size = 16993947, upload-time = "2026-08-09T13:47:48.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/93/2abcb807712b289d6d60fe4cf30532f98974a8396d885650f3ba5a13026e/numpy-2.5.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c081cbe16ba1ab53078e5ff29013621e33c509eedab055775d956427712c236e", size = 12025331, upload-time = "2026-08-09T13:47:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3a/2898e003a5fbaf87e76c039b4ee1f5eb390471b4ffe74887c1f34c4e791e/numpy-2.5.2-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a", size = 5472336, upload-time = "2026-08-09T13:47:55.403Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/23f69d07c544597b29758b31b55c27dc9d541012a2c1496189fef702aec2/numpy-2.5.2-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:6a9bb119fb8dd21ba30b3f0e555b7e2b081bd9883af21ec9c1c633d161cda3a8", size = 6788387, upload-time = "2026-08-09T13:47:58.192Z" }, + { url = "https://files.pythonhosted.org/packages/15/ea/c0dbdbcf22f43782510a3e492dd3da73c6112b69cac8929d16d127536fc4/numpy-2.5.2-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a839318485284a6fb31be4f8f2c91c8f2cb22f4543c4a8903f12b0671ffe07cc", size = 15667096, upload-time = "2026-08-09T13:48:01.562Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5e/29c73c31748cdb0f7566642125ba17fd5b56780cddf891b085dab27e4466/numpy-2.5.2-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba0a474801b8dc67b66bf465548abc90e82b44d2611b5770f33008dcabffe8ec", size = 16751730, upload-time = "2026-08-09T13:48:05.706Z" }, + { url = "https://files.pythonhosted.org/packages/47/95/02501e8454796bb58dadf7a99d3181e0b464bf264e1003039572f9779fac/numpy-2.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0a4035ae1129ff8777f08bfbd44f1e5d8e9c049ce0c2dd78fc0d92c13e7251c0", size = 17038686, upload-time = "2026-08-09T13:48:09.627Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b5/53a681d91b5c82687067d8ea5035e02d917b5509d6f334cb06484a954714/numpy-2.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:77843ca236b777e67f8d6b3660ea116e499612703a0ecd7093f316201eb9d8e2", size = 18507727, upload-time = "2026-08-09T13:48:13.744Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/6e11443f7b64ee376c860506091103bf68f92d2cab9e8d96d4501babf07c/numpy-2.5.2-cp315-cp315t-win32.whl", hash = "sha256:7354826bc6f8f69402e9b7fe28d15fcd34feebd74f856f111585c5b0c9fb0251", size = 6269775, upload-time = "2026-08-09T13:48:17.543Z" }, + { url = "https://files.pythonhosted.org/packages/f1/18/195d6b86cd72dbbc501edfa778005fa6b87afd34c153e46028cd3a0938f4/numpy-2.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:e5651f3f87add730ee6608d915009e19c911fba0cb000c7e3ea994b7d768eb12", size = 12782559, upload-time = "2026-08-09T13:48:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/b4/07/458c344f0f0c178f4481dad5cca790626ffe4c34eabf9467069d06ee4999/numpy-2.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:5f8e00be2ec6f45f4e8a41a527f68d44a7d96fee92a650e4d8b1326f77f61e6e", size = 10748103, upload-time = "2026-08-09T13:48:24.21Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124, upload-time = "2025-03-07T01:43:53.556Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318, upload-time = "2025-03-07T01:40:10.421Z" }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076, upload-time = "2025-03-07T01:41:59.817Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265, upload-time = "2025-03-07T01:39:43.533Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.19.0.56" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/b8/277c51962ee46fa3e5b203ac5f76107c650f781d6891e681e28e6f3e9fe6/nvidia_cudnn_cu12-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:08caaf27fe556aca82a3ee3b5aa49a77e7de0cfcb7ff4e5c29da426387a8267e", size = 656910700, upload-time = "2026-02-03T20:40:25.508Z" }, + { url = "https://files.pythonhosted.org/packages/c5/41/65225d42fba06fb3dd3972485ea258e7dd07a40d6e01c95da6766ad87354/nvidia_cudnn_cu12-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ac6ad90a075bb33a94f2b4cf4622eac13dd4dc65cf6dd9c7572a318516a36625", size = 657906812, upload-time = "2026-02-03T20:44:12.638Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211, upload-time = "2025-03-07T01:44:56.873Z" }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a", size = 1120705, upload-time = "2025-03-07T01:45:41.434Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd", size = 63625754, upload-time = "2025-03-07T01:46:10.735Z" }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841, upload-time = "2025-03-07T01:46:54.356Z" }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129, upload-time = "2025-03-07T01:47:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5", size = 283835557, upload-time = "2025-02-26T00:16:54.265Z" }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.28.9" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/c4/120d2dfd92dff2c776d68f361ff8705fdea2ca64e20b612fab0fd3f581ac/nvidia_nccl_cu12-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:50a36e01c4a090b9f9c47d92cec54964de6b9fcb3362d0e19b8ffc6323c21b60", size = 296766525, upload-time = "2025-11-18T05:49:16.094Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4e/44dbb46b3d1b0ec61afda8e84837870f2f9ace33c564317d59b70bc19d3e/nvidia_nccl_cu12-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:485776daa8447da5da39681af455aa3b2c2586ddcf4af8772495e7c532c7e5ab", size = 296782137, upload-time = "2025-11-18T05:49:34.248Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204, upload-time = "2025-03-07T01:49:43.612Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/6a/03aa43cc9bd3ad91553a88b5f6fb25ed6a3752ae86ce2180221962bc2aa5/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15", size = 138936938, upload-time = "2025-09-06T00:32:05.589Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615", size = 91161, upload-time = "2025-03-07T01:42:23.922Z" }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + +[[package]] +name = "onnxruntime" +version = "1.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flatbuffers" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "protobuf" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/12/3807e2b17d9eb71d3cb78ed2ba76869b05c637c9b9d6112e636098b0c97a/onnxruntime-1.28.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:31410f544674f534c2f27348af52ef81682ca9c8719154bf4d48f0ef23823b1e", size = 19141759, upload-time = "2026-07-25T01:21:53.765Z" }, + { url = "https://files.pythonhosted.org/packages/c0/23/b46045c3bf67a9cf54c12f5df0f018a422c65fbb9d6072b10071bebfaae2/onnxruntime-1.28.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f649dd6f6452d12a8059888aa489fe519e062e18793dac72b9efa0f9fdb64135", size = 17049339, upload-time = "2026-07-25T01:21:43.005Z" }, + { url = "https://files.pythonhosted.org/packages/78/b6/8c5396e7894e77c5a7d1e026f3acb9dd39c4b5644e412e37a0055eaa3bc5/onnxruntime-1.28.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fa221d669282bd8f582708ce4c96010a7e9fb0661f9006b37fe2fedafb73fe", size = 19214329, upload-time = "2026-07-25T01:22:04.133Z" }, + { url = "https://files.pythonhosted.org/packages/56/f1/51225c202edba4dfc94e1ea03f3d78f1aaf307da75fd792c0ce1946b2514/onnxruntime-1.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:1a1a19175464665c9b8d50bc916f216cc0b569110045b7bbca8f9f290b186f58", size = 13755033, upload-time = "2026-07-25T01:22:29.302Z" }, + { url = "https://files.pythonhosted.org/packages/f4/db/f59f715edfdd96a051f32b5ef0e680a20a8755d4ecd75f63090e960e347a/onnxruntime-1.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:cfab507abe09d6ffeb817eee07944d452fdc0b00fdcef34cab4db10a45e378c7", size = 13454175, upload-time = "2026-07-25T01:22:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/47/28/810314fa88647af9f4cdaf438a30ad1cfebebb53ded55499232d7a0094e6/onnxruntime-1.28.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac301f53b1930402fc46c368e268acfed02f3207272aaff05070d7e09f96f031", size = 17057307, upload-time = "2026-07-25T01:21:45.492Z" }, + { url = "https://files.pythonhosted.org/packages/3d/cc/9e9f193cc0f29f263a8f09ec08487aed6c96ee856d5fd77da32a425c1949/onnxruntime-1.28.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7f022a1103cae591c75fc4565589a515f2ddd14a6ac8e8a05812dfeda142e28", size = 19222954, upload-time = "2026-07-25T01:22:06.952Z" }, + { url = "https://files.pythonhosted.org/packages/4e/eb/952314c451d9463e5c9aed9978eec76cf32930d407d9ab8700dd0f4ea1ea/onnxruntime-1.28.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:8adff67a3f28257b37cfe945a7e952e4122666aa8c91a0380862e9fd4c2ed19f", size = 19143748, upload-time = "2026-07-25T01:21:56.297Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e9/139180b4dd810329aaa42c238b4e6383c906202d98609ae29d66eb7c32b1/onnxruntime-1.28.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc2565e487b4896fb988d6383577d875d958e071fc5f6c3550bd5d02ae98264b", size = 17051950, upload-time = "2026-07-25T01:21:48.606Z" }, + { url = "https://files.pythonhosted.org/packages/03/88/9432428273356ad3c8aa01f52c1b3e7f53c4c0192748f41ad983872b436b/onnxruntime-1.28.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6afdc83f1317c136e92fc29f5ee9f058de59d87c0b22cee3fdbfbaa0ccc2098a", size = 19214924, upload-time = "2026-07-25T01:22:09.727Z" }, + { url = "https://files.pythonhosted.org/packages/bb/e2/6feb3a43517aaf2b1bf7e46897ba5eb81a29717f7d7901420614d5ee4653/onnxruntime-1.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:f2a3b9e30ce880d4ca54999cb313569e36da4f62eefe25f87be18f43e9a3a4d5", size = 14093738, upload-time = "2026-07-25T01:22:31.629Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8f/83974a1e201dc2e58e5e7111bcaeb1ca2413e9c41f505d26419ee9e3dddf/onnxruntime-1.28.0-cp314-cp314-win_arm64.whl", hash = "sha256:07fb3cbe990d6bf0ab3c22bfbbfb0e314151266046ea6edb4a07f556b4258c5f", size = 13821117, upload-time = "2026-07-25T01:22:22.387Z" }, + { url = "https://files.pythonhosted.org/packages/0d/83/00e606bc25c756d76a267370c39b7516ad52f9cf134d7ff2bff8b6108bc4/onnxruntime-1.28.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e562d6e36a749f6764481c0ddb0f2af3d0b5a3c164291361d08803c557f369af", size = 17055518, upload-time = "2026-07-25T01:21:51.08Z" }, + { url = "https://files.pythonhosted.org/packages/94/a9/68707e1ce345cbdbcd4df65932ebc82a673e917d63eda0007ebcff948691/onnxruntime-1.28.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f6e92367ddce1e4d33cf295024f40192be6c6171a09208f515ba169ced06c8e", size = 19222976, upload-time = "2026-07-25T01:22:12.474Z" }, +] + +[[package]] +name = "openai" +version = "2.54.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/9a/8c75e8c8a5b407a0586faeb2afac91674ff955c191ecc1d6d3b6669f6788/openai-2.54.0.tar.gz", hash = "sha256:e3e6f8bc1ba30ddf381ace1a14340eed381cb984a1a59bd0f34b5be3b5d49cfa", size = 1100285, upload-time = "2026-08-11T18:46:59.035Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/a8/bb76c7356de8ad57f59d5ff993d434df0607f07f08bcc9c9a5c275e399c0/openai-2.54.0-py3-none-any.whl", hash = "sha256:89089789197ccdb87f173a03145ed1598d00795220c93e96cf712b1cbf5e5f2b", size = 1660351, upload-time = "2026-08-11T18:46:56.684Z" }, +] + +[[package]] +name = "opencv-python" +version = "5.0.0.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/4c/a438d23e09ce2033c09f7b784ad2fbdb0adf529e434101ed28f142226f98/opencv_python-5.0.0.93.tar.gz", hash = "sha256:66aac3e5b5faa48d4025816592f3af19e4bfc2c68dec067bae2dbb4ca10aa9e2", size = 81802749, upload-time = "2026-07-02T06:59:53.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/75/76f6ade78f6102c61034f828e2a22616708df2c9504bc8d6af9dd8f73dc5/opencv_python-5.0.0.93-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:198a75138241810206a17c829dbcc40a7cb1841cda538ca86cbbfc6c7d95f898", size = 48322443, upload-time = "2026-07-02T05:50:25.466Z" }, + { url = "https://files.pythonhosted.org/packages/15/8c/bc1bda6aae69a32e9d84fc34153ba104cd25226861eb4aea33b2cea4860d/opencv_python-5.0.0.93-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:6bbc32f59e1b1a7db7b39c81f63d00625f041d333037fd8702f6da52cc39108b", size = 34782755, upload-time = "2026-07-02T05:51:30.556Z" }, + { url = "https://files.pythonhosted.org/packages/f4/8a/b04776ec45d2dea08a1b176f1829201db3515d4ed16c35f8fcc9fa7beb16/opencv_python-5.0.0.93-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2b4272e736836f66c2d176e43ab8101f3a00d45654916399f52e150c58981ac", size = 50614064, upload-time = "2026-07-02T06:53:22.604Z" }, + { url = "https://files.pythonhosted.org/packages/95/54/eb47866b94f2b5b42dde17644b78055ef1ee05aae59962c7290e55270803/opencv_python-5.0.0.93-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8b6d0a212253dd26ad338c812f1f23ca118fdf05a9c8c6b9444f161aa8c5881", size = 71064711, upload-time = "2026-07-02T06:54:13.148Z" }, + { url = "https://files.pythonhosted.org/packages/93/da/962579f1e703cbf8c5422fd1f576467dcb3b5b0b0b81c1471c979764353a/opencv_python-5.0.0.93-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:08d5d91d967b58d6db86073b2ad3eaef88ca4ebdfd45c9059bf59f5ded0c7ad2", size = 49798576, upload-time = "2026-07-02T06:54:33.781Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4c/c73f828fdbcd37eaf21d08fa852544a3ca7c2dbb3ea76873d64f2ea413d1/opencv_python-5.0.0.93-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c8de2dec111122a02e8beb28e16c31904992dfd6186560b142a92c71403c1039", size = 73783032, upload-time = "2026-07-02T06:55:03.415Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4b/edaf83b996ca5a1a3d8ccad485706b9c6d4742b13b9c4586bf1c1e7d9423/opencv_python-5.0.0.93-cp37-abi3-win32.whl", hash = "sha256:4b4b1a34c79bf8d3738e3cfe9a9e67b51a79663f6b692cbdad8c31f570da4157", size = 35564734, upload-time = "2026-07-02T05:49:57.704Z" }, + { url = "https://files.pythonhosted.org/packages/21/f0/9fa6e85cb10c8eb36a0222d27e50fe381b86ce49a55446bf39f491727564/opencv_python-5.0.0.93-cp37-abi3-win_amd64.whl", hash = "sha256:f90ba04b8f73bc5c3814037699739f0156f597338a98f05956c684e7c3ca10d2", size = 44000345, upload-time = "2026-07-02T05:49:54.971Z" }, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pdftext" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "numpy" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pypdfium2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/48/6c12661234ef71a9349bb1fbdd0259a6d8189376f664492e2c206ab3c397/pdftext-0.7.1.tar.gz", hash = "sha256:7484add28d48d8c04aab0076769f2d626454233de95de00c7b348cb6398045bd", size = 28483, upload-time = "2026-07-08T21:57:14.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/5d/997add0ca9f74d77afd9ab355b301a495420ce0b3258bf6954b00aa47465/pdftext-0.7.1-py3-none-any.whl", hash = "sha256:ffb47a36e7f0c9a295e16c0e0a9f6a323026f88416f8045330746604f7212986", size = 29712, upload-time = "2026-07-08T21:57:15.209Z" }, +] + +[[package]] +name = "pillow" +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, + { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, + { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, + { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, + { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.53" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ea/39b988c938f75cb75d7045b5c69f8bfed47ee2152c8837fb403de29d6fb8/prompt_toolkit-3.0.53.tar.gz", hash = "sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6", size = 435492, upload-time = "2026-07-26T20:56:14.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl", hash = "sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2", size = 392288, upload-time = "2026-07-26T20:56:12.512Z" }, +] + +[[package]] +name = "protobuf" +version = "7.35.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/01/9ef0afd7999eb9badb3a768b4aedd78c86d4c65cfaf1958ab276199e76b4/protobuf-7.35.1.tar.gz", hash = "sha256:ce115a26fe0c39a2c29973d914d327e516a6455464489fe3cd1e51a1b354f81a", size = 458717, upload-time = "2026-06-11T21:55:40.257Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:24f857477359a85c0c235261b8ba905fd51b2562f4a64ca1df5473f29850cbf6", size = 433226, upload-time = "2026-06-11T21:55:31.719Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/dfb89eb0e652a1ff073c39a59fb5e3a83cfe9b57a2c83fa6d78270101767/protobuf-7.35.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:11d6b0ec246892d85215b0a13ca6e0233cf5284b68f0ac02646427f4ff88a799", size = 328847, upload-time = "2026-06-11T21:55:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/0f/58/dc12f2cd484951524af6e3382c785869b9b3fb5e52ee95ae23add53ee8f9/protobuf-7.35.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:b73f9489a4b8b1c9cb1f8ed951c736392592edb24b9d6819f36d2e10b171d5b4", size = 344030, upload-time = "2026-06-11T21:55:34.941Z" }, + { url = "https://files.pythonhosted.org/packages/e4/be/5b3cfe508bfab6761414ff944e3366eb13be4fd71efcd69450f89ba39f43/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:74758715c53d7158fb76caf4f0cfdacc5329a4b1bb994f865d6cf302d413a1c4", size = 327130, upload-time = "2026-06-11T21:55:35.921Z" }, + { url = "https://files.pythonhosted.org/packages/d8/bc/6d6c7ba8709c85f8f2c390b2b118d6fb08a783676a572271851bf45a7d22/protobuf-7.35.1-cp310-abi3-win32.whl", hash = "sha256:353652e4efd0bca5b5fc2656abf8307ef351f0cf938c9eba09f0e09c20a25c30", size = 428945, upload-time = "2026-06-11T21:55:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/0a/19/8d0cb6f20a1ef7b18f1c8986ad5783f22f84cce39c6ce9a6e645ea55192e/protobuf-7.35.1-cp310-abi3-win_amd64.whl", hash = "sha256:230a75ddfc2de4806e56696ce9640c1cdfdb6543b7cfce98d42a4c0a0e7bdb87", size = 439996, upload-time = "2026-06-11T21:55:38.123Z" }, + { url = "https://files.pythonhosted.org/packages/19/c7/5f7c636ec43e0c545e28d1f1db71990108306f7bdcb89f069ba97e428e7f/protobuf-7.35.1-py3-none-any.whl", hash = "sha256:4bc97768d8fe4ad6743c8a19403e314511ed9f6d13205b687e52421c023ac1b9", size = 171659, upload-time = "2026-06-11T21:55:39.155Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "pyclipper" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/21/3c06205bb407e1f79b73b7b4dfb3950bd9537c4f625a68ab5cc41177f5bc/pyclipper-1.4.0.tar.gz", hash = "sha256:9882bd889f27da78add4dd6f881d25697efc740bf840274e749988d25496c8e1", size = 54489, upload-time = "2025-12-01T13:15:35.015Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/d0/cbce7d47de1e6458f66a4d999b091640134deb8f2c7351eab993b70d2e10/pyclipper-1.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d49df13cbb2627ccb13a1046f3ea6ebf7177b5504ec61bdef87d6a704046fd6e", size = 264342, upload-time = "2025-12-01T13:15:12.697Z" }, + { url = "https://files.pythonhosted.org/packages/ce/cc/742b9d69d96c58ac156947e1b56d0f81cbacbccf869e2ac7229f2f86dc4e/pyclipper-1.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37bfec361e174110cdddffd5ecd070a8064015c99383d95eb692c253951eee8a", size = 139839, upload-time = "2025-12-01T13:15:13.911Z" }, + { url = "https://files.pythonhosted.org/packages/db/48/dd301d62c1529efdd721b47b9e5fb52120fcdac5f4d3405cfc0d2f391414/pyclipper-1.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14c8bdb5a72004b721c4e6f448d2c2262d74a7f0c9e3076aeff41e564a92389f", size = 972142, upload-time = "2025-12-01T13:15:15.477Z" }, + { url = "https://files.pythonhosted.org/packages/07/bf/d493fd1b33bb090fa64e28c1009374d5d72fa705f9331cd56517c35e381e/pyclipper-1.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2a50c22c3a78cb4e48347ecf06930f61ce98cf9252f2e292aa025471e9d75b1", size = 952789, upload-time = "2025-12-01T13:15:17.042Z" }, + { url = "https://files.pythonhosted.org/packages/cf/88/b95ea8ea21ddca34aa14b123226a81526dd2faaa993f9aabd3ed21231604/pyclipper-1.4.0-cp313-cp313-win32.whl", hash = "sha256:c9a3faa416ff536cee93417a72bfb690d9dea136dc39a39dbbe1e5dadf108c9c", size = 94817, upload-time = "2025-12-01T13:15:18.724Z" }, + { url = "https://files.pythonhosted.org/packages/ba/42/0a1920d276a0e1ca21dc0d13ee9e3ba10a9a8aa3abac76cd5e5a9f503306/pyclipper-1.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d4b2d7c41086f1927d14947c563dfc7beed2f6c0d9af13c42fe3dcdc20d35832", size = 104007, upload-time = "2025-12-01T13:15:19.763Z" }, + { url = "https://files.pythonhosted.org/packages/1a/20/04d58c70f3ccd404f179f8dd81d16722a05a3bf1ab61445ee64e8218c1f8/pyclipper-1.4.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7c87480fc91a5af4c1ba310bdb7de2f089a3eeef5fe351a3cedc37da1fcced1c", size = 265167, upload-time = "2025-12-01T13:15:20.844Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2e/a570c1abe69b7260ca0caab4236ce6ea3661193ebf8d1bd7f78ccce537a5/pyclipper-1.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81d8bb2d1fb9d66dc7ea4373b176bb4b02443a7e328b3b603a73faec088b952e", size = 139966, upload-time = "2025-12-01T13:15:22.036Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/e0859e54adabdde8a24a29d3f525ebb31c71ddf2e8d93edce83a3c212ffc/pyclipper-1.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:773c0e06b683214dcfc6711be230c83b03cddebe8a57eae053d4603dd63582f9", size = 968216, upload-time = "2025-12-01T13:15:23.18Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6b/e3c4febf0a35ae643ee579b09988dd931602b5bf311020535fd9e5b7e715/pyclipper-1.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bc45f2463d997848450dbed91c950ca37c6cf27f84a49a5cad4affc0b469e39", size = 954198, upload-time = "2025-12-01T13:15:24.522Z" }, + { url = "https://files.pythonhosted.org/packages/fc/74/728efcee02e12acb486ce9d56fa037120c9bf5b77c54bbdbaa441c14a9d9/pyclipper-1.4.0-cp314-cp314-win32.whl", hash = "sha256:0b8c2105b3b3c44dbe1a266f64309407fe30bf372cf39a94dc8aaa97df00da5b", size = 96951, upload-time = "2025-12-01T13:15:25.79Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d7/7f4354e69f10a917e5c7d5d72a499ef2e10945312f5e72c414a0a08d2ae4/pyclipper-1.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:6c317e182590c88ec0194149995e3d71a979cfef3b246383f4e035f9d4a11826", size = 106782, upload-time = "2025-12-01T13:15:26.945Z" }, + { url = "https://files.pythonhosted.org/packages/63/60/fc32c7a3d7f61a970511ec2857ecd09693d8ac80d560ee7b8e67a6d268c9/pyclipper-1.4.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:f160a2c6ba036f7eaf09f1f10f4fbfa734234af9112fb5187877efed78df9303", size = 269880, upload-time = "2025-12-01T13:15:28.117Z" }, + { url = "https://files.pythonhosted.org/packages/49/df/c4a72d3f62f0ba03ec440c4fff56cd2d674a4334d23c5064cbf41c9583f6/pyclipper-1.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a9f11ad133257c52c40d50de7a0ca3370a0cdd8e3d11eec0604ad3c34ba549e9", size = 141706, upload-time = "2025-12-01T13:15:30.134Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0b/cf55df03e2175e1e2da9db585241401e0bc98f76bee3791bed39d0313449/pyclipper-1.4.0-cp314-cp314t-win32.whl", hash = "sha256:bbc827b77442c99deaeee26e0e7f172355ddb097a5e126aea206d447d3b26286", size = 105308, upload-time = "2025-12-01T13:15:31.225Z" }, + { url = "https://files.pythonhosted.org/packages/8f/dc/53df8b6931d47080b4fe4ee8450d42e660ee1c5c1556c7ab73359182b769/pyclipper-1.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29dae3e0296dff8502eeb7639fcfee794b0eec8590ba3563aee28db269da6b04", size = 117608, upload-time = "2025-12-01T13:15:32.69Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/ca/31c57507b13119d7d3cfa1576dad2911a4861e3be07b579395f4e9d393f9/pydantic_settings-2.15.0.tar.gz", hash = "sha256:694b793e84f766ba76a90ebdefc01d0a9a045dab0382bee70393da93712ad117", size = 261253, upload-time = "2026-08-07T09:24:57.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl", hash = "sha256:0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42", size = 69413, upload-time = "2026-08-07T09:24:55.839Z" }, +] + +[[package]] +name = "pylatexenc" +version = "2.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/45/ddb0fb04acf95fe9cf9c369814dbdd08651bd2c9ee455f142651e06f4488/pylatexenc-2.11.tar.gz", hash = "sha256:305a072a99ce736246049c9da05841b9d718c0f7ea8888f5f596cf15cb621053", size = 165743, upload-time = "2026-07-25T17:26:31.534Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/06/3d67bd912ef337aa4856b466121d03f304aa1bb4d804f9298b6227cd227e/pylatexenc-2.11-py2.py3-none-any.whl", hash = "sha256:e78e7391d6c104f1ed150e21cfaa58016cdb50aa54406a2eecb793649ffdfdd0", size = 137533, upload-time = "2026-07-25T17:26:30.141Z" }, +] + +[[package]] +name = "pypdf" +version = "6.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/5a/df92d1c1ef8806ca28f20f978ee059894868d93de797a7e2edebe7fe1a43/pypdf-6.16.1.tar.gz", hash = "sha256:c4d1b43ddae921387321cf63936cd16a7743b91d2da92f165c149a195c972ba9", size = 7003737, upload-time = "2026-08-14T12:24:04.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/a1/724b18d6757ab7253a8fecd3a430eb8d980ed26872ba16651e7b5ddfc63f/pypdf-6.16.1-py3-none-any.whl", hash = "sha256:63fec31c4092ae50b6729beedcb469055b60d20c834bde1c402df241f371f644", size = 382924, upload-time = "2026-08-14T12:24:02.854Z" }, +] + +[[package]] +name = "pypdfium2" +version = "5.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/78/d9b45abb97a3686643f7c6472a5f7688f2013a373226121dc76b9debbacf/pypdfium2-5.10.1.tar.gz", hash = "sha256:f257d2011eb43c846b7e9f5a802e28646b29732763e4a35dd6ca76f9be580538", size = 272963, upload-time = "2026-06-15T10:09:16.179Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/49/0b85fcc0d236582143a25cf275b0b4f5d786f51eb07a89ec9c79d43efe18/pypdfium2-5.10.1-py3-none-android_23_arm64_v8a.whl", hash = "sha256:13abf7a9f5e0ddebc8bbcccea5f13ae5abe8a298ea219e125b0fc24c1d2171b4", size = 3409176, upload-time = "2026-06-15T10:08:36.017Z" }, + { url = "https://files.pythonhosted.org/packages/20/d2/2f522c5b2ad5166edf256bb4dbab97de5d07b2573f8a5630ddfcd1f8d4ee/pypdfium2-5.10.1-py3-none-android_23_armeabi_v7a.whl", hash = "sha256:a0dc52b56631e2f7edcdb22bae3b155aa840bec32c5bd05781e90baaecee88e7", size = 2866175, upload-time = "2026-06-15T10:08:37.955Z" }, + { url = "https://files.pythonhosted.org/packages/a7/be/477548c026c2badfdbf4afc3358b7135121fb5bec2e2effcb67e3a674d0b/pypdfium2-5.10.1-py3-none-macosx_12_0_arm64.whl", hash = "sha256:ebb9e63f92d15fc41b359fe7a187233dfae37548800e1fa09cb2fc466ac89951", size = 3621427, upload-time = "2026-06-15T10:08:39.969Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9b/0131c7f711b62c6edd6b200e9eb6340be6de4f6dc5baae625e3394d8d5fb/pypdfium2-5.10.1-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:d04f2050b6b32bb18624688b600543342a4ab3aacf64bf66521a5af72bbc7de1", size = 3682825, upload-time = "2026-06-15T10:08:42.181Z" }, + { url = "https://files.pythonhosted.org/packages/9d/3d/bddfceb6e67e54d6dd1ab6c0f1feff796a89596e40f6345a3b4cc6a3d408/pypdfium2-5.10.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0de53d2710ca9509fc2812340acc57c3f043697609068592d87de654f8cabf44", size = 3682206, upload-time = "2026-06-15T10:08:44.087Z" }, + { url = "https://files.pythonhosted.org/packages/58/08/dedeb25c6645fc8a5eda24f54e0b1d083b7334ababf5f518bb939d729cc0/pypdfium2-5.10.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:10a26ce04795f8ec079e81c707fcb5737061e8a78025babc3d6e36642e9c903a", size = 3413720, upload-time = "2026-06-15T10:08:45.739Z" }, + { url = "https://files.pythonhosted.org/packages/cc/48/69e1fc8b1216005243c6415183bbf6de1cda3f5a06758b3fa4a26a7385c6/pypdfium2-5.10.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be6d2a8d1bcfd777188e7aa55a25c83f34d54e8350ad8810fb32017787d9b0d9", size = 3812913, upload-time = "2026-06-15T10:08:47.45Z" }, + { url = "https://files.pythonhosted.org/packages/72/7f/132455a58ad736d76815c6cd1307532c3f433299d945b9d2f8cc2387c309/pypdfium2-5.10.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf4d2527f79f31c550490cc74c9f32e19385a944630a3ef4cd4d9b6f961fbf77", size = 4223220, upload-time = "2026-06-15T10:08:49.099Z" }, + { url = "https://files.pythonhosted.org/packages/bf/8c/4d5804eca598bbe894e0a9a510807e221c1623e7276ce6b68fa2660dc933/pypdfium2-5.10.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ba127750bc3f4161461538d532d74491cd976f584f1753a6cee9cb821338ec1", size = 3738950, upload-time = "2026-06-15T10:08:50.821Z" }, + { url = "https://files.pythonhosted.org/packages/d1/7f/baac59bf14ff914d97789ee0368c22ae233aa857aa9c0726bcb515dbc4d7/pypdfium2-5.10.1-py3-none-manylinux_2_27_s390x.manylinux_2_28_s390x.whl", hash = "sha256:80f30517ee089dfbbc6e9de6da365b6e8c0ce8f80c40b7d4025a3b1d3bbd8a70", size = 4029869, upload-time = "2026-06-15T10:08:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d7/a5d58a0bcba31a0e37ed636a76ef3d2d215733f28af61637287d061b0c54/pypdfium2-5.10.1-py3-none-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ad5f5de15febb788c6eb3853e58aceda9cd8c5187c92472abaeecf9558deb0cf", size = 3990927, upload-time = "2026-06-15T10:08:54.555Z" }, + { url = "https://files.pythonhosted.org/packages/da/1a/98eebd14b36812176297cf765d504ebeebc982f895c8a3a9fbd2717797de/pypdfium2-5.10.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8947fa3cd808da33960bdf8ff9e5247aa94ccd94f0b09bb3402e99498d83bcc9", size = 4989624, upload-time = "2026-06-15T10:08:56.284Z" }, + { url = "https://files.pythonhosted.org/packages/1c/20/f2e124d607b8bb90a9f1ce976afff38c70e215cd7ea86af784cb2e8a19dd/pypdfium2-5.10.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:139a6387a3a2652f288e53164268bb03af4fa221d78484ee18407053a60082a3", size = 4535124, upload-time = "2026-06-15T10:08:58.214Z" }, + { url = "https://files.pythonhosted.org/packages/59/ef/469ea87f668a32ff3280ea15e522e3a7858d2c80f1ecc320ece1244624c9/pypdfium2-5.10.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:172ff3e10358d66456e27fb0b8b5098e28ec24e51072eb4b5b86077d550e21bb", size = 5229373, upload-time = "2026-06-15T10:09:00.702Z" }, + { url = "https://files.pythonhosted.org/packages/c3/48/c7ed3001f0c5e28114c98bf918c8121e422a65ba7323e1e12f3f28e2d278/pypdfium2-5.10.1-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:09fda0609dc4749c9865a0315c447d6f2583a580693bcabd69980d3fbb22ad51", size = 5140010, upload-time = "2026-06-15T10:09:02.734Z" }, + { url = "https://files.pythonhosted.org/packages/b2/bc/00b731bfc1fdc0f3c7d91108fceb54978ff4f5a92336820e5ed6c12535ff/pypdfium2-5.10.1-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:6b01adcfe9aaf7a635a59bed5687fd4fd7b0da292664f050d4ebd2bfa5c70584", size = 4643310, upload-time = "2026-06-15T10:09:04.794Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e8/3ad242233f657c19092a8c83684c8154b8d02a826535a6a2591d91aa5dde/pypdfium2-5.10.1-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:610e14c37d2090b826dccc0604fc7e7612c0ce591190780ae228abdb9abb971e", size = 5087879, upload-time = "2026-06-15T10:09:06.861Z" }, + { url = "https://files.pythonhosted.org/packages/80/3f/9d0747a02ac3021ed3db7ac27c5187d97e78b0253a4bdfbf386666968474/pypdfium2-5.10.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f2803020952afa57e1e148adc19369b835154e1fa251a1ca85008ab6466a710f", size = 5047369, upload-time = "2026-06-15T10:09:08.883Z" }, + { url = "https://files.pythonhosted.org/packages/9e/41/7d5187e9527eae81890a3b13193442112ef788d61ddaa68a6d59ac447bec/pypdfium2-5.10.1-py3-none-win32.whl", hash = "sha256:8702bb4f01ddfc8e7757b41b4c2c8392ac17c9f0234476e1e69672ea7c6d6aa0", size = 3680056, upload-time = "2026-06-15T10:09:10.95Z" }, + { url = "https://files.pythonhosted.org/packages/16/1d/c62bd59dd8345cc4b640f942f465633f6b07b859d01ddb648610a7bf5c7c/pypdfium2-5.10.1-py3-none-win_amd64.whl", hash = "sha256:58da5b51fb7884c7d21a05062ab13edb011d1a08dfd9694f3d5d685df62796b9", size = 3812105, upload-time = "2026-06-15T10:09:12.684Z" }, + { url = "https://files.pythonhosted.org/packages/10/d5/21bac39125df8a93e99c04583486b58a62b5997d6b3541e3ad0f69053392/pypdfium2-5.10.1-py3-none-win_arm64.whl", hash = "sha256:e3301c2f7a66fb8cb57dba857d0c9e90215e178f6602a87c5a306cd98513dab8", size = 3600043, upload-time = "2026-06-15T10:09:14.606Z" }, +] + +[[package]] +name = "pypptx-with-oxml" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lxml" }, + { name = "pillow" }, + { name = "typing-extensions" }, + { name = "xlsxwriter" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/17/7a499cea9e03b37ef5a502a7792431b5e609328908e2e11b02453071367b/pypptx_with_oxml-1.0.3.tar.gz", hash = "sha256:a093d8c8e99dfcd0131ea98b4dbc0a0a40adf44869edbb74c57d0cb87b8e4208", size = 9767175, upload-time = "2026-01-30T08:51:32.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/d8/940fcaa6a1f3763d72751b6bc8054f40beeacd6e9e5b19069c6c73dab5af/pypptx_with_oxml-1.0.3-py3-none-any.whl", hash = "sha256:4b3ccf51185e0f9e60ebf2884e74153d7fcb00e7e4f0461404e96e0260d7bba1", size = 493041, upload-time = "2026-01-30T08:51:25.797Z" }, +] + +[[package]] +name = "python-docx" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lxml" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "quantum-system" +version = "0.1.0" +source = { virtual = "." } + +[package.dev-dependencies] +dev = [ + { name = "commitizen" }, + { name = "lefthook" }, +] +mineru = [ + { name = "mineru", extra = ["pipeline", "vlm"] }, + { name = "six" }, + { name = "torch" }, + { name = "torchvision" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "commitizen", specifier = ">=4.17.0" }, + { name = "lefthook", specifier = ">=2.1.10" }, +] +mineru = [ + { name = "mineru", extras = ["pipeline", "vlm"], specifier = ">=3.4.5" }, + { name = "six", specifier = ">=1.17.0" }, + { name = "torch", specifier = ">=2.6,<2.12", index = "https://download.pytorch.org/whl/cu128" }, + { name = "torchvision", index = "https://download.pytorch.org/whl/cu128" }, +] + +[[package]] +name = "questionary" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "prompt-toolkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/45/eafb0bba0f9988f6a2520f9ca2df2c82ddfa8d67c95d6625452e97b204a5/questionary-2.1.1.tar.gz", hash = "sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d", size = 25845, upload-time = "2025-08-28T19:00:20.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" }, +] + +[[package]] +name = "regex" +version = "2026.7.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/98/04b13f1ddfb63158025291c02e03eb42fbb7acb51d091d541050eb4e35e8/regex-2026.7.19.tar.gz", hash = "sha256:7e77b324909c1617cbb4c668677e2c6ae13f44d7c1de0d4f15f2e3c10f3315b5", size = 416440, upload-time = "2026-07-19T00:19:48.923Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/3d/84165e4299ff76f3a40fe1f2abf939e976f693383a08d2beea6af62bd2c1/regex-2026.7.19-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f035d9dc1d25eff9d361456572231c7d27b5ccd473ca7dc0adfce732bd006d40", size = 496552, upload-time = "2026-07-19T00:17:36.808Z" }, + { url = "https://files.pythonhosted.org/packages/02/a2/a65293e6e4cf28eb7ee1be5335a5386c40d6742e9f47fafc8fec785e16c7/regex-2026.7.19-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c42572142ed0b9d5d261ba727157c426510da78e20828b66bbb855098b8a4e38", size = 296983, upload-time = "2026-07-19T00:17:38.816Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/2d0564e93d87bc48618360ddca232a2ca612bbdf53ce8465d45ca5ce14ee/regex-2026.7.19-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40b34dd88658e4fedd2fddbf0275ac970d00614b731357f425722a3ed1983d11", size = 291832, upload-time = "2026-07-19T00:17:40.726Z" }, + { url = "https://files.pythonhosted.org/packages/07/cd/42dfbabff3dfc9603c501c0e2e2c5adbb09d127b267bf5348de0af338c15/regex-2026.7.19-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c41c63992bf1874cebb6e7f56fd7d3c007924659a604ae3d90e427d40d4fd13", size = 796775, upload-time = "2026-07-19T00:17:42.382Z" }, + { url = "https://files.pythonhosted.org/packages/df/5d/f6a4839f2b934e3eed5973fd07f5929ee97d4c98939fb275ea23c274ee16/regex-2026.7.19-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d3372064506b94dd2c67c845f2db8062e9e9ba84d04e33cb96d7d33c11fe1ae", size = 865687, upload-time = "2026-07-19T00:17:44.185Z" }, + { url = "https://files.pythonhosted.org/packages/14/b0/b47d6c36049bc59806a50bd4c86ced70bbe058d787f80281b1d7a9b0e024/regex-2026.7.19-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fce7760bf283405b2c7999cab3da4e72f7deca6396013115e3f7a955db9760da", size = 911962, upload-time = "2026-07-19T00:17:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/2a/be/ff61f28f9273658cfe23acbbac5217221f6519960ed401e61dfdab12bc35/regex-2026.7.19-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0d702548d89d572b2929879bc883bb7a4c4709efafe4512cadee56c55c9bd15", size = 801817, upload-time = "2026-07-19T00:17:48.25Z" }, + { url = "https://files.pythonhosted.org/packages/c3/bb/8b4f7f26b333f9f79e1b453613c39bb4776f51d38ae66dd0ba31d6b354ca/regex-2026.7.19-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d446c6ac40bb6e05025ccee55b84d80fe9bf8e93010ffc4bb9484f13d498835f", size = 776908, upload-time = "2026-07-19T00:17:50.183Z" }, + { url = "https://files.pythonhosted.org/packages/09/13/610110fc5921d380516d03c26b652555f08aa0d23ea78a771231873c3638/regex-2026.7.19-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4c3501bfa814ab07b5580741f9bf78dfdfe146a04057f82df9e2402d2a975939", size = 784426, upload-time = "2026-07-19T00:17:52.454Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f5/1ef9e2a83a5947c57ebff0b377cb5727c3d5ec1992317a320d035cd0dbb6/regex-2026.7.19-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c4585c3e64b4f9e583b4d2683f18f5d5d872b3d71dcf24594b74ecc23602fa96", size = 860600, upload-time = "2026-07-19T00:17:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/a0/02/073af33a3ec149241d11c80acea91e722aa0adbf05addd50f251c4fe89c3/regex-2026.7.19-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:571fde9741eb0ccde23dd4e0c1d50fbae910e901fa7e629faf39b2dda740d220", size = 765950, upload-time = "2026-07-19T00:17:56.041Z" }, + { url = "https://files.pythonhosted.org/packages/81/a9/d1e9f819dc394a568ef370cd56cf25394e957a2235f8370f23b576e5a475/regex-2026.7.19-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:15b364b9b98d6d2fe1a85034c23a3180ff913f46caddc3895f6fd65186255ccc", size = 851794, upload-time = "2026-07-19T00:17:57.897Z" }, + { url = "https://files.pythonhosted.org/packages/03/3a/8ae83eda7579feacdf984e71fb9e70635fb6f832eeddca58427ec4fca926/regex-2026.7.19-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffd8893ccc1c2fce6e0d6ca402d716fe1b29db70c7132609a05955e31b2aa8f2", size = 789845, upload-time = "2026-07-19T00:17:59.97Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/c195cbfe5a75fdec64d8f6554fd15237b837919d2c61bdc141d7c807b08b/regex-2026.7.19-cp313-cp313-win32.whl", hash = "sha256:f0fa4fa9c3632d708742baf2282f2055c11d888a790362670a403cbf48a2c404", size = 267135, upload-time = "2026-07-19T00:18:01.958Z" }, + { url = "https://files.pythonhosted.org/packages/b2/80/a11de8404b7272b70acb45c1c05987cce60b45d5693da2e176f0e390d564/regex-2026.7.19-cp313-cp313-win_amd64.whl", hash = "sha256:d51ffd3427640fa2da6ade574ceba932f210ad095f65fcc450a2b0a0d454868e", size = 277747, upload-time = "2026-07-19T00:18:04.121Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/0f5c8eff1b4f1f3d83276d365fccecf666afcc7d947420943bf394d07adb/regex-2026.7.19-cp313-cp313-win_arm64.whl", hash = "sha256:c670fe7be5b6020b76bc6e8d2196074657e1327595bca93a389e1a76ab130ad8", size = 277129, upload-time = "2026-07-19T00:18:05.821Z" }, + { url = "https://files.pythonhosted.org/packages/dc/4c/44b74742052cedda40f9ae469532a037112f7311a36669a891fba8984bb0/regex-2026.7.19-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db47b561c9afd884baa1f96f797c9ca369872c4b65912bc691cfa99e68340af2", size = 501134, upload-time = "2026-07-19T00:18:07.567Z" }, + { url = "https://files.pythonhosted.org/packages/f0/45/bbd038b5e39ee5613a5a689290145b40058cc152c41de9cc23639d2b9734/regex-2026.7.19-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65dcd28d3eba2ab7c2fd906485cc301392b47cc2234790d27d4e4814e02cdfda", size = 299418, upload-time = "2026-07-19T00:18:09.38Z" }, + { url = "https://files.pythonhosted.org/packages/65/38/c5bde94b4cedfd5850d64c3f08222d8e1600e84f6ee71d9b44b4b8163f74/regex-2026.7.19-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f2e7f8e2ab6c2922be02c7ec45185aa5bd771e2e57b95455ee343a44d8130dff", size = 294486, upload-time = "2026-07-19T00:18:11.188Z" }, + { url = "https://files.pythonhosted.org/packages/d7/6a/2f5e107cb26c960b781967178899daf2787a7ab151844ed3c01d6fc95474/regex-2026.7.19-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe31f28c94402043161876a258a9c6f757cb485905c7614ce8d6cd40e6b7bdc1", size = 811643, upload-time = "2026-07-19T00:18:12.975Z" }, + { url = "https://files.pythonhosted.org/packages/37/d4/a2f963406d7d73a62eed84ba05a258afb6cad1b21aa4517443ce40506b78/regex-2026.7.19-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8f6fa298bb4f7f58a33334406218ba74716e68feddf5e4e54cd5d8082705abf", size = 871081, upload-time = "2026-07-19T00:18:14.733Z" }, + { url = "https://files.pythonhosted.org/packages/45/a3/44be546340bedb15f13063f5e7fe16793ea4d9ea2e805d09bd174ac27724/regex-2026.7.19-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cc1b2440423a851fad781309dd87843868f4f66a6bcd1ddb9225cf4ec2c84732", size = 917372, upload-time = "2026-07-19T00:18:16.724Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f6/e0870b0fd2a40dba0074e4b76e514b21313d37946c9248453e34ec43923e/regex-2026.7.19-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ac59a0900474a52b7c04af8196affc22bd9842acb0950df12f7b813e983609a", size = 816089, upload-time = "2026-07-19T00:18:18.617Z" }, + { url = "https://files.pythonhosted.org/packages/ae/27/957e8e22690ad6634572b39b71f130a6105f4d0718bb16849eac00fff147/regex-2026.7.19-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4896db1f4ce0576765b8272aa922df324e0f5b9bb2c3d03044ff32a7234a9aba", size = 785206, upload-time = "2026-07-19T00:18:20.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/a4/186e410941e731037c01166069ab86da9f65e8f8110c18009ccf4bd623ee/regex-2026.7.19-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4e6883a021db30511d9fb8cfb0f222ce1f2c369f7d4d8b0448f449a93ba0bdfc", size = 800431, upload-time = "2026-07-19T00:18:22.716Z" }, + { url = "https://files.pythonhosted.org/packages/73/9f/e4e10e023d291d64a33e246610b724493bf1ce98e0e59c9b7c837e5acfb7/regex-2026.7.19-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:09523a592938aa9f587fb74467c63ff0cf88fc3df14c82ab0f0517dcf76aaa62", size = 864906, upload-time = "2026-07-19T00:18:24.772Z" }, + { url = "https://files.pythonhosted.org/packages/24/57/ccb20b6be5f1f52a053d1ba2a8f7a077edb9d918248b8490d7506c6832b3/regex-2026.7.19-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1ebac3474b8589fce2f9b225b650afd61448f7c73a5d0255a10cc6366471aed1", size = 773559, upload-time = "2026-07-19T00:18:27.008Z" }, + { url = "https://files.pythonhosted.org/packages/a3/82/f3b263cf8fad927dc102891da8502e718b7ff9d19af7a2a07c03865d7188/regex-2026.7.19-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4a0530bb1b8c1c985e7e2122e2b4d3aedd8a3c21c6bfddae6767c4405668b56e", size = 857739, upload-time = "2026-07-19T00:18:29.107Z" }, + { url = "https://files.pythonhosted.org/packages/47/2e/1687bd1b6c2aed5e672ccf845fc11557821fe7366d921b50889ea5ce57bf/regex-2026.7.19-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef7eeb108c47ce7bcc9513e51bcb1bf57e8f483d52fce68a8642e3527141ae0", size = 804522, upload-time = "2026-07-19T00:18:31.362Z" }, + { url = "https://files.pythonhosted.org/packages/76/7c/cc4e7655181b2d9235b704f2c5e19d8eff002bbc437bae59baee0e381aca/regex-2026.7.19-cp313-cp313t-win32.whl", hash = "sha256:64b6ca7391a1395c2638dd5c7456d67bea44fc6c5e8e92c5dc8aa6a8f23292b4", size = 269141, upload-time = "2026-07-19T00:18:33.479Z" }, + { url = "https://files.pythonhosted.org/packages/bb/14/961b4c7b05a2391c32dbc85e27773076671ef8f97f36cec70fe414734c02/regex-2026.7.19-cp313-cp313t-win_amd64.whl", hash = "sha256:f04b9f56b0e0614c0126be12c2c2d9f8850c1e57af302bd0a63bed379d4af974", size = 280036, upload-time = "2026-07-19T00:18:35.419Z" }, + { url = "https://files.pythonhosted.org/packages/ce/67/795644550d788ddbb6dc458c95895f8009978ea6d6ea76b005eb3f45e8c9/regex-2026.7.19-cp313-cp313t-win_arm64.whl", hash = "sha256:fcee38cd8e5089d6d4f048ba1233b3ad76e5954f545382180889112ff5cb712d", size = 279394, upload-time = "2026-07-19T00:18:37.454Z" }, + { url = "https://files.pythonhosted.org/packages/d2/25/0c4c452f8ef3efe456745b2f33195f5904b573fb4c2ff3f0cb9ec188461e/regex-2026.7.19-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a81758ed242b861b72e778ba34d41366441a2e10b16b472784c88da2dea7e2dd", size = 496750, upload-time = "2026-07-19T00:18:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/24/9e/b70ca6c1704f6c7cd32a9e143c86cc5968d10981eca284bad670c245ea7d/regex-2026.7.19-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4aa5435cdb3eb6f55fe98a171b05e3fbcd95fadaa4aa32acf62afd9b0cfdbcac", size = 297093, upload-time = "2026-07-19T00:18:41.583Z" }, + { url = "https://files.pythonhosted.org/packages/87/74/0b692da2520d51fbff19c88b83d97e4c702909dd02386c585998b7e2dbed/regex-2026.7.19-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:60be8693a1dadc210bbcbc0db3e26da5f7d01d1d5a3da594e99b4fa42df404f5", size = 292043, upload-time = "2026-07-19T00:18:43.347Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a7/1d478e614016045a33feae57446215f9fd65b665a5ceb2f891fb3183bc52/regex-2026.7.19-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d19662dbedbe783d323196312d38f5ba53cf56296378252171985da6899887d3", size = 797214, upload-time = "2026-07-19T00:18:45.362Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ae/11b9c9411d92c30e3d2db32df5a31133e4a99a8fc397a604fd08f6c4bffb/regex-2026.7.19-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d15df07081d91b76ff20d43f94592ee110330152d617b730fdbe5ef9fb680053", size = 866433, upload-time = "2026-07-19T00:18:47.315Z" }, + { url = "https://files.pythonhosted.org/packages/b1/62/2b2efc4992f91d6d204b24c647c9f9412e85379d92b7c0ab9fdae622327e/regex-2026.7.19-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:56ad4d9f77df871a99e25c37091052a02528ec0eb059de928ee33956b854b45b", size = 911360, upload-time = "2026-07-19T00:18:49.588Z" }, + { url = "https://files.pythonhosted.org/packages/14/71/986ceea9aa3da548bf1357cad89b63915ec6d21ec957c8113b29ece567df/regex-2026.7.19-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7322ec6cc9fba9d49ab888bb82d67ac5625627aa168f0165139b17018df3fb8a", size = 801275, upload-time = "2026-07-19T00:18:51.767Z" }, + { url = "https://files.pythonhosted.org/packages/15/be/ce9d9534b2cda96eab32c548261224b9b4e220a4126f098f60f42ae7b4cd/regex-2026.7.19-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9c7472192ebfad53a6be7c4a8bfb2d64b81c0e93a1fc8c57e1dd0b638297b5d1", size = 777131, upload-time = "2026-07-19T00:18:54.053Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/58b5c710f2c3929515a25f3a1ca0dad0dcd4518d4fff3cf23bc7adb8dcd2/regex-2026.7.19-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c10b82c2634df08dfb13b1f04e38fe310d086ee092f4f69c0c8da234251e556e", size = 785020, upload-time = "2026-07-19T00:18:56.579Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/5fe091935b74f15fe0f97998c215cae418d1c0413f6258c7d4d2e83aa37f/regex-2026.7.19-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:17ed5692f6acc4183e98331101a5f9e4f64d72fe58b753da4d444a2c77d05b12", size = 861263, upload-time = "2026-07-19T00:18:58.64Z" }, + { url = "https://files.pythonhosted.org/packages/d8/fa/d60bf82e10841eef62a9e32aac401468f05fddfbcb2942e342b1ba3d2433/regex-2026.7.19-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:22a992de9a0d91bda927bf02b94351d737a0302905432c88a53de7c4b9ce62e2", size = 766199, upload-time = "2026-07-19T00:19:00.705Z" }, + { url = "https://files.pythonhosted.org/packages/bf/5d/11e64d151b0662b81d6bf644c74dc118d461df85bdf2577fadbbf751788a/regex-2026.7.19-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:618a0aed532be87294c4477b0481f3aa0f1520f4014a4374dd4cf789b4cd2c97", size = 851317, upload-time = "2026-07-19T00:19:03.015Z" }, + { url = "https://files.pythonhosted.org/packages/7c/34/532efb87488d90807bae6a443d357ee5e2728a478c597619c8aaa17cc0bd/regex-2026.7.19-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ce9e679f776649746729b6c86382da519ef649c8e34cc41df0d2e5e0f6c36d4", size = 789557, upload-time = "2026-07-19T00:19:05.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/90/3a8d5ca977171ec3ae21a71207d2228b2663bde14d7f7ef0e6363ecf9290/regex-2026.7.19-cp314-cp314-win32.whl", hash = "sha256:73f272fba87b8ccfe70a137d02a54af386f6d27aa509fbffdd978f5947aae1aa", size = 272531, upload-time = "2026-07-19T00:19:07.487Z" }, + { url = "https://files.pythonhosted.org/packages/96/e1/8862885e70409de70e8c005f57fb2e7be8d9ef0317250d60f4c9660a300d/regex-2026.7.19-cp314-cp314-win_amd64.whl", hash = "sha256:d721e53758b2cca74990185eb0671dd466d7a388a1a45d0c6f4c13cef41a68ac", size = 280831, upload-time = "2026-07-19T00:19:09.46Z" }, + { url = "https://files.pythonhosted.org/packages/08/82/2693e53e29f9104d9de95d37ce4dd826bd32d5f9c0085d3aa6ac042675c4/regex-2026.7.19-cp314-cp314-win_arm64.whl", hash = "sha256:65fa6cb38ed5e9c3637e68e544f598b39c3b86b808ed0627a67b68320384b459", size = 281099, upload-time = "2026-07-19T00:19:11.398Z" }, + { url = "https://files.pythonhosted.org/packages/92/b7/9a01aa16461a18cde9d7b9c3ab21e501db2ce33725f53014342b91df2b0a/regex-2026.7.19-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:5a2721c8720e2cb3c209925dfb9200199b4b07361c9e01d321719404b21458b3", size = 501121, upload-time = "2026-07-19T00:19:13.425Z" }, + { url = "https://files.pythonhosted.org/packages/f3/5e/bbaeca815dc9191c424c94a4fdc5c87c75748a64a6271821212ebdd4e1a3/regex-2026.7.19-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:199535629f25caf89698039af3d1ad5fcae7f933e2112c73f1cdf49165c99518", size = 299415, upload-time = "2026-07-19T00:19:15.43Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d6/0dd1a321afaab95eb7ff44aa0f637301786f1dc71c6b797b9ed236ed8890/regex-2026.7.19-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9b60d7814174f059e5de4ab98271cc5ba9259cfea55273a81544dceea32dc8d9", size = 294483, upload-time = "2026-07-19T00:19:17.879Z" }, + { url = "https://files.pythonhosted.org/packages/92/5f/40bacf91d0904f812e13bbbab3864604c463eced8afdc54aeaa50492ea95/regex-2026.7.19-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbece16025afda5e3031af0c4059207e61dcf73ef13af844964f57f387d1c435", size = 811833, upload-time = "2026-07-19T00:19:20.102Z" }, + { url = "https://files.pythonhosted.org/packages/94/7c/4902744261f775aeede8b5627314b38482da29cf49a57b66a6fb753246c5/regex-2026.7.19-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d24ecb4f5e009ea0bd275ee37ad9953b32005e2e5e60f8bbae16da0dbbf0d3a0", size = 871270, upload-time = "2026-07-19T00:19:22.365Z" }, + { url = "https://files.pythonhosted.org/packages/16/70/6980c9be6bf21c0a60ed3e0aea39cf419ecf3b08d1d9947bc56e196ef186/regex-2026.7.19-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8cae6fd77a5b72dae505084b1a2ee0360139faf72fedbab667cd7cc65aae7a6a", size = 917534, upload-time = "2026-07-19T00:19:24.529Z" }, + { url = "https://files.pythonhosted.org/packages/52/92/8b2bd872782ce8c42691e39acb38eb8efe014e5ddb78ad7d943d6f197ce9/regex-2026.7.19-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9724e6cb5e478cd7d8cabf027826178739cb18cf0e117d0e32814d479fa02276", size = 816135, upload-time = "2026-07-19T00:19:26.919Z" }, + { url = "https://files.pythonhosted.org/packages/de/2d/33a602f657bdc4041f17d79f92ab18261d255d91a06117a6e29df023e5e2/regex-2026.7.19-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:572fc57b0009c735ee56c175ea021b637a15551a312f56734277f923d6fd0f6c", size = 785492, upload-time = "2026-07-19T00:19:29.192Z" }, + { url = "https://files.pythonhosted.org/packages/9e/36/0987cf4cb271680064a70d24a475873775a151d0b7058698a006cb0cae4a/regex-2026.7.19-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:20568e182eb82d39a6bf7cff3fd58566f14c75c6f74b2c8c96537eecf9010e3a", size = 800658, upload-time = "2026-07-19T00:19:31.392Z" }, + { url = "https://files.pythonhosted.org/packages/a8/24/c14f31c135e1ba55fa4f9a58ca98d0842512bf6188230763c31c8f449e3b/regex-2026.7.19-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:1d58561843f0ff7dc78b4c28b5e2dc388f3eff94ebc8a232a3adba961fc00009", size = 865073, upload-time = "2026-07-19T00:19:33.485Z" }, + { url = "https://files.pythonhosted.org/packages/14/85/181a12211f22469f24d2de1ebddfe397d2396e2c29013b9a58134a91069a/regex-2026.7.19-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:61bb1bd45520aacd56dd80943bd34991fb5350afdd1f36f2282230fd5154a218", size = 773684, upload-time = "2026-07-19T00:19:35.599Z" }, + { url = "https://files.pythonhosted.org/packages/23/58/bd1a0c1a62251366f8d21f41b1ea3c76994962071b8b6ea42f72d505c0f0/regex-2026.7.19-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:cd3584591ea4429026cdb931b054342c2bcf189b44ff367f8d5c15bc092a2966", size = 857769, upload-time = "2026-07-19T00:19:37.738Z" }, + { url = "https://files.pythonhosted.org/packages/e4/4f/f7e2dad6756b2fe1fe75dd90a628c3b45f249d39f948dd90cd2476325417/regex-2026.7.19-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5cc26a66e212fa5d6c6170c3a40d99d888db3020c6fdab1523250d4341382e44", size = 804546, upload-time = "2026-07-19T00:19:40.229Z" }, + { url = "https://files.pythonhosted.org/packages/2b/d7/01d31d5bdb09bc026fab77f59a371fdf8f9b292e4810546c56182ca70498/regex-2026.7.19-cp314-cp314t-win32.whl", hash = "sha256:2c4e61e2e1be56f63ec3cc618aa9e0de81ef6f43d177205451840022e24f5b78", size = 274526, upload-time = "2026-07-19T00:19:42.398Z" }, + { url = "https://files.pythonhosted.org/packages/52/0e/cea4ce73bc0a8247a0748228ae6669984c7e1f8134b6fa66e59c0572e0ea/regex-2026.7.19-cp314-cp314t-win_amd64.whl", hash = "sha256:c639ea314df70a7b2811e8020448c75af8c9445f5a60f8a4ced81c306a9380c2", size = 283763, upload-time = "2026-07-19T00:19:44.644Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b6/26e41975febae63b7a6e3e02f32cff6cff2e4f10d19c929082f56aebf7c6/regex-2026.7.19-cp314-cp314t-win_arm64.whl", hash = "sha256:9a15e785f244f3e07847b984ce8773fc3da10a9f3c131cc49a4c5b4d672b4547", size = 283451, upload-time = "2026-07-19T00:19:46.639Z" }, +] + +[[package]] +name = "reportlab" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "charset-normalizer" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/d6/4b7b0cf56880eb96533e607967be6a939e344675601e033d113a0bfa1f4e/reportlab-5.0.0.tar.gz", hash = "sha256:e4494a0c6623ae213bb856fba523171b2b54a7bf629fda02d5e525a7b899a784", size = 3701928, upload-time = "2026-06-18T11:34:31.145Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/07/70085c17a369605f15e301d10ab902115019b1126c7253d964afc230c7d6/reportlab-5.0.0-py3-none-any.whl", hash = "sha256:9d5a3affa84919e1111ede580031266a570e93b1ce388219621347965ff1d93c", size = 1956710, upload-time = "2026-06-18T11:34:29.07Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "robust-downloader" +version = "0.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorlog" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/20/8d28efa080f58fa06f6378875ac482ee511c076369e5293a2e65128cf9a0/robust-downloader-0.0.2.tar.gz", hash = "sha256:08c938b96e317abe6b037e34230a91bda9b5d613f009bca4a47664997c61de90", size = 15785, upload-time = "2023-11-13T03:00:20.637Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/a1/779e9d0ebbdc704411ce30915a1105eb01aeaa9e402d7e446613ff8fb121/robust_downloader-0.0.2-py3-none-any.whl", hash = "sha256:8fe08bfb64d714fd1a048a7df6eb7b413eb4e624309a49db2c16fbb80a62869d", size = 15534, upload-time = "2023-11-13T03:00:18.957Z" }, +] + +[[package]] +name = "safetensors" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/06/f955dbbb1859e3bd23c8ac6141af5106e7ad5fedec4a3a6e3d60f94b7001/safetensors-0.8.0.tar.gz", hash = "sha256:fabaf3e0f18a6618d9b36560682562157f77c2b71fcffc7b432be2baed9d753d", size = 325846, upload-time = "2026-06-09T07:52:25.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/a0/f718cda65b05407d228f97602cf60dca269c979867aa5beb25410de26cd3/safetensors-0.8.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c554f85858e05226d3c2828e32395e677434685d6d94594a41643361c5e837f0", size = 473568, upload-time = "2026-06-09T07:52:18.829Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b1/fa7c600e7dceae12e9606c7578cbc9ff1e1ed55844883ee5c92205e86226/safetensors-0.8.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c80201d22cbf405b80647a60ada77bba06c8fba2da2743ba1e89cdcc39a81f25", size = 484562, upload-time = "2026-06-09T07:52:17.518Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/65a7de0af421317bb36a067241e4235fff194eed60b961ed6d3f59a3fc60/safetensors-0.8.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a46e5ff292c356d6991e60942ba7f79817682d3a2cef0702136448cb9c4d235", size = 502844, upload-time = "2026-06-09T07:52:07.624Z" }, + { url = "https://files.pythonhosted.org/packages/91/4f/3175c9d75634e0e0dda0082794193521035edd7c70a6f212bf33ca06ddf4/safetensors-0.8.0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4124502b78f03534117c848f87a39b8f31e577b15eff423bf8bfb95f2a8c30d0", size = 511823, upload-time = "2026-06-09T07:52:09.565Z" }, + { url = "https://files.pythonhosted.org/packages/20/87/846c289e7aa2299eff406335717cf43ce8777194ece8aad75772e0411615/safetensors-0.8.0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bc0a787ba8a35be368ee3574edfa2b1ad389eebd0a72e482ae275490e3f6c98", size = 633461, upload-time = "2026-06-09T07:52:11.128Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/8d64d9df2c45d5ded401df889d0ad90882804ca172d79ec4f0df8f727fe0/safetensors-0.8.0-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040070828e36dc8e122178bbbd5830ff9e97920affb84cbe0f46442497bed358", size = 545148, upload-time = "2026-06-09T07:52:13.603Z" }, + { url = "https://files.pythonhosted.org/packages/28/50/f203ff3a3ddfe19308efc83c5a3a29ed02bf786732ec35e68bf9162f3365/safetensors-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd6f3f93c9a0a7cc2788ee63fb763353d4bd2e89b0751bc78fcf7dda00bea774", size = 516040, upload-time = "2026-06-09T07:52:16.29Z" }, + { url = "https://files.pythonhosted.org/packages/46/fb/cdaed17ceb2948784fd9c36b6fd3e951b608547cea81a48e8ee6f8cfdfcb/safetensors-0.8.0-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:fcdd41ec4628fee5799f807c73c353629130fbd942aa23d83c623dd6c9d52d78", size = 513832, upload-time = "2026-06-09T07:52:12.37Z" }, + { url = "https://files.pythonhosted.org/packages/0d/49/1e15de264dcc3b77943d2d0c56a95809956883b1c2d6d585c792523f180b/safetensors-0.8.0-cp310-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e9f537aa183a38ace122d27303dcd986b26bd2a7591f9181d7f0c396f4677ca", size = 559930, upload-time = "2026-06-09T07:52:14.743Z" }, + { url = "https://files.pythonhosted.org/packages/2a/43/bf38443278eab4b1be1fce2931e2b012ad9cb7df52ada751d0aab8f7659a/safetensors-0.8.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87eec7ffed2b809f05a398a8becb7d013f19f7837cd15d9748580d6cf30dbaf4", size = 678670, upload-time = "2026-06-09T07:52:20.032Z" }, + { url = "https://files.pythonhosted.org/packages/72/e3/68cd3fa5b48488e84add63e04cb12f3bc28ae4638c06d4508c6e88823d0e/safetensors-0.8.0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:4a95ae2b05d7726d751da4ebf626a2ca782b706e101bd894c95bc2450b1cffcc", size = 786679, upload-time = "2026-06-09T07:52:21.322Z" }, + { url = "https://files.pythonhosted.org/packages/29/4b/1c19c509d56e01f4fbb3d0a2e597450f6cc04d1d56cf52defb0a62dfd715/safetensors-0.8.0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae091f16662658bdc019a4ff6cb4c085bb7d725eb5978b183ffd265863b6d2d", size = 765683, upload-time = "2026-06-09T07:52:22.594Z" }, + { url = "https://files.pythonhosted.org/packages/27/43/41c1621732edd934d868a00d1b891584c892a7b62a9aab82ea5a0a5623ee/safetensors-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8e080062fcde23be189565e1c3305d16751a218ecf9412c8601e64204eb6f846", size = 722361, upload-time = "2026-06-09T07:52:23.924Z" }, + { url = "https://files.pythonhosted.org/packages/8e/3f/73ccf82579412b4a71c4ca673f10b5f1f888d7cf5af7fe24f27d30307be4/safetensors-0.8.0-cp310-abi3-win32.whl", hash = "sha256:2ddf52eac562eda224f99acfa7889d02968c1fd59a5b011ae7d8137c37e9c02d", size = 342401, upload-time = "2026-06-09T07:52:28.895Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6d/3fba214c1e5e0f69991677ec3bc17023f0421776975e1de0c682dca475e2/safetensors-0.8.0-cp310-abi3-win_amd64.whl", hash = "sha256:096ec1a98435df7beb08853bb5aa9081a84f23d0adc67ed1a0a10550f608373f", size = 355540, upload-time = "2026-06-09T07:52:27.832Z" }, + { url = "https://files.pythonhosted.org/packages/8d/fc/7eedc3510d97878876e32774eebbeb61c43f148a96e915c84229a3e967aa/safetensors-0.8.0-cp310-abi3-win_arm64.whl", hash = "sha256:f7838e5135a406ad3e02efdcb8cf2e5397d368b0154537c4fec682dbc544d452", size = 340500, upload-time = "2026-06-09T07:52:26.745Z" }, +] + +[[package]] +name = "setuptools" +version = "81.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, +] + +[[package]] +name = "shapely" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/90/98ef257c23c46425dc4d1d31005ad7c8d649fe423a38b917db02c30f1f5a/shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8", size = 1832644, upload-time = "2025-09-24T13:50:44.886Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ab/0bee5a830d209adcd3a01f2d4b70e587cdd9fd7380d5198c064091005af8/shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a", size = 1642887, upload-time = "2025-09-24T13:50:46.735Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5e/7d7f54ba960c13302584c73704d8c4d15404a51024631adb60b126a4ae88/shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e", size = 2970931, upload-time = "2025-09-24T13:50:48.374Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a2/83fc37e2a58090e3d2ff79175a95493c664bcd0b653dd75cb9134645a4e5/shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6", size = 3082855, upload-time = "2025-09-24T13:50:50.037Z" }, + { url = "https://files.pythonhosted.org/packages/44/2b/578faf235a5b09f16b5f02833c53822294d7f21b242f8e2d0cf03fb64321/shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af", size = 3979960, upload-time = "2025-09-24T13:50:51.74Z" }, + { url = "https://files.pythonhosted.org/packages/4d/04/167f096386120f692cc4ca02f75a17b961858997a95e67a3cb6a7bbd6b53/shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd", size = 4142851, upload-time = "2025-09-24T13:50:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/48/74/fb402c5a6235d1c65a97348b48cdedb75fb19eca2b1d66d04969fc1c6091/shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350", size = 1541890, upload-time = "2025-09-24T13:50:55.337Z" }, + { url = "https://files.pythonhosted.org/packages/41/47/3647fe7ad990af60ad98b889657a976042c9988c2807cf322a9d6685f462/shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715", size = 1722151, upload-time = "2025-09-24T13:50:57.153Z" }, + { url = "https://files.pythonhosted.org/packages/3c/49/63953754faa51ffe7d8189bfbe9ca34def29f8c0e34c67cbe2a2795f269d/shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40", size = 1834130, upload-time = "2025-09-24T13:50:58.49Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ee/dce001c1984052970ff60eb4727164892fb2d08052c575042a47f5a9e88f/shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b", size = 1642802, upload-time = "2025-09-24T13:50:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/da/e7/fc4e9a19929522877fa602f705706b96e78376afb7fad09cad5b9af1553c/shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801", size = 3018460, upload-time = "2025-09-24T13:51:02.08Z" }, + { url = "https://files.pythonhosted.org/packages/a1/18/7519a25db21847b525696883ddc8e6a0ecaa36159ea88e0fef11466384d0/shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0", size = 3095223, upload-time = "2025-09-24T13:51:04.472Z" }, + { url = "https://files.pythonhosted.org/packages/48/de/b59a620b1f3a129c3fecc2737104a0a7e04e79335bd3b0a1f1609744cf17/shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c", size = 4030760, upload-time = "2025-09-24T13:51:06.455Z" }, + { url = "https://files.pythonhosted.org/packages/96/b3/c6655ee7232b417562bae192ae0d3ceaadb1cc0ffc2088a2ddf415456cc2/shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99", size = 4170078, upload-time = "2025-09-24T13:51:08.584Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8e/605c76808d73503c9333af8f6cbe7e1354d2d238bda5f88eea36bfe0f42a/shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf", size = 1559178, upload-time = "2025-09-24T13:51:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/36/f7/d317eb232352a1f1444d11002d477e54514a4a6045536d49d0c59783c0da/shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c", size = 1739756, upload-time = "2025-09-24T13:51:12.105Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223", size = 1831290, upload-time = "2025-09-24T13:51:13.56Z" }, + { url = "https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c", size = 1641463, upload-time = "2025-09-24T13:51:14.972Z" }, + { url = "https://files.pythonhosted.org/packages/a5/57/91d59ae525ca641e7ac5551c04c9503aee6f29b92b392f31790fcb1a4358/shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df", size = 2970145, upload-time = "2025-09-24T13:51:16.961Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf", size = 3073806, upload-time = "2025-09-24T13:51:18.712Z" }, + { url = "https://files.pythonhosted.org/packages/03/83/f768a54af775eb41ef2e7bec8a0a0dbe7d2431c3e78c0a8bdba7ab17e446/shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4", size = 3980803, upload-time = "2025-09-24T13:51:20.37Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/559c7c195807c91c79d38a1f6901384a2878a76fbdf3f1048893a9b7534d/shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc", size = 4133301, upload-time = "2025-09-24T13:51:21.887Z" }, + { url = "https://files.pythonhosted.org/packages/80/cd/60d5ae203241c53ef3abd2ef27c6800e21afd6c94e39db5315ea0cbafb4a/shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566", size = 1583247, upload-time = "2025-09-24T13:51:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c", size = 1773019, upload-time = "2025-09-24T13:51:24.873Z" }, + { url = "https://files.pythonhosted.org/packages/a3/05/a44f3f9f695fa3ada22786dc9da33c933da1cbc4bfe876fe3a100bafe263/shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a", size = 1834137, upload-time = "2025-09-24T13:51:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/4d57db45bf314573427b0a70dfca15d912d108e6023f623947fa69f39b72/shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076", size = 1642884, upload-time = "2025-09-24T13:51:28.029Z" }, + { url = "https://files.pythonhosted.org/packages/5a/27/4e29c0a55d6d14ad7422bf86995d7ff3f54af0eba59617eb95caf84b9680/shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1", size = 3018320, upload-time = "2025-09-24T13:51:29.903Z" }, + { url = "https://files.pythonhosted.org/packages/9f/bb/992e6a3c463f4d29d4cd6ab8963b75b1b1040199edbd72beada4af46bde5/shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0", size = 3094931, upload-time = "2025-09-24T13:51:32.699Z" }, + { url = "https://files.pythonhosted.org/packages/9c/16/82e65e21070e473f0ed6451224ed9fa0be85033d17e0c6e7213a12f59d12/shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26", size = 4030406, upload-time = "2025-09-24T13:51:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/7c/75/c24ed871c576d7e2b64b04b1fe3d075157f6eb54e59670d3f5ffb36e25c7/shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0", size = 4169511, upload-time = "2025-09-24T13:51:36.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f7/b3d1d6d18ebf55236eec1c681ce5e665742aab3c0b7b232720a7d43df7b6/shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735", size = 1602607, upload-time = "2025-09-24T13:51:37.757Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f6/f09272a71976dfc138129b8faf435d064a811ae2f708cb147dccdf7aacdb/shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9", size = 1796682, upload-time = "2025-09-24T13:51:39.233Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/99/a6ca3beb3ccacb41fb3321d8a60e5566f9e6467601ef8eba6a17e1b89778/soupsieve-2.9.2.tar.gz", hash = "sha256:4a55d8cf158a9c2e587fa4922f1bbb91d68ac829e2d6f25403a85747c71daf74", size = 122445, upload-time = "2026-08-07T00:57:24.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl", hash = "sha256:8089a26fd974ca7a1f30276d3d8492ab266ab15af581642dfe8aa162e0c1c823", size = 37370, upload-time = "2026-08-07T00:57:23.524Z" }, +] + +[[package]] +name = "starlette" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/b4/205b0d5241d934e8add0c38aa924c4f9fb7330834ff11e5444db964ec3f9/starlette-1.6.0.tar.gz", hash = "sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b", size = 2716969, upload-time = "2026-08-08T18:27:57.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "termcolor" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, +] + +[[package]] +name = "torch" +version = "2.11.0+cu128" +source = { registry = "https://download.pytorch.org/whl/cu128" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cudnn-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7db3580106bba044da5b8950f3fb8fe5f31999eaab3f6a3aa2ac5d202c3684d2", upload-time = "2026-04-27T17:45:35Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:db964b33c55035a72ab3e2162287af8f1cc276039c65d015740cc88c26dcedf7", upload-time = "2026-04-27T17:46:18Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:6f367e62fd81b75cdf23ca4b75ced834d2db2cf98d1588ac935bde345de9de23", upload-time = "2026-04-27T17:48:09Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd1cf1005c5fe419194ee294b7b584ba5ad0f2fb1778b3fe5a7b9c3f4617ddbc", upload-time = "2026-04-27T17:50:01Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:74b628dbc71603977b09f4e140792c6e997081a35ef3421555f3f6e201b81210", upload-time = "2026-04-27T17:50:42Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:c2a5984deba8e001d166bf9cb83b8351f63a28b009e1a2fa0e4bbf08c90b259b", upload-time = "2026-04-27T17:52:32Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:baa52f7b8a53cab16587b10f1c27d1000ca033f97236878b685b75d5a1b92408", upload-time = "2026-04-27T17:54:24Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d389a850677f0d24dafae1573644034428d8d3b9c80b51d55ba62fed7e6c8777", upload-time = "2026-04-27T17:55:03Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314-win_amd64.whl", hash = "sha256:d6c21797ff75271b4fbdd905e2d703be4ecea5ea5bbdde4d1c201e9c71bc411d", upload-time = "2026-04-27T17:56:46Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:06849e9311dbb0617c97557d9c26c99a9e1c4f2ac9cb8e9b6d9b420d522acb91", upload-time = "2026-04-27T17:58:48Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:169a9987e1f84f0c5eee07544b3a34827a163ac9180e23abf0c3548f1335762c", upload-time = "2026-04-27T17:59:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.11.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:d86c125d720c2c368c53bd1a4ef062916d91fa965c10448c74c78b5d039faf2d", upload-time = "2026-04-27T18:01:14Z" }, +] + +[[package]] +name = "torchvision" +version = "0.26.0+cu128" +source = { registry = "https://download.pytorch.org/whl/cu128" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c4a9cacd521f2a4df0bcd9d8e96704771b928f478f1f3067e4085bb53a1da298", upload-time = "2026-04-09T23:21:37Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cb1f6184a7ba30fba40580e1a01a6604a86c55e79fdda187f40116ee680441ec", upload-time = "2026-03-23T15:36:22Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:0232cb219927a52d6c98ff202f32d1cdf4802c2195a85fc1f1a0c1b0b4983a4d", upload-time = "2026-04-09T23:21:38Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e594732552a8c2fee2ace9c6475c6c6904fc44ccca622ee6765a89a045416a44", upload-time = "2026-04-09T23:21:38Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6168abc019803ac9e97efce27eafd2fdb33db04dcc54a86039537729e5047b29", upload-time = "2026-03-23T15:36:23Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:367d42ea703844ecdb516e9d5eb09929012a58705d2622cf4e9e3c37f278cb85", upload-time = "2026-04-09T23:21:39Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:b3865fa227661dd75b7b28c96d3d14e739bd08bf0614132758922fe0e7206f91", upload-time = "2026-04-09T23:21:39Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:aac647c9130f1f25f5c8f5bca3d95cfd96bdfac93ab54529690b088e64e4fa64", upload-time = "2026-03-23T15:36:23Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314-win_amd64.whl", hash = "sha256:6319e1ba49c6f62ac9902f73d0eab207b8a4dc6b4d3392fe9edd9903fff1be0a", upload-time = "2026-04-09T23:21:40Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:e2ee9e16ee4518292694537fcbd20d2d27044e381d92b864f637e82795796a84", upload-time = "2026-04-09T23:21:40Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:b5772c55bfda4377df8f1930d43c4e0231ef231b0228eade4b227c8d3ba6e34e", upload-time = "2026-03-23T15:36:23Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torchvision-0.26.0%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:f160dc552a086244f7102c898f7be8ef46a41b36bce5ea80a4f2493cb30ca1fc", upload-time = "2026-04-09T23:21:41Z" }, +] + +[[package]] +name = "tqdm" +version = "4.70.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/3b/6c24bec5be5e743ffd99576daa5cc077722fc7d5bbc00bd133fa0c698dc6/tqdm-4.70.0.tar.gz", hash = "sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220", size = 795438, upload-time = "2026-07-27T11:33:15.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/1c/01bfd571a64e7f270e6bab5e33777debe0edc56759233ce84f27dec92d14/tqdm-4.70.0-py3-none-any.whl", hash = "sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953", size = 80184, upload-time = "2026-07-27T11:33:13.167Z" }, +] + +[[package]] +name = "transformers" +version = "4.57.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912, upload-time = "2026-01-16T10:38:39.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498, upload-time = "2026-01-16T10:38:31.289Z" }, +] + +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" }, + { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, + { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" }, + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, + { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" }, + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.52.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/28/64ca011edf31c715b4fad359c587ea52391aaffa125065695590241ff617/uvicorn-0.52.3.tar.gz", hash = "sha256:18857b9e6579300be55c91c0a1cfd37d9a2cf0cabea33b88275f199eb73b8b58", size = 100621, upload-time = "2026-08-13T16:50:02.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/2b/ebd108734a8204c6b4b93c681c9a38c5273b3ccd5d129fee4ffc1d97772c/uvicorn-0.52.3-py3-none-any.whl", hash = "sha256:116af2710dbf47c80f463cd20ee4884b6662f4c9f227d797ddc7279d2fcc2c7c", size = 79859, upload-time = "2026-08-13T16:50:01.323Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253, upload-time = "2026-06-29T18:11:11.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, +] + +[[package]] +name = "wrapt" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/b0/c1f5a970721f06b85c0cd5142e0ff8fe067708abd779b0c4f4be7d61d09f/wrapt-2.3.0.tar.gz", hash = "sha256:681a2d0eefd721998f90642762b8e75c2159ec531b20ad5e437245ea7b06a107", size = 131509, upload-time = "2026-07-28T06:06:14.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/6e/0f88a072483e76b881e3fdcd6b6ffb4a5791002514fe541e72b1b73c859a/wrapt-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d3fb71e65b001adfc42684522eeccd9c21d8ba679945abc993439567b66e59f", size = 81960, upload-time = "2026-07-28T06:04:49.622Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ff/b7e2776e7c294075eb712cc9ef573d1b818f393006d09787262b8fc871c4/wrapt-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51a7a4181c1295774812271fbcd7c909df372bc25579d4ed9eb875caaf0ae86f", size = 82435, upload-time = "2026-07-28T06:04:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/d8/90/343bb5d0f1f9669bc252a6073f085b4abf862511bd5c9c9eaec754341f1d/wrapt-2.3.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9045917809c63fdf7abe3a2ceaed3d670b8ee4500ddd9291192d30aeb34467c5", size = 170350, upload-time = "2026-07-28T06:04:52.187Z" }, + { url = "https://files.pythonhosted.org/packages/59/f8/13b79a392930bd0dd6b86cbfbfe1c40944110456e1dc6d809e5c46ece904/wrapt-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54ca1d5573f69b5fe1d74f1f65799c68015e82f685efec9fd8cfa40a094c44d0", size = 170022, upload-time = "2026-07-28T06:04:53.599Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fc/4f1b6918f5290db959d6e0c07f77385d87cede29c39c9cf8f145e9c82954/wrapt-2.3.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:242b60c21e30866e6a2fa606c612b47c553fa60c0eaeeeb7797fb842ac0ce609", size = 161043, upload-time = "2026-07-28T06:04:54.936Z" }, + { url = "https://files.pythonhosted.org/packages/01/e1/45d3cf74414780bdff6d0380467e003f6eb0f028b6c9403db868dbc7209c/wrapt-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3f3d7ec0a51fbfe00d3aef047641ff2c58b25565b4717fc1f90e050be01cba8", size = 168576, upload-time = "2026-07-28T06:04:56.261Z" }, + { url = "https://files.pythonhosted.org/packages/f3/73/2fa58dd97f191c997755e2c6d569a68f0c433db4e4b36099bdd7227b6cac/wrapt-2.3.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:261f53870cd4fb2bf38f9f972c56c728fd224cb7c65721307de59d9e7e6741ae", size = 159140, upload-time = "2026-07-28T06:04:57.754Z" }, + { url = "https://files.pythonhosted.org/packages/29/a8/08a56e2000a8816d449dcbad8c8b081697acbbd490821ceca0f9d8e8d20c/wrapt-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8159ec0b0cb7608175eb150de94c19e34f4d47ac655f5ca9baf45df6b688ffd3", size = 169263, upload-time = "2026-07-28T06:04:59.161Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d4/354e1725e35a73b2af4fa70a3e024c7a5d1bf1802dfb862dcb668aae0253/wrapt-2.3.0-cp313-cp313-win32.whl", hash = "sha256:10461884b3014fbfc8eb7d09a93c5f246363e6711d9d881f95eb8c27fdef049f", size = 78241, upload-time = "2026-07-28T06:05:00.507Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7e/34c87fa2174848dfee820322aaa318bab08913998ccecc8d2f57b4ad4639/wrapt-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac870cc97b73bb00ac353329e9559a4bebc47c4c86792ed9b23b58c15b6ad838", size = 81113, upload-time = "2026-07-28T06:05:01.839Z" }, + { url = "https://files.pythonhosted.org/packages/11/86/fcc9a530579e008c9478bb565a6cdfbfd33536660f069c8b91a6607c5050/wrapt-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:a65e8db2b4e90c2e7ade931086351c98ef420bf7a94ee08c95ac8a3cbbc43579", size = 80182, upload-time = "2026-07-28T06:05:03.152Z" }, + { url = "https://files.pythonhosted.org/packages/96/50/3864848b95b28ef73e17551fc8dccbff2628a834f52cf26a57f9c419fb83/wrapt-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:fd1f2f557dd3491fe75905e578f4db967393d40d1a8f468edc4d40ac7f2d5944", size = 83921, upload-time = "2026-07-28T06:05:04.476Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4c/3d1921a60c3e8c71c540ff136e6a47a1fbccf7f671e818394889f7871d9c/wrapt-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9f5d2aec29dfc76c37e23897dee92766a3fd4f3bff3ae7fc9c6b4bf37d8c1360", size = 84412, upload-time = "2026-07-28T06:05:05.921Z" }, + { url = "https://files.pythonhosted.org/packages/fa/1a/4a796ff7adb26ada6d4b758c94d47a38320b085e7099afc088efbbcdb006/wrapt-2.3.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:646d20d413ffcd1b0a2f700076e2d0252d872dcb7754860a73e45a59ea883614", size = 207168, upload-time = "2026-07-28T06:05:07.256Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3e/d7777776806c579b761bac2f91721dda9f04c7a1b380213c5935cc750ae6/wrapt-2.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:379f670f45b7bb8993edd9f6fc36c6cc65edb81cffa0b504be34acb0303fff0a", size = 214351, upload-time = "2026-07-28T06:05:08.945Z" }, + { url = "https://files.pythonhosted.org/packages/63/27/2d64d394df7bf181955b3bb562bf33c4492fb4be113f53071106d43ad8b5/wrapt-2.3.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6208f302f110295d64b22a7ac96500c791bf492dce4366e622e4912b077c9687", size = 199020, upload-time = "2026-07-28T06:05:10.418Z" }, + { url = "https://files.pythonhosted.org/packages/3e/3d/fb31d3db7d9834d265fb1a27a2adf0ddf51557c67458c97b22439ad6ae3d/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ed635a9ca4f3a5a2b900c10c69e823373bc00ebc114b459383596d3487da3570", size = 209969, upload-time = "2026-07-28T06:05:11.983Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d1/8724b5da582e62070dc9bf4d8bf1972f317297eefd7ba1f2b5c6393ccf6c/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e3b9eaa742ae7a0aaaaad4ca4b69469d757af2d6e6663ef1dadc47adec0aeb41", size = 196324, upload-time = "2026-07-28T06:05:13.557Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5c/3d9ef411149543016ee6bcf3af707f787cebd946527452b94bf122e9b7b4/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0f7284f88f4833705132d06d3b425a43095c2cbd07c58166aac3ab646ba12a4", size = 202610, upload-time = "2026-07-28T06:05:15.048Z" }, + { url = "https://files.pythonhosted.org/packages/13/9b/4fc042ceb757866dd4a5fc057b3b736f2b360d3703ce9f830d83dc9226e0/wrapt-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:7ebb274aba688b043429eb1500ff8a76ce0cb8ac0812ca3e301f06247b8722b3", size = 79178, upload-time = "2026-07-28T06:05:16.469Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ff/b94878f8eed809ca042685276bcea9f24e8c2ca7c9653bb80bbb920a68a5/wrapt-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c4bded758ad6f03b965830944a2f0bc5b2eb3767fe5a7310134315d1a6610e98", size = 82634, upload-time = "2026-07-28T06:05:18.026Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/663e1de5332a71685a729754312d327d4cada767c36e1c5a2db4c8de49e6/wrapt-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:d2cc64539da63e39ffb9c7ede849b6e8ddaaf7b3876b5cfb04efd85a5f3f4eb6", size = 81387, upload-time = "2026-07-28T06:05:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/58/10/b073beaea89bc0d3670a75ff51139430a54b6af7ba7796507730634536dd/wrapt-2.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ea52a0d0f08c584943d5764be0e84efa912c8da23c23e1e285ff2f5641c18fcc", size = 81978, upload-time = "2026-07-28T06:05:21.133Z" }, + { url = "https://files.pythonhosted.org/packages/b3/31/0916d9cebf848ed3f1a0c1888faee421747df77331e4db2bc527a9a85988/wrapt-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd85b0aa88efdb189d6ae2f35f4526943a8f091c38599c9c31478241c819e6a1", size = 82518, upload-time = "2026-07-28T06:05:22.562Z" }, + { url = "https://files.pythonhosted.org/packages/f5/73/31c1bf0f3384062751c2094dadb314916d70aa9b6bfd26d994b4a7b393fa/wrapt-2.3.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:141ed6211286a9660d8d6702de598b43f0934b4f0eda16393f100a80f501d945", size = 170187, upload-time = "2026-07-28T06:05:23.904Z" }, + { url = "https://files.pythonhosted.org/packages/ed/25/fce087d54b79b8905f3c3c9dd5f454bbd8d8acb80b960c4a6aee5b4659b3/wrapt-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e49885a62ec4ee854d1b9e6371fda6afd219917225752abf729a3f36d4df9a5", size = 169288, upload-time = "2026-07-28T06:05:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/c7/30/0d09e6dddc6b7a7230ac77f50254b5980ab4fcd22976f72f8cc8a0404458/wrapt-2.3.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d6159c9b2fefec02314e1332dbbbfaf960e369dfd26bcf7f8b258b5732065b3", size = 160932, upload-time = "2026-07-28T06:05:27.022Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ca/0913af0d2ec0c43865d32d615f518fea66c13c5c930e489e9b0de248e9a8/wrapt-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24da48596326ef8e448cfa837b454f638713d3531262375f00e5a9681682fc07", size = 169017, upload-time = "2026-07-28T06:05:28.501Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f2/3d1e47ea81b822210f5df1bf942fd90780a75c055243d569b664529dea88/wrapt-2.3.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cd3a2edf0427013736b8127955cec62608c56e53ea47e82812ea32059cda407f", size = 159065, upload-time = "2026-07-28T06:05:30.01Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/ef2066ced8e5fca204e2b361e9708e36555b40949c583d997ea3b590817d/wrapt-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0df3bff4e7ce45759f33fd39335fe2f60477bb9ecf7b8aa41e7d07ee36a23", size = 168821, upload-time = "2026-07-28T06:05:31.649Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e1/016104650d4e572fa91506eb396b3dd8efbccc9284fdc1c9479c3d21db28/wrapt-2.3.0-cp314-cp314-win32.whl", hash = "sha256:2935d5454b3f179a29b12cf390ee47246740ba2c3a7545b1b46ba31a5f2a4a0b", size = 78700, upload-time = "2026-07-28T06:05:33.391Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/6fdc20a9f2ca304748b3f0819cbf377d55260562777bf0b615431bc3c181/wrapt-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:cc2cea812e5cb179a796b766747e7d3b21088760d8deb95676d482b8c8e6fa7d", size = 81422, upload-time = "2026-07-28T06:05:34.774Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a4/9cbd53bf05746bea2c392af39cb052427a8ec95cbd494d930733d8f44681/wrapt-2.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:22cc5c0a717bd4da87018ae0bffd4c19c6fb679d3ff357216ba566ab26c76cab", size = 80639, upload-time = "2026-07-28T06:05:36.228Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/6c5e4a0f66ea0d2b2dd267e8dd05a0014eea56840b3c8595d40b0a5d1f91/wrapt-2.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a6b5984cd65dd639546f0eb4b8eacf1c31cb2fe9fb5c27bffe240987cdb2cf84", size = 84030, upload-time = "2026-07-28T06:05:37.714Z" }, + { url = "https://files.pythonhosted.org/packages/6a/eb/a1aedf03283bc9cbf8a1783995ddc54e3c5a86878f19002d2c428494f4c5/wrapt-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c88abcf53daef80e01a75c7530e727fa6e2c1888fe83e3dcdba4c96216a1f5c7", size = 84419, upload-time = "2026-07-28T06:05:39.131Z" }, + { url = "https://files.pythonhosted.org/packages/63/61/50d511c0dc5105563849e86daa3e16ac7feef699f79fb05af45ea70107d5/wrapt-2.3.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:85de890ff968196e92dd1ae73a9fb8970495e7650a457b1c9ef0ac3dd550bce2", size = 207171, upload-time = "2026-07-28T06:05:40.69Z" }, + { url = "https://files.pythonhosted.org/packages/3f/59/9b538cf7795217e810699d16bc88b96a830d9b5c403eb2ec2db6b5f2ae81/wrapt-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50f416b74d092bb9f41b424e90dd457f365f7ba4b11de62a23679769a21bd85c", size = 214329, upload-time = "2026-07-28T06:05:42.287Z" }, + { url = "https://files.pythonhosted.org/packages/b3/28/9935d62b1499e5c8b3d191e99ba4eb31ca237a0b699142011a837e9dc7ea/wrapt-2.3.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39febbee6d77301d31da6996b152ce52452da7c7ef72aba10c2fa976dff9c295", size = 199079, upload-time = "2026-07-28T06:05:43.958Z" }, + { url = "https://files.pythonhosted.org/packages/2b/01/4446b80fa2ffa47a3449b250d004ba1c1937f07f64a179608fec735df866/wrapt-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:93513bec052c6cd987f9f580c3df068c8bc4ebae6543736be3ca7ec5959cafcd", size = 209992, upload-time = "2026-07-28T06:05:45.677Z" }, + { url = "https://files.pythonhosted.org/packages/d4/07/56f26c9f9979586a021e8148747004aba4498f49458c90b0502969b904e1/wrapt-2.3.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:729126e667da34d251b8ebf8a45ef0c5ddadc21542b3d6e1abf4259ece6508df", size = 196334, upload-time = "2026-07-28T06:05:47.608Z" }, + { url = "https://files.pythonhosted.org/packages/8b/41/6d7bcc895b0f28b2250e10908f060687b9165429dcd7f22ddb3d4c031b74/wrapt-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:626b69db2021aa01671ec7bbc9740e558522bd44c18cf2ce69bf3d666a014109", size = 202644, upload-time = "2026-07-28T06:05:49.183Z" }, + { url = "https://files.pythonhosted.org/packages/cd/25/7860927edba06b758b8852a6f02e832be715563c67a6795d94350bc81099/wrapt-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:629d73378082c00a8173031f9fb30a3ac6abbc894a5bfdfae71fabc60642d501", size = 79685, upload-time = "2026-07-28T06:05:50.976Z" }, + { url = "https://files.pythonhosted.org/packages/c4/0f/270bafe92fde3b069a39bc01e39ee79340895b335640df861d43d2a51885/wrapt-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:42869085687f0aefd57c0f636c3f9354f8ffb321a8ba9cb52d19beb796e561c5", size = 83104, upload-time = "2026-07-28T06:05:52.405Z" }, + { url = "https://files.pythonhosted.org/packages/55/b3/af176d79a8515a8a720eccdad9a96f6e31a30abf2865430c8c42adf2fd13/wrapt-2.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b1e5aa486e269b00ed35e64771c7d0ab8096cfd2643405ca8cd60ebedc099a51", size = 81774, upload-time = "2026-07-28T06:05:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/00/39/3daf9f47be208606586de4568ba6713db53ebc8fd7a575aea1fe57983b69/wrapt-2.3.0-py3-none-any.whl", hash = "sha256:d8c7ed08477429752b8c44991f40ad7838b18332a160698740a6bfbc10d998a2", size = 61866, upload-time = "2026-07-28T06:06:12.9Z" }, +] + +[[package]] +name = "xlsxwriter" +version = "3.2.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/2c/c06ef49dc36e7954e55b802a8b231770d286a9758b3d936bd1e04ce5ba88/xlsxwriter-3.2.9.tar.gz", hash = "sha256:254b1c37a368c444eac6e2f867405cc9e461b0ed97a3233b2ac1e574efb4140c", size = 215940, upload-time = "2025-09-16T00:16:21.63Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/0c/3662f4a66880196a590b202f0db82d919dd2f89e99a27fadef91c4a33d41/xlsxwriter-3.2.9-py3-none-any.whl", hash = "sha256:9a5db42bc5dff014806c58a20b9eae7322a134abb6fce3c92c181bfb275ec5b3", size = 175315, upload-time = "2025-09-16T00:16:20.108Z" }, +]