#!/usr/bin/env python3 # -*- coding: utf-8 -*- """mass_relations.py — the PARAMETER-FREE RELATION engine. Part of the "Observed Particle Spectrum Closure" companion. This engine loads PDG-2024 masses + geometry-derived quark content from ./data and evaluates the parameter-free QCD symmetry RELATIONS — the genuine geometry+QCD tests. These relations need NO geometry mass scale: only measured masses and the quark assignments enter, so they are RELATION-grade (parameter-free), not a fit. It evaluates four RELATION families: (R1) Gell-Mann-Okubo (GMO) baryon octet: 2(m_N + m_Xi) = 3*m_Lambda + m_Sigma (R2) Decuplet equal-spacing: m_Sigma*-m_Delta == m_Xi*-m_Sigma* == m_Omega - m_Xi* (R3) Isospin-splitting SIGNS: m_n>m_p, m_K0>m_K+, m_D0m_u + EM (R4) Regge linearity: least-squares M^2 vs J (and vs n); report slope + R^2 (expect ~linear) For each relation we report the predicted-vs-PDG residual, a declared tolerance (from ./relations_config.json), and PASS/FAIL. We FAIL-CLOSED: any quantum-number inconsistency, any missing required field, or any RELATION residual exceeding its declared tolerance => that test FAILs and the process exits non-zero. Before the relations run, a quantum-number GATE checks that every baryon / multiplet member is quantum-number-consistent with its listed constituents (charge = sum of constituent charges; strangeness = -(# strange quarks); baryon number = (#quarks)/3). A deliberately-broken row (charge != sum of constituents) MUST trip this gate and fail closed — that is exercised by the test suite. SAFE WORDING (also written into every report): This suite verifies that every observed particle is quantum-number- consistent with its geometry-derived constituents and that the parameter-free QCD symmetry relations hold against PDG-2024; it does NOT compute or claim absolute hadron masses from geometry. Design constraints (PM-AGENT governance, RUNNABLE-FIRST): * Pure Python 3 standard library only: csv, json, math, statistics, argparse, dataclasses, pathlib, sys, typing. Zero pip installs. * CITE EVERY CONSTANT: every PDG mass carries a 'pdg_source' column; a numeric mass without a pdg_source FAILs the required-field gate. No magic numbers; all theory/relation tolerances are declared in relations_config.json. CLI: python mass_relations.py --data data --out out --config relations_config.json Exit codes: 0 every quantum-number gate passed AND every RELATION residual is within its declared tolerance. 1 a quantum-number inconsistency, a missing required field, an unparseable input, or a RELATION residual beyond tolerance (FAIL-CLOSED). 2 CLI / configuration usage error. """ from __future__ import annotations import argparse import csv import json import math import statistics import sys from dataclasses import dataclass, field, asdict from pathlib import Path from typing import Any, Dict, List, Optional, Sequence, Tuple # --------------------------------------------------------------------------- # # Constants # # --------------------------------------------------------------------------- # SAFE_WORDING = ( "This suite verifies that every observed particle is quantum-number-" "consistent with its geometry-derived constituents and that the " "parameter-free QCD symmetry relations hold against PDG-2024; it does NOT " "compute or claim absolute hadron masses from geometry." ) GRADE = "RELATION" # parameter-free; needs only measured masses + quark content. # The four required input CSVs. INPUT_CSVS: Tuple[str, ...] = ( "baryon_octet.csv", "baryon_decuplet.csv", "isospin_multiplets.csv", "regge_trajectories.csv", ) # Electric charge of each quark flavour (in units of e). Down-type = -1/3, # up-type = +2/3. These are the Standard-Model quark charges, NOT free numbers. QUARK_CHARGE: Dict[str, float] = { "u": 2.0 / 3.0, "d": -1.0 / 3.0, "s": -1.0 / 3.0, "c": 2.0 / 3.0, "b": -1.0 / 3.0, "t": 2.0 / 3.0, } # Strangeness contributed by each quark (only the s quark carries S; s has S=-1). QUARK_STRANGENESS: Dict[str, int] = { "u": 0, "d": 0, "s": -1, "c": 0, "b": 0, "t": 0, } OUTPUT_FILES: Tuple[str, ...] = ( "relations_report.md", "relations_report.json", ) # --------------------------------------------------------------------------- # # Configuration # # --------------------------------------------------------------------------- # @dataclass class Tolerance: """A single declared tolerance from relations_config.json.""" name: str value: Any kind: str rationale: str @dataclass class Config: pdg_version: str data_freeze_date: str grade: str safe_wording: str fail_closed: bool tolerances: Dict[str, Tolerance] def tol(self, name: str) -> Tolerance: if name not in self.tolerances: raise SystemExit( f"[FAIL-CLOSED] required tolerance '{name}' missing from config; " f"every relation must declare its tolerance." ) return self.tolerances[name] def load_config(config_path: Path) -> Config: if not config_path.exists(): raise SystemExit( f"[FAIL-CLOSED] config not found: {config_path}. A relations config " f"declaring every tolerance is required." ) try: raw = json.loads(config_path.read_text(encoding="utf-8")) except json.JSONDecodeError as exc: raise SystemExit(f"[FAIL-CLOSED] config unparseable: {config_path}: {exc}") if not isinstance(raw, dict): raise SystemExit(f"[FAIL-CLOSED] config root must be an object: {config_path}") tol_raw = raw.get("tolerances") if not isinstance(tol_raw, dict) or not tol_raw: raise SystemExit( f"[FAIL-CLOSED] config has no 'tolerances' block: {config_path}" ) tolerances: Dict[str, Tolerance] = {} for name, body in tol_raw.items(): if not isinstance(body, dict) or "value" not in body or "kind" not in body: raise SystemExit( f"[FAIL-CLOSED] tolerance '{name}' must declare 'value' and 'kind'." ) tolerances[name] = Tolerance( name=name, value=body["value"], kind=str(body["kind"]), rationale=str(body.get("rationale", "")), ) return Config( pdg_version=str(raw.get("pdg_version", "PDG 2024")), data_freeze_date=str(raw.get("data_freeze_date", "")), grade=str(raw.get("grade", GRADE)), safe_wording=str(raw.get("safe_wording", SAFE_WORDING)), fail_closed=bool(raw.get("fail_closed", True)), tolerances=tolerances, ) # --------------------------------------------------------------------------- # # CSV loading + parsing helpers # # --------------------------------------------------------------------------- # def load_csv(path: Path) -> List[Dict[str, str]]: """Load a CSV into dict rows with normalised (lower) headers. Fails closed.""" if not path.exists(): raise SystemExit(f"[FAIL-CLOSED] required input file missing: {path}") with path.open("r", encoding="utf-8-sig", newline="") as fh: reader = csv.DictReader(fh) if reader.fieldnames is None: raise SystemExit(f"[FAIL-CLOSED] input file has no header: {path}") rows: List[Dict[str, str]] = [] for raw in reader: row = { (k or "").strip().lower(): (v.strip() if isinstance(v, str) else v) for k, v in raw.items() if k is not None } rows.append(row) return rows def parse_float(value: Any) -> Optional[float]: if value is None: return None text = str(value).strip() if text == "": return None try: return float(text) except ValueError: return None def require_field(row: Dict[str, str], field_name: str, ctx: str) -> str: val = row.get(field_name, "") if val is None or str(val).strip() == "": raise SystemExit( f"[FAIL-CLOSED] {ctx}: missing required field '{field_name}'." ) return str(val).strip() def require_mass(row: Dict[str, str], field_name: str, ctx: str) -> float: """Require a numeric mass AND a 'pdg_source' citation (CITE EVERY CONSTANT).""" raw = require_field(row, field_name, ctx) m = parse_float(raw) if m is None: raise SystemExit(f"[FAIL-CLOSED] {ctx}: field '{field_name}'='{raw}' is not numeric.") if not str(row.get("pdg_source", "")).strip(): raise SystemExit( f"[FAIL-CLOSED] {ctx}: numeric mass '{field_name}'={m} lacks a 'pdg_source' " f"citation (CITE EVERY CONSTANT)." ) return m # --------------------------------------------------------------------------- # # Quark-content parsing + the quantum-number GATE # # --------------------------------------------------------------------------- # def expand_quark_content(token: str) -> List[str]: """Expand a quark-content token into a flat list of single-quark flavours. Accepts either a single assignment like 'uds' or an underscore-separated set of isospin-partner assignments (decuplet charge-averaged members) like 'uus_uds_dds'. For a multi-assignment token, all assignments must contain the SAME number of quarks and the SAME multiset of strange/charm/bottom content (only the up<->down balance may differ across isospin partners); we return the first assignment's flavour list as the representative, after verifying that property. Returns [] for an empty/invalid token. """ token = (token or "").strip().lower() if token == "": return [] assignments = [a for a in token.split("_") if a] parsed = [list(a) for a in assignments] # All assignments must be the same length (same baryon/meson type). lengths = {len(p) for p in parsed} if len(lengths) != 1: raise SystemExit( f"[FAIL-CLOSED] quark_content '{token}': isospin partners differ in " f"quark count {sorted(lengths)} — inconsistent assignment." ) # Heavy/strange content must agree across partners (only u<->d may differ). def heavy_multiset(p: List[str]) -> Tuple[Tuple[str, int], ...]: counts: Dict[str, int] = {} for q in p: if q in ("s", "c", "b", "t"): counts[q] = counts.get(q, 0) + 1 return tuple(sorted(counts.items())) heavy = {heavy_multiset(p) for p in parsed} if len(heavy) != 1: raise SystemExit( f"[FAIL-CLOSED] quark_content '{token}': isospin partners disagree on " f"strange/heavy content {heavy} — inconsistent assignment." ) return parsed[0] def derived_charge(quarks: Sequence[str]) -> float: total = 0.0 for q in quarks: if q not in QUARK_CHARGE: raise SystemExit(f"[FAIL-CLOSED] unknown quark flavour '{q}'.") total += QUARK_CHARGE[q] return total def derived_strangeness(quarks: Sequence[str]) -> int: return sum(QUARK_STRANGENESS[q] for q in quarks) def derived_baryon_number(quarks: Sequence[str]) -> float: return len(quarks) / 3.0 @dataclass class QNFinding: particle: str quark_content: str field: str derived: Any listed: Any ok: bool detail: str def quantum_number_gate( octet: List[Dict[str, str]], decuplet: List[Dict[str, str]], multiplets: List[Dict[str, str]], ) -> Tuple[List[QNFinding], bool]: """Check every baryon/member is quantum-number-consistent with constituents. For each baryon row we verify: * charge == sum of constituent quark charges, * strangeness == sum of constituent quark strangeness, * baryon number == (#quarks)/3 (a baryon must be 3 quarks => B=1). For isospin-multiplet rows we verify both members' charges against their listed quark content (no explicit charge column => derive only, and check the heavier/lighter quark contents are themselves valid flavours and the declared multiplet differs by exactly an up<->down swap). Returns (findings, ok). ok is False if ANY check failed (=> fail-closed). """ findings: List[QNFinding] = [] ok = True tol = 1e-9 for row in octet + decuplet: name = str(row.get("particle", "?")) qc = str(row.get("quark_content", "")) quarks = expand_quark_content(qc) if not quarks: findings.append(QNFinding(name, qc, "quark_content", "", "", False, "empty/invalid quark_content")) ok = False continue # Charge. listed_q = parse_float(row.get("q")) der_q = derived_charge(quarks) if listed_q is None: findings.append(QNFinding(name, qc, "charge", round(der_q, 6), "MISSING", False, "no listed charge Q to compare")) ok = False else: good = abs(der_q - listed_q) < tol findings.append(QNFinding(name, qc, "charge", round(der_q, 6), round(listed_q, 6), good, "Q == sum(quark charges)")) ok = ok and good # Strangeness. listed_s = parse_float(row.get("strangeness")) der_s = derived_strangeness(quarks) if listed_s is None: findings.append(QNFinding(name, qc, "strangeness", der_s, "MISSING", False, "no listed strangeness to compare")) ok = False else: good = abs(der_s - listed_s) < tol findings.append(QNFinding(name, qc, "strangeness", der_s, int(listed_s), good, "S == sum(quark strangeness)")) ok = ok and good # Baryon number: a baryon must be exactly 3 quarks => B == 1. der_b = derived_baryon_number(quarks) good_b = abs(der_b - 1.0) < tol findings.append(QNFinding(name, qc, "baryon_number", round(der_b, 6), 1.0, good_b, "B == (#quarks)/3 == 1 for a baryon")) ok = ok and good_b # Isospin-multiplet member charges (derive from their quark content). for row in multiplets: mult = str(row.get("multiplet", "?")) for tag in ("heavier", "lighter"): member = str(row.get(f"member_{tag}", "?")) qc = str(row.get(f"quark_{tag}", "")) quarks = expand_quark_content(qc) if not quarks: findings.append(QNFinding(f"{mult}:{member}", qc, "quark_content", "", "", False, "empty/invalid quark_content")) ok = False continue der_q = derived_charge(quarks) findings.append(QNFinding(f"{mult}:{member}", qc, "charge_derivable", round(der_q, 6), "(no column)", True, "derived charge is well-formed from quark flavours")) return findings, ok # --------------------------------------------------------------------------- # # Relation results model # # --------------------------------------------------------------------------- # @dataclass class RelationResult: relation_id: str name: str grade: str formula: str inputs: Dict[str, Any] predicted: Any observed: Any residual: Any residual_kind: str # "absolute" | "relative" | "max_relative" | "min_r_squared" tolerance_name: str tolerance_value: Any tolerance_rationale: str passed: bool detail: str def to_dict(self) -> Dict[str, Any]: return asdict(self) # --------------------------------------------------------------------------- # # (R1) Gell-Mann-Okubo baryon octet # # --------------------------------------------------------------------------- # def _octet_index(octet: List[Dict[str, str]]) -> Dict[str, Dict[str, str]]: by_name = {str(r.get("particle", "")).strip(): r for r in octet} return by_name def _isospin_average(octet_by: Dict[str, Dict[str, str]], names: Sequence[str], ctx: str) -> Tuple[float, float]: """Charge-averaged mass + propagated sigma over an isospin multiplet.""" masses: List[float] = [] sig2 = 0.0 for n in names: if n not in octet_by: raise SystemExit(f"[FAIL-CLOSED] {ctx}: octet member '{n}' missing from baryon_octet.csv") masses.append(require_mass(octet_by[n], "mass_mev", f"octet {n}")) s = parse_float(octet_by[n].get("mass_sigma_mev")) or 0.0 sig2 += s * s avg = sum(masses) / len(masses) sigma = math.sqrt(sig2) / len(masses) return avg, sigma def relation_gmo_octet(octet: List[Dict[str, str]], cfg: Config) -> RelationResult: by = _octet_index(octet) # Isospin-averaged multiplet masses. m_N, sN = _isospin_average(by, ["proton", "neutron"], "GMO m_N") m_Xi, sXi = _isospin_average(by, ["Xi0", "Xi-"], "GMO m_Xi") m_Lambda = require_mass(by["Lambda"], "mass_mev", "GMO Lambda") if "Lambda" in by else None if m_Lambda is None: raise SystemExit("[FAIL-CLOSED] GMO: Lambda missing from baryon_octet.csv") m_Sigma, sSig = _isospin_average(by, ["Sigma+", "Sigma0", "Sigma-"], "GMO m_Sigma") lhs = 2.0 * (m_N + m_Xi) # 2(m_N + m_Xi) rhs = 3.0 * m_Lambda + m_Sigma # 3 m_Lambda + m_Sigma residual_abs = lhs - rhs scale = 0.5 * (abs(lhs) + abs(rhs)) residual_rel = abs(residual_abs) / scale if scale > 0 else float("inf") tol = cfg.tol("gmo_octet_rel_tol") passed = residual_rel <= float(tol.value) return RelationResult( relation_id="R1", name="Gell-Mann-Okubo baryon octet mass formula", grade=cfg.grade, formula="2(m_N + m_Xi) = 3*m_Lambda + m_Sigma", inputs={ "m_N_MeV": round(m_N, 6), "m_Xi_MeV": round(m_Xi, 6), "m_Lambda_MeV": round(m_Lambda, 6), "m_Sigma_MeV": round(m_Sigma, 6), }, predicted=round(rhs, 6), observed=round(lhs, 6), residual=round(residual_rel, 8), residual_kind="relative", tolerance_name=tol.name, tolerance_value=tol.value, tolerance_rationale=tol.rationale, passed=passed, detail=(f"LHS=2(m_N+m_Xi)={lhs:.4f} MeV; RHS=3 m_Lambda+m_Sigma={rhs:.4f} MeV; " f"|LHS-RHS|={abs(residual_abs):.4f} MeV; rel={residual_rel*100:.4f}% " f"(tol {float(tol.value)*100:.2f}%)"), ) # --------------------------------------------------------------------------- # # (R2) Decuplet equal-spacing # # --------------------------------------------------------------------------- # def relation_decuplet_equal_spacing(decuplet: List[Dict[str, str]], cfg: Config) -> RelationResult: by = {str(r.get("particle", "")).strip(): r for r in decuplet} needed = ["Delta", "Sigma*", "Xi*", "Omega-"] for n in needed: if n not in by: raise SystemExit(f"[FAIL-CLOSED] decuplet: member '{n}' missing from baryon_decuplet.csv") m_Delta = require_mass(by["Delta"], "mass_mev", "decuplet Delta") m_Sigma_star = require_mass(by["Sigma*"], "mass_mev", "decuplet Sigma*") m_Xi_star = require_mass(by["Xi*"], "mass_mev", "decuplet Xi*") m_Omega = require_mass(by["Omega-"], "mass_mev", "decuplet Omega-") step1 = m_Sigma_star - m_Delta # Sigma* - Delta step2 = m_Xi_star - m_Sigma_star # Xi* - Sigma* step3 = m_Omega - m_Xi_star # Omega - Xi* steps = [step1, step2, step3] mean_step = sum(steps) / 3.0 max_dev = max(abs(s - mean_step) for s in steps) residual_rel = max_dev / abs(mean_step) if mean_step != 0 else float("inf") tol = cfg.tol("decuplet_equal_spacing_rel_tol") passed = residual_rel <= float(tol.value) return RelationResult( relation_id="R2", name="Baryon decuplet equal-spacing rule", grade=cfg.grade, formula="m_Sigma* - m_Delta == m_Xi* - m_Sigma* == m_Omega - m_Xi*", inputs={ "m_Delta_MeV": round(m_Delta, 6), "m_Sigma*_MeV": round(m_Sigma_star, 6), "m_Xi*_MeV": round(m_Xi_star, 6), "m_Omega-_MeV": round(m_Omega, 6), "step_Sigma*-Delta_MeV": round(step1, 4), "step_Xi*-Sigma*_MeV": round(step2, 4), "step_Omega-Xi*_MeV": round(step3, 4), "mean_step_MeV": round(mean_step, 4), }, predicted=round(mean_step, 4), observed=[round(step1, 4), round(step2, 4), round(step3, 4)], residual=round(residual_rel, 8), residual_kind="max_relative", tolerance_name=tol.name, tolerance_value=tol.value, tolerance_rationale=tol.rationale, passed=passed, detail=(f"steps = [{step1:.2f}, {step2:.2f}, {step3:.2f}] MeV; mean={mean_step:.2f} MeV; " f"max deviation from mean = {max_dev:.2f} MeV ({residual_rel*100:.2f}%, " f"tol {float(tol.value)*100:.0f}%)"), ) # --------------------------------------------------------------------------- # # (R3) Isospin-splitting signs # # --------------------------------------------------------------------------- # def relation_isospin_signs(multiplets: List[Dict[str, str]], cfg: Config) -> RelationResult: """Verify each multiplet's observed mass-ordering sign matches the expected sign (m_d>m_u + EM). The sign is the parameter-free RELATION; the magnitude is LATTICE-IMPORTED and is NOT tested here. expected_sign='positive' => mass_heavier > mass_lighter (heavier member truly heavier in PDG). expected_sign='negative' => mass_heavier < mass_lighter (i.e. the row's 'heavier' label is the QCD-naive heavier one, but the OBSERVED ordering is inverted, as for D0/D+ where charm dominates). """ rows_out: List[Dict[str, Any]] = [] all_ok = True for row in multiplets: mult = str(row.get("multiplet", "?")) mh = require_mass(row, "mass_heavier_mev", f"isospin {mult} heavier") ml = require_mass(row, "mass_lighter_mev", f"isospin {mult} lighter") expected = str(row.get("expected_sign", "")).strip().lower() if expected not in ("positive", "negative"): raise SystemExit( f"[FAIL-CLOSED] isospin {mult}: expected_sign must be " f"'positive' or 'negative', got '{expected}'." ) observed_diff = mh - ml # heavier-labelled minus lighter-labelled observed_sign = "positive" if observed_diff > 0 else ("negative" if observed_diff < 0 else "zero") ok = observed_sign == expected all_ok = all_ok and ok rows_out.append({ "multiplet": mult, "member_heavier": str(row.get("member_heavier", "")), "member_lighter": str(row.get("member_lighter", "")), "mass_heavier_MeV": round(mh, 6), "mass_lighter_MeV": round(ml, 6), "observed_diff_MeV": round(observed_diff, 6), "observed_sign": observed_sign, "expected_sign": expected, "mechanism": str(row.get("mechanism", "")), "pdg_source": str(row.get("pdg_source", "")), "ok": ok, }) n_ok = sum(1 for r in rows_out if r["ok"]) return RelationResult( relation_id="R3", name="Isospin-splitting signs consistent with m_d>m_u + EM", grade=cfg.grade, formula="sign(m_member_heavier - m_member_lighter) == expected_sign (per multiplet)", inputs={"multiplets": rows_out}, predicted=[r["expected_sign"] for r in rows_out], observed=[r["observed_sign"] for r in rows_out], residual=f"{len(rows_out) - n_ok} sign mismatch(es)", residual_kind="sign_match_count", tolerance_name="(exact sign match required)", tolerance_value=0, tolerance_rationale=( "The SIGN of each isospin splitting is the parameter-free RELATION; " "0 mismatches are tolerated. Magnitudes are LATTICE-IMPORTED and not " "tested here." ), passed=all_ok, detail="; ".join( f"{r['multiplet']}: {r['member_heavier']}({r['mass_heavier_MeV']}) - " f"{r['member_lighter']}({r['mass_lighter_MeV']}) = {r['observed_diff_MeV']:.4f} MeV " f"=> {r['observed_sign']} (expected {r['expected_sign']}) " f"[{'OK' if r['ok'] else 'MISMATCH'}]" for r in rows_out ), ) # --------------------------------------------------------------------------- # # (R4) Regge linearity # # --------------------------------------------------------------------------- # def _least_squares(xs: Sequence[float], ys: Sequence[float]) -> Tuple[float, float, float]: """Ordinary least-squares fit y = slope*x + intercept; returns (slope, intercept, r_squared). Uses stdlib statistics only. """ n = len(xs) if n < 2: raise SystemExit("[FAIL-CLOSED] Regge fit needs >= 2 points.") mean_x = statistics.fmean(xs) mean_y = statistics.fmean(ys) sxx = sum((x - mean_x) ** 2 for x in xs) sxy = sum((x - mean_x) * (y - mean_y) for x, y in zip(xs, ys)) if sxx == 0: raise SystemExit("[FAIL-CLOSED] Regge fit: zero variance in the J/n axis.") slope = sxy / sxx intercept = mean_y - slope * mean_x ss_tot = sum((y - mean_y) ** 2 for y in ys) ss_res = sum((y - (slope * x + intercept)) ** 2 for x, y in zip(xs, ys)) r_squared = 1.0 - (ss_res / ss_tot) if ss_tot > 0 else 1.0 return slope, intercept, r_squared def relation_regge_linearity(regge: List[Dict[str, str]], cfg: Config) -> List[RelationResult]: """For each trajectory, fit M^2 against its axis (J or n) by least squares and report slope + R^2. PASS requires R^2 >= floor AND a positive slope. """ # Group rows by trajectory. by_traj: Dict[str, List[Dict[str, str]]] = {} for row in regge: t = str(row.get("trajectory", "")).strip() if not t: raise SystemExit("[FAIL-CLOSED] regge row missing 'trajectory'.") by_traj.setdefault(t, []).append(row) r2_tol = cfg.tol("regge_r2_floor") slope_tol = cfg.tol("regge_slope_positive") floor = float(r2_tol.value) results: List[RelationResult] = [] for traj, rows in sorted(by_traj.items()): axis = str(rows[0].get("axis", "")).strip().lower() if axis not in ("j", "n"): raise SystemExit(f"[FAIL-CLOSED] regge trajectory '{traj}': axis must be 'J' or 'n'.") xs: List[float] = [] ys: List[float] = [] points: List[Dict[str, Any]] = [] for row in rows: x = parse_float(row.get(axis)) m = require_mass(row, "mass_mev", f"regge {traj} {row.get('state','?')}") if x is None: raise SystemExit( f"[FAIL-CLOSED] regge {traj} state '{row.get('state','?')}': " f"missing axis value '{axis}'." ) xs.append(x) ys.append(m * m) # M^2 in MeV^2 points.append({"state": str(row.get("state", "")), axis: x, "mass_MeV": round(m, 4), "M2_MeV2": round(m * m, 2), "pdg_source": str(row.get("pdg_source", ""))}) slope, intercept, r2 = _least_squares(xs, ys) slope_ok = (slope > 0) if bool(slope_tol.value) else True r2_ok = r2 >= floor passed = slope_ok and r2_ok # Express slope in GeV^-2 inverse for the orbital case (alpha' ~ 1/slope). alpha_prime = (1.0 / slope) * 1e6 if (axis == "j" and slope > 0) else None # MeV^2->GeV^2: /1e6 on slope => *1e6 on 1/slope results.append(RelationResult( relation_id=f"R4:{traj}", name=f"Regge linearity ({traj}, M^2 vs {axis.upper()})", grade=cfg.grade, formula=f"M^2 = slope*{axis.upper()} + intercept (least squares)", inputs={"axis": axis.upper(), "points": points, "n_points": len(points)}, predicted={"slope_MeV2_per_unit": round(slope, 3), "intercept_MeV2": round(intercept, 3), "alpha_prime_GeV-2": round(alpha_prime, 4) if alpha_prime else None}, observed={"R_squared": round(r2, 6)}, residual=round(r2, 6), residual_kind="min_r_squared", tolerance_name=r2_tol.name, tolerance_value=r2_tol.value, tolerance_rationale=r2_tol.rationale, passed=passed, detail=(f"{len(points)}-point fit M^2 vs {axis.upper()}: slope={slope:.1f} MeV^2/unit " f"({'positive' if slope > 0 else 'NON-POSITIVE'}), R^2={r2:.5f} " f"(floor {floor:.2f}) => {'PASS' if passed else 'FAIL'}" + ("" if slope_ok else "; SLOPE NOT POSITIVE")), )) return results # --------------------------------------------------------------------------- # # Orchestration # # --------------------------------------------------------------------------- # @dataclass class SuiteResult: qn_findings: List[QNFinding] qn_ok: bool relations: List[RelationResult] config: Config @property def relation_fails(self) -> List[RelationResult]: return [r for r in self.relations if not r.passed] @property def overall_pass(self) -> bool: return self.qn_ok and not self.relation_fails def run_suite(data_dir: Path, cfg: Config) -> SuiteResult: octet = load_csv(data_dir / "baryon_octet.csv") decuplet = load_csv(data_dir / "baryon_decuplet.csv") multiplets = load_csv(data_dir / "isospin_multiplets.csv") regge = load_csv(data_dir / "regge_trajectories.csv") # GATE: quantum-number consistency. Fail-closed if any inconsistency. qn_findings, qn_ok = quantum_number_gate(octet, decuplet, multiplets) relations: List[RelationResult] = [] # Only evaluate relations if the quantum-number gate passes; otherwise the # constituents are not trustworthy and the relations would be meaningless. if qn_ok: relations.append(relation_gmo_octet(octet, cfg)) relations.append(relation_decuplet_equal_spacing(decuplet, cfg)) relations.append(relation_isospin_signs(multiplets, cfg)) relations.extend(relation_regge_linearity(regge, cfg)) return SuiteResult(qn_findings=qn_findings, qn_ok=qn_ok, relations=relations, config=cfg) # --------------------------------------------------------------------------- # # Output writers # # --------------------------------------------------------------------------- # def build_report(result: SuiteResult) -> Dict[str, Any]: cfg = result.config return { "grade": cfg.grade, "pdg_version": cfg.pdg_version, "data_freeze_date": cfg.data_freeze_date, "safe_wording": cfg.safe_wording, "quantum_number_gate": { "passed": result.qn_ok, "findings": [asdict(f) for f in result.qn_findings], "n_checks": len(result.qn_findings), "n_failed": sum(1 for f in result.qn_findings if not f.ok), }, "relations": [r.to_dict() for r in result.relations], "totals": { "relations_evaluated": len(result.relations), "relations_passed": sum(1 for r in result.relations if r.passed), "relations_failed": len(result.relation_fails), }, "overall_pass": result.overall_pass, } def render_markdown(report: Dict[str, Any]) -> str: L: List[str] = [] L.append("# Parameter-Free RELATION Report") L.append("") L.append(f"- Grade: **{report['grade']}** (parameter-free; measured masses + quark content only)") L.append(f"- PDG version: `{report['pdg_version']}`") L.append(f"- Data freeze date: `{report['data_freeze_date']}`") verdict = "PASS" if report["overall_pass"] else "FAIL (fail-closed)" L.append(f"- Overall: **{verdict}**") L.append(f"- Relations: {report['totals']['relations_passed']}/" f"{report['totals']['relations_evaluated']} passed " f"({report['totals']['relations_failed']} failed)") L.append("") qn = report["quantum_number_gate"] L.append("## Quantum-Number Gate") L.append("") L.append(f"Result: **{'PASS' if qn['passed'] else 'FAIL'}** " f"({qn['n_checks']} checks, {qn['n_failed']} failed)") L.append("") L.append("| Particle | Quark content | Field | Derived | Listed | OK |") L.append("|---|---|---|---|---|---|") for f in qn["findings"]: L.append(f"| {f['particle']} | {f['quark_content']} | {f['field']} | " f"{f['derived']} | {f['listed']} | {'yes' if f['ok'] else '**NO**'} |") L.append("") L.append("## Relations") L.append("") for r in report["relations"]: status = "PASS" if r["passed"] else "**FAIL**" L.append(f"### {r['relation_id']} — {r['name']} ({status})") L.append("") L.append(f"- Grade: {r['grade']}") L.append(f"- Formula: `{r['formula']}`") L.append(f"- Predicted: `{r['predicted']}`") L.append(f"- Observed: `{r['observed']}`") L.append(f"- Residual ({r['residual_kind']}): `{r['residual']}`") L.append(f"- Tolerance `{r['tolerance_name']}` = `{r['tolerance_value']}`") if r["tolerance_rationale"]: L.append(f" - Rationale: {r['tolerance_rationale']}") L.append(f"- Detail: {r['detail']}") L.append("") L.append("## Disclaimer") L.append("") L.append("> " + report["safe_wording"]) L.append("") return "\n".join(L) def write_outputs(out_dir: Path, report: Dict[str, Any]) -> List[str]: out_dir.mkdir(parents=True, exist_ok=True) written: List[str] = [] p = out_dir / "relations_report.json" p.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8") written.append(str(p)) p = out_dir / "relations_report.md" p.write_text(render_markdown(report), encoding="utf-8") written.append(str(p)) return written # --------------------------------------------------------------------------- # # CLI # # --------------------------------------------------------------------------- # def run(data_dir: Path, out_dir: Path, config_path: Path) -> int: cfg = load_config(config_path) result = run_suite(data_dir, cfg) report = build_report(result) written = write_outputs(out_dir, report) print(f"[mass_relations] grade={cfg.grade} data={data_dir} out={out_dir}") print(f"[mass_relations] quantum-number gate: " f"{'PASS' if result.qn_ok else 'FAIL'} " f"({report['quantum_number_gate']['n_failed']} failed checks)") for r in result.relations: print(f"[mass_relations] {r.relation_id} {r.name}: " f"{'PASS' if r.passed else 'FAIL'} ({r.detail})") for fpath in written: print(f"[mass_relations] wrote {fpath}") print(f"[mass_relations] {cfg.safe_wording}") if not result.overall_pass: n = len(result.relation_fails) print(f"[mass_relations] RESULT: FAIL " f"(quantum_number_ok={result.qn_ok}; {n} relation(s) beyond tolerance)", file=sys.stderr) return 1 print("[mass_relations] RESULT: PASS") return 0 def build_arg_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser( prog="mass_relations.py", description="Parameter-free QCD symmetry RELATION engine. " + SAFE_WORDING, ) p.add_argument("--data", default="data", help="directory holding the 4 input CSVs") p.add_argument("--out", default="out", help="directory for relations_report.{md,json}") p.add_argument("--config", default="relations_config.json", help="JSON config declaring every relation tolerance") return p def main(argv: Optional[Sequence[str]] = None) -> int: args = build_arg_parser().parse_args(argv) base = Path.cwd() def resolve(p: str) -> Path: return Path(p) if Path(p).is_absolute() else (base / p).resolve() try: return run(resolve(args.data), resolve(args.out), resolve(args.config)) except SystemExit as exc: if isinstance(exc.code, int): return exc.code print(str(exc.code), file=sys.stderr) return 1 if __name__ == "__main__": sys.exit(main())