core/alignment/graph.py
Shay a4b4d22987 Add alignment graph, cross-language edges, and HolonomyAlignmentCase tests
alignment/graph.py
  Lightweight in-memory alignment graph. Loads AlignmentEdge records from
  a pack's alignment.jsonl. Exposes edges_from(), aligned_pairs(), and
  load_alignment(). No external deps — pure schema + stdlib.

language_packs/data/he_logos_micro_v1/alignment.jsonl
language_packs/data/grc_logos_micro_v1/alignment.jsonl
  Seven bidirectional cross-language edges per pack encoding the semantic
  resonances already implicit in the lexicon semantic_domains:
  דבר↔λόγος, ראשית↔ἀρχή, אור↔φῶς, חיים↔ζωή, אמת↔ἀλήθεια, רוח↔πνεῦμα, ברא↔κτίζω

tests/test_alignment_graph.py
  Four tests:
  - load returns AlignmentEdge instances with correct structure
  - דבר↔λόγος edge weight >= 0.9
  - aligned_pairs() filters by relation prefix
  - HolonomyAlignmentCase formal proof: positive triple closer than
    negative triple, wrapping the geometry already proven in
    test_holonomy_resonance.py into the schema's crown proof type
2026-05-13 14:43:17 -07:00

88 lines
3 KiB
Python

"""
Alignment graph — cross-language resonance edges.
AlignmentEdge records live in a pack's alignment.jsonl alongside its
lexicon.jsonl. This module loads them into a queryable in-memory graph.
Design constraints:
- No numpy, no algebra imports. The graph is pure schema + stdlib.
Geometric verification belongs in tests or holonomy proofs, not here.
- load_alignment() is the single entry point. It reads bytes, not strings,
so the caller can checksum if needed.
- AlignmentGraph is immutable after construction (frozen edges tuple).
"""
from __future__ import annotations
import json
from pathlib import Path
from language_packs.schema import AlignmentEdge
_DATA_DIR = Path(__file__).parent.parent / "language_packs" / "data"
class AlignmentGraph:
"""Immutable in-memory graph of AlignmentEdge records for one pack."""
def __init__(self, edges: list[AlignmentEdge]) -> None:
self._edges: tuple[AlignmentEdge, ...] = tuple(edges)
# Index by source_id for O(1) lookup on hot path
self._by_source: dict[str, list[AlignmentEdge]] = {}
for edge in self._edges:
self._by_source.setdefault(edge.source_id, []).append(edge)
def __len__(self) -> int:
return len(self._edges)
def edges_from(self, source_id: str) -> list[AlignmentEdge]:
"""Return all edges originating from source_id."""
return list(self._by_source.get(source_id, []))
def aligned_pairs(self, relation_prefix: str) -> list[AlignmentEdge]:
"""Return all edges whose relation starts with relation_prefix."""
return [
e for e in self._edges
if e.relation.startswith(relation_prefix)
]
def get_edge(self, source_id: str, target_id: str) -> AlignmentEdge | None:
"""Return the edge between source and target, or None."""
for edge in self._by_source.get(source_id, []):
if edge.target_id == target_id:
return edge
return None
@property
def edges(self) -> tuple[AlignmentEdge, ...]:
return self._edges
def _parse_edge(payload: dict) -> AlignmentEdge:
return AlignmentEdge(
source_id=payload["source_id"],
target_id=payload["target_id"],
relation=payload["relation"],
weight=float(payload["weight"]),
evidence_ids=tuple(payload.get("evidence_ids", [])),
)
def load_alignment(pack_id: str) -> AlignmentGraph:
"""
Load AlignmentEdge records from language_packs/data/<pack_id>/alignment.jsonl.
Returns an empty AlignmentGraph if the file does not exist.
This is intentional: operational_base packs (en_minimal_v1) do not
currently carry cross-language alignment edges.
"""
alignment_path = _DATA_DIR / pack_id / "alignment.jsonl"
if not alignment_path.exists():
return AlignmentGraph([])
edges: list[AlignmentEdge] = []
for line in alignment_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if line:
edges.append(_parse_edge(json.loads(line)))
return AlignmentGraph(edges)