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
This commit is contained in:
Shay 2026-05-13 14:43:17 -07:00
parent 3affd29a82
commit a4b4d22987
5 changed files with 261 additions and 0 deletions

5
alignment/__init__.py Normal file
View file

@ -0,0 +1,5 @@
"""Alignment graph — cross-language resonance edges and holonomy proof cases."""
from .graph import AlignmentGraph, load_alignment
__all__ = ["AlignmentGraph", "load_alignment"]

88
alignment/graph.py Normal file
View file

@ -0,0 +1,88 @@
"""
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)

View file

@ -0,0 +1,7 @@
{"source_id": "grc-001", "target_id": "he-001", "relation": "cross_lang.logos.utterance", "weight": 0.95, "evidence_ids": ["John1:1", "Gen1:1"]}
{"source_id": "grc-002", "target_id": "he-002", "relation": "cross_lang.logos.genesis", "weight": 0.93, "evidence_ids": ["John1:1", "Gen1:1"]}
{"source_id": "grc-003", "target_id": "he-003", "relation": "cross_lang.logos.illumination", "weight": 0.95, "evidence_ids": ["John1:4", "Gen1:3"]}
{"source_id": "grc-004", "target_id": "he-004", "relation": "cross_lang.logos.vitality", "weight": 0.92, "evidence_ids": ["John1:4"]}
{"source_id": "grc-005", "target_id": "he-005", "relation": "cross_lang.logos.aletheia", "weight": 0.94, "evidence_ids": ["John14:6"]}
{"source_id": "grc-006", "target_id": "he-006", "relation": "cross_lang.logos.pneuma", "weight": 0.96, "evidence_ids": ["Gen1:2", "John3:8"]}
{"source_id": "grc-007", "target_id": "he-007", "relation": "cross_lang.logos.ktizo", "weight": 0.91, "evidence_ids": ["Gen1:1", "John1:3"]}

View file

@ -0,0 +1,7 @@
{"source_id": "he-001", "target_id": "grc-001", "relation": "cross_lang.logos.utterance", "weight": 0.95, "evidence_ids": ["John1:1", "Gen1:1"]}
{"source_id": "he-002", "target_id": "grc-002", "relation": "cross_lang.logos.genesis", "weight": 0.93, "evidence_ids": ["John1:1", "Gen1:1"]}
{"source_id": "he-003", "target_id": "grc-003", "relation": "cross_lang.logos.illumination", "weight": 0.95, "evidence_ids": ["John1:4", "Gen1:3"]}
{"source_id": "he-004", "target_id": "grc-004", "relation": "cross_lang.logos.vitality", "weight": 0.92, "evidence_ids": ["John1:4"]}
{"source_id": "he-005", "target_id": "grc-005", "relation": "cross_lang.logos.aletheia", "weight": 0.94, "evidence_ids": ["John14:6"]}
{"source_id": "he-006", "target_id": "grc-006", "relation": "cross_lang.logos.pneuma", "weight": 0.96, "evidence_ids": ["Gen1:2", "John3:8"]}
{"source_id": "he-007", "target_id": "grc-007", "relation": "cross_lang.logos.ktizo", "weight": 0.91, "evidence_ids": ["Gen1:1", "John1:3"]}

View file

@ -0,0 +1,154 @@
"""Tests for the alignment graph and HolonomyAlignmentCase formal proof."""
from __future__ import annotations
import numpy as np
import pytest
from alignment.graph import load_alignment
from language_packs.schema import AlignmentEdge, HolonomyAlignmentCase
from algebra.holonomy import holonomy_encode, holonomy_similarity
from language_packs import load_pack
# ---------------------------------------------------------------------------
# Alignment graph loading
# ---------------------------------------------------------------------------
def test_load_he_alignment_returns_seven_edges():
graph = load_alignment("he_logos_micro_v1")
assert len(graph) == 7
for edge in graph.edges:
assert isinstance(edge, AlignmentEdge)
assert 0.0 <= edge.weight <= 1.0
assert edge.relation.startswith("cross_lang.")
def test_load_grc_alignment_returns_seven_edges():
graph = load_alignment("grc_logos_micro_v1")
assert len(graph) == 7
def test_load_en_alignment_returns_empty_graph():
"""Operational base packs carry no cross-language edges yet."""
graph = load_alignment("en_minimal_v1")
assert len(graph) == 0
def test_davar_logos_edge_weight_above_threshold():
"""דבר ↔ λόγος edge weight must be >= 0.9 (logos.utterance canonical pair)."""
graph = load_alignment("he_logos_micro_v1")
edge = graph.get_edge("he-001", "grc-001")
assert edge is not None, "he-001 → grc-001 edge missing"
assert edge.weight >= 0.9
assert edge.relation == "cross_lang.logos.utterance"
def test_aligned_pairs_by_relation_prefix():
"""aligned_pairs() should filter by relation prefix correctly."""
graph = load_alignment("he_logos_micro_v1")
all_cross = graph.aligned_pairs("cross_lang.logos")
assert len(all_cross) == 7
logos_only = graph.aligned_pairs("cross_lang.logos.utterance")
assert len(logos_only) == 1
assert logos_only[0].source_id == "he-001"
def test_edges_from_source():
graph = load_alignment("grc_logos_micro_v1")
edges = graph.edges_from("grc-001")
assert len(edges) == 1
assert edges[0].target_id == "he-001"
# ---------------------------------------------------------------------------
# HolonomyAlignmentCase formal proof
# ---------------------------------------------------------------------------
def _encode(manifold, tokens: list[str]) -> np.ndarray:
return holonomy_encode([manifold.get_versor(t) for t in tokens])
def test_holonomy_alignment_case_positive_closer_than_negative():
"""
Crown proof case: positive aligned triple must be geometrically closer
than the negative (misaligned) triple.
This wraps the geometry proven in test_holonomy_resonance.py into the
formal HolonomyAlignmentCase schema type, so the proof is both
machine-checkable and linked to the schema's contract.
"""
case = HolonomyAlignmentCase(
case_id="HAC-001",
description=(
"Aligned Logos clause (word/דבר/λόγος + beginning/ראשית/ἀρχή + truth/אמת/ἀλήθεια) "
"produces closer holonomies across three languages than a misaligned clause "
"substituting ζωή (vitality) for ἀλήθεια (truth)."
),
source_refs=("Gen1:1", "John1:1", "John14:6"),
pack_ids=("en_minimal_v1", "he_logos_micro_v1", "grc_logos_micro_v1"),
expected_relation="cross_lang.closer_than_negative",
negative_source_refs=("John1:4",),
tolerance=0.0,
)
# Validate the case schema itself
assert case.case_id == "HAC-001"
assert len(case.pack_ids) == 3
assert len(case.source_refs) >= 2
# Load packs
_, en = load_pack("en_minimal_v1")
_, he = load_pack("he_logos_micro_v1")
_, grc = load_pack("grc_logos_micro_v1")
# Positive triple: aligned canonical clause across all three languages
en_h = _encode(en, ["word", "beginning", "truth"])
he_h = _encode(he, ["\u05d3\u05d1\u05e8", "\u05e8\u05d0\u05e9\u05d9\u05ea", "\u05d0\u05de\u05ea"])
grc_h = _encode(grc, ["\u03bb\u03cc\u03b3\u03bf\u03c2", "\u1f00\u03c1\u03c7\u03ae", "\u1f00\u03bb\u03ae\u03b8\u03b5\u03b9\u03b1"])
# Negative: replace ἀλήθεια with ζωή — different semantic domain
grc_neg_h = _encode(grc, ["\u03bb\u03cc\u03b3\u03bf\u03c2", "\u1f00\u03c1\u03c7\u03ae", "\u03b6\u03c9\u03ae"])
# Positive score: mean distance of aligned cross-language pair
positive_dist = (
np.linalg.norm(en_h - he_h) +
np.linalg.norm(en_h - grc_h) +
np.linalg.norm(he_h - grc_h)
) / 3.0
# Negative score: distance when Greek clause uses misaligned token
negative_dist = (
np.linalg.norm(en_h - he_h) +
np.linalg.norm(en_h - grc_neg_h) +
np.linalg.norm(he_h - grc_neg_h)
) / 3.0
# The formal case assertion: aligned closer than misaligned
assert positive_dist < negative_dist, (
f"HolonomyAlignmentCase {case.case_id} failed: "
f"positive_dist={positive_dist:.6f} >= negative_dist={negative_dist:.6f}. "
f"Case: {case.description}"
)
def test_holonomy_alignment_case_schema_validation():
"""HolonomyAlignmentCase must reject under-specified instances."""
with pytest.raises(ValueError, match="at least two source_refs"):
HolonomyAlignmentCase(
case_id="BAD-001",
description="missing refs",
source_refs=("Gen1:1",), # only one
pack_ids=("en_minimal_v1", "he_logos_micro_v1"),
expected_relation="cross_lang.closer_than_negative",
)
with pytest.raises(ValueError, match="at least two pack_ids"):
HolonomyAlignmentCase(
case_id="BAD-002",
description="missing packs",
source_refs=("Gen1:1", "John1:1"),
pack_ids=("en_minimal_v1",), # only one
expected_relation="cross_lang.closer_than_negative",
)