Research-Stack/4-Infrastructure/shim/math_symbols.py
allaun 77488ac0ae feat(lean): close gaussian_line_integral_unit_dir + consolidate infrastructure
Lean proof fixes:
- N3L_Energy.lean: fully close gaussian_line_integral_unit_dir (nlinarith+hab
  for unit-circle quadratic, sqrt_mul+neg_div for integral_gaussian_1d match,
  exp_sum_of_sq order fix, add_assoc for h_gauss_shift, sq_sqrt for field_simp,
  sq_abs for perpDistance hd)
- Add Adapters/AlphaProofNexus: 12 Erdos/graph adapter stubs (AlphaProof nexus)
- Add Adapters/ErgodicAdditive.lean, SidonMatroid.lean
- Add AntiDiophantine.lean, EffectiveBoundDQ.lean, PVGS_DQ_Bridge.lean
- Add FormalConjectures/Util/ProblemImports.lean
- Add RRC/EntropyCandidates/Candidates.lean
- Add OTOM external project (lakefile.toml, lake-manifest.json, lean-toolchain)

Infrastructure:
- Add 4-Infrastructure/shim/: 17 Python probes (RRC manifold, Sidon kernel,
  Wannier, arxiv harvest, math_symbols DB, coverage density, geometric entropy)
- Add 4-Infrastructure/NoDupeLabs/: Node server + package files
- Add 6-Documentation/docs/specs/DP_RRC_RECEIPT_ENCODING_SPEC.md
- Add fix_offloat.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 16:53:23 -05:00

133 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
math_symbols.py — Math-symbol database loader + LaTeX/Unicode normalizer.
Backs the RRC geometry / tensor-notation kernel. Provides:
* MATH_SYMBOLS — full symbol table from shared-data/data/math_symbols_v1.json
(built by build_math_symbols_db.py: unicode-math-table.tex +
unicodedata, ~2950 symbols).
* LATEX_TO_CHAR — command → Unicode char. A curated map of the STANDARD LaTeX
macros (\\Gamma, \\rho, \\nabla, …) takes precedence over the
unicode-math table (which binds \\Gamma to a math-italic
codepoint, not the plain Greek letter), then the full DB
fills in the long tail (\\boxtimes, \\curlyvee, …).
* CHAR_INFO — char → {role, name, block, category} for feature extraction.
* normalize_math(text) — canonicalize LaTeX/Unicode so the notation signatures
match regardless of encoding: \\Gamma ≡ Γ, \\rho\\sigma ≡ ρσ,
and Penrose abstract-index R^{a}{}_{bcd} collapses cleanly.
Degrades gracefully: if the JSON DB is absent, the curated map alone still drives
normalization of the common macros.
"""
from __future__ import annotations
import json
import re
from pathlib import Path
_DB_PATHS = [
Path("/home/allaun/Research Stack/shared-data/data/math_symbols_v1.json"),
Path(__file__).resolve().parent.parent.parent / "shared-data/data/math_symbols_v1.json",
Path("shared-data/data/math_symbols_v1.json"),
]
# ── Curated standard-LaTeX macros (authoritative for the common commands) ─────
_GREEK = {
"alpha": "α", "beta": "β", "gamma": "γ", "delta": "δ", "epsilon": "ε",
"varepsilon": "ε", "zeta": "ζ", "eta": "η", "theta": "θ", "vartheta": "ϑ",
"iota": "ι", "kappa": "κ", "lambda": "λ", "mu": "μ", "nu": "ν", "xi": "ξ",
"omicron": "ο", "pi": "π", "varpi": "ϖ", "rho": "ρ", "varrho": "ϱ",
"sigma": "σ", "varsigma": "ς", "tau": "τ", "upsilon": "υ", "phi": "φ",
"varphi": "φ", "chi": "χ", "psi": "ψ", "omega": "ω",
"Gamma": "Γ", "Delta": "Δ", "Theta": "Θ", "Lambda": "Λ", "Xi": "Ξ",
"Pi": "Π", "Sigma": "Σ", "Upsilon": "Υ", "Phi": "Φ", "Psi": "Ψ", "Omega": "Ω",
}
_OPS = {
"nabla": "", "partial": "", "infty": "", "times": "×", "cdot": "",
"otimes": "", "oplus": "", "odot": "", "wedge": "", "vee": "",
"pm": "±", "mp": "", "ast": "", "star": "", "circ": "", "bullet": "",
"to": "", "rightarrow": "", "longrightarrow": "", "mapsto": "",
"leftarrow": "", "Rightarrow": "", "Leftarrow": "", "leftrightarrow": "",
"leq": "", "le": "", "geq": "", "ge": "", "neq": "", "ne": "",
"approx": "", "equiv": "", "cong": "", "sim": "", "simeq": "",
"propto": "", "in": "", "notin": "", "ni": "", "subset": "",
"subseteq": "", "supset": "", "supseteq": "", "cup": "", "cap": "",
"setminus": "", "emptyset": "", "forall": "", "exists": "",
"sum": "", "prod": "", "coprod": "", "int": "", "oint": "", "iint": "",
"Box": "", "square": "", "Diamond": "", "dagger": "", "ddagger": "",
"ell": "", "hbar": "", "Re": "", "Im": "", "aleph": "", "wp": "",
"angle": "", "perp": "", "parallel": "", "nparallel": "", "top": "",
"bot": "", "models": "", "vdash": "", "boxtimes": "", "boxplus": "",
"rtimes": "", "ltimes": "", "bigwedge": "", "bigvee": "",
"bigcup": "", "bigcap": "", "bigotimes": "", "bigoplus": "", "bigodot": "",
"langle": "", "rangle": "", "lVert": "", "rVert": "", "Vert": "",
"nabla": "", "triangle": "", "sharp": "", "flat": "", "lor": "", "land": "",
}
# Formatting / spacing macros that carry no symbol meaning — stripped.
_DROP_WORD = {
"mathrm", "mathbf", "mathit", "mathsf", "mathtt", "mathcal", "mathbb",
"mathfrak", "mathscr", "boldsymbol", "bm", "operatorname", "text", "textrm",
"textbf", "textit", "mathnormal", "left", "right", "big", "Big", "bigg",
"Bigg", "bigl", "bigr", "Bigl", "Bigr", "displaystyle", "textstyle",
"scriptstyle", "limits", "nolimits", "quad", "qquad",
}
_CURATED: dict[str, str] = {**_GREEK, **_OPS}
def _load_db() -> dict:
for p in _DB_PATHS:
try:
if p.exists():
return json.loads(p.read_text(encoding="utf-8"))
except Exception:
continue
return {"symbols": []}
MATH_SYMBOLS: list[dict] = _load_db().get("symbols", [])
# command (no backslash) → char. Curated wins; DB fills the long tail.
LATEX_TO_CHAR: dict[str, str] = {}
for _s in MATH_SYMBOLS:
_cmd = (_s.get("latex") or "").lstrip("\\").strip()
if _cmd and _cmd.isalpha() and _cmd not in LATEX_TO_CHAR:
LATEX_TO_CHAR[_cmd] = _s["char"]
LATEX_TO_CHAR.update(_CURATED) # curated standard macros take precedence
CHAR_INFO: dict[str, dict] = {s["char"]: s for s in MATH_SYMBOLS}
_CMD_RE = re.compile(r"\\([A-Za-z]+)")
_SPACE_RE = re.compile(r"\\[,!;:> ]") # \, \! \; \: thin/neg spaces
_EMPTY_GRP_RE = re.compile(r"\{\s*\}") # Penrose empty index slots {}
_WS_RE = re.compile(r"[ \t]+")
def _sub_cmd(m: re.Match) -> str:
word = m.group(1)
if word in _DROP_WORD:
return " "
if word in LATEX_TO_CHAR:
return LATEX_TO_CHAR[word]
return m.group(0) # unknown command: leave untouched
def normalize_math(text: str) -> str:
"""Canonicalize LaTeX + Unicode math so notation signatures match uniformly.
\\Gamma → Γ, \\rho\\sigma\\mu\\nu → ρσμν, \\nabla_\\mu → ∇_μ, strips
\\mathrm/\\left/\\, wrappers, and collapses Penrose empty index groups
R^{a}{}_{bcd} → R^{a}_{bcd}. Idempotent on already-Unicode input.
"""
if not text:
return ""
text = _SPACE_RE.sub(" ", text)
# iterate to resolve nested wrappers like \mathrm{\Gamma}
for _ in range(3):
new = _CMD_RE.sub(_sub_cmd, text)
if new == text:
break
text = new
text = _EMPTY_GRP_RE.sub("", text)
return _WS_RE.sub(" ", text).strip()